QT+ffmpeg实现视频解析的示例详解

目录
  • 一、创建QT项目
  • 二、引入ffmpeg
    • 1、复制头文件和lib
    • 2、复制bin文件
    • 3、简单测试
  • 三、视频解析
    • 1、创建线程
    • 2、创建自定义绘制控件
    • 3、使用自定义控件
    • 4、开启线程,进行视频解析

一、创建QT项目

首先安装了最新的Community版本,Creator是8.0.1版本了。

然后进行项目的创建。

得到的项目没有pro文件,而是CMakeLists.txt。

二、引入ffmpeg

从下面下载的ffmpeg-5.0.1-full_build-shared.7z。

https://www.gyan.dev/ffmpeg/builds/

1、复制头文件和lib

在项目内创建一个文件夹,我这里起名叫lib,然后在lib文件夹下创建了ffmpeg文件夹,然后将上面下载的压缩包内的include和lib文件夹复制到ffmpeg文件夹下。

然后修改CMakeLists.txt,添加如下的内容。

include_directories(${CMAKE_SOURCE_DIR}/lib/ffmpeg/include)

target_link_libraries(QtFFmpegApp1 PRIVATE ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/avcodec.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/avdevice.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/avfilter.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/avformat.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/avutil.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/postproc.lib
                                          ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/swresample.lib
                                           ${CMAKE_SOURCE_DIR}/lib/ffmpeg/lib/swscale.lib
                                       )

2、复制bin文件

将下面的dll文件复制到build-QtFFmpegApp1-Desktop_Qt_6_3_1_MinGW_64_bit-Debug文件夹下。

3、简单测试

在mainwindow.h文件内添加以下引用

extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/buffer.h"
#include "libavutil/error.h"
#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavfilter/avfilter.h>
}

在mainwindow.cpp文件内的MainWindow::MainWindow方法内添加

std::string s = avcodec_configuration();
QString dlgTitle = "information消息框";
QString strInfo = QString::fromStdString(s);
QMessageBox::information(this, dlgTitle, strInfo, QMessageBox::Ok, QMessageBox::NoButton);

运行,会看到如下消息窗口,其中显示的ffmpeg的编译参数,看到这个就表明ffmpeg引入成功。

三、视频解析

1、创建线程

(1)MyThread.h文件

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QImage>

#include <iostream>
#include <fstream>
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/buffer.h"
#include "libavutil/error.h"
#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavfilter/avfilter.h>
}

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
protected:
    void run() override;
signals:
    void minNumToamin(int min);
    void getPicFromFrame(QImage image);
};

#endif // MYTHREAD_H

(2)MyThread.cpp文件

#include <MyThread.h>
#include <QDebug>

#define INBUF_SIZE 4096

MyThread::MyThread()
{

}

void MyThread::run()
{
    const char* filename, * outfilename;
    const AVCodec* codec;
    AVCodecParserContext* parser;
    AVCodecContext* c = NULL;
    FILE* f;
    AVFrame* frame;
    AVFrame* pFrameBGR;
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t* data;
    size_t   data_size;
    int ret;
    AVPacket* pkt;
    AVFormatContext* inFmtCtx = NULL;
    int video_in_stream_index = -1, audio_in_stream_index = -1;
    AVCodecID src_video_id = AVCodecID::AV_CODEC_ID_NONE, src_audio_id = AVCodecID::AV_CODEC_ID_NONE;
    SwsContext* sws_ctx;
    uint8_t* buffer = nullptr;

    //文件路径
    filename = "C:\\Users\\zyh\\Desktop\\2.mp4";
    outfilename = "";

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);

    // 打开输入文件
    if ((ret = avformat_open_input(&inFmtCtx, filename, NULL, NULL)) < 0) {
        //LOGD("avformat_open_input() fail");
        //releaseSources();
        return;
    }
    if ((ret = avformat_find_stream_info(inFmtCtx, NULL)) < 0) {
        //LOGD("avformat_find_stream_info fail %d", ret);
        //releaseSources();
        return;
    }
    // 输出输入文件信息
    av_dump_format(inFmtCtx, 0, filename, 0);

    for (int i = 0; i < inFmtCtx->nb_streams; i++) {
        AVCodecParameters* codecpar = inFmtCtx->streams[i]->codecpar;
        if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_in_stream_index == -1) {
            src_video_id = codecpar->codec_id;
            video_in_stream_index = i;
        }
        if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_in_stream_index == -1) {
            src_audio_id = codecpar->codec_id;
            audio_in_stream_index = i;
        }
    }

    /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    /* find the MPEG-1 video decoder */
    codec = avcodec_find_decoder(src_video_id);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    AVCodecParameters* codecpar = inFmtCtx->streams[video_in_stream_index]->codecpar;
    if ((ret = avcodec_parameters_to_context(c, codecpar)) < 0) {
        //LOGD("avcodec_parameters_to_context fail %d", ret);
        //releaseSources();
        return;
    }

    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */
    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    frame = av_frame_alloc();
    pFrameBGR = av_frame_alloc();

    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }

    int index = 0;
    while (av_read_frame(inFmtCtx, pkt) >= 0) {

        // 迭代结束后释放 av_read_frame 分配的 packet 内存
        //std::shared_ptr<AVPacket> packetDeleter(&pkt, av_packet_unref);

        if(index >0) break;
        // 说明读取的视频数据
        if (pkt->stream_index == video_in_stream_index) {
            if ((ret = avcodec_send_packet(c, pkt)) < 0) {
                //LOGD("video avcodec_send_packet fail %s", av_err2str(ret));
                //releaseSources();
                return;
            }
            while (true) {
                // 从解码缓冲区接收解码后的数据
                if ((ret = avcodec_receive_frame(c, frame)) < 0) {
                    if (ret == AVERROR_EOF) {
                        exit(1);
                        // 解码缓冲区结束了,那么也要flush编码缓冲区
                        //doEncodeVideo(NULL);
                    }
                    break;
                }

                int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, c->width, c->height, 1);
                if(buffer == nullptr) buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
                av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, buffer, AV_PIX_FMT_RGB24,  c->width, c->height, 1);

                sws_ctx = sws_getContext(codecpar->width, codecpar->height, (enum AVPixelFormat)codecpar->format,
                    frame->width, frame->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);

                sws_scale(sws_ctx, frame->data, frame->linesize, 0, c->height, pFrameBGR->data, pFrameBGR->linesize);

                // 图像转换
                sws_scale(sws_ctx, frame->data, frame->linesize, 0, c->height, pFrameBGR->data, pFrameBGR->linesize);

                // 得到QImage
                QImage tempImage((uchar*)pFrameBGR->data[0], c->width, c->height, QImage::Format_RGB888);
                // 对于qt还是不是很熟悉,理解是传递给主线程
                getPicFromFrame(tempImage);
            }
        }

        // 因为每一次读取的AVpacket的数据大小不一样,所以用完之后要释放
        av_packet_unref(pkt);
    }

    fclose(f);

    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);
}

2、创建自定义绘制控件

(1)PlayImage.h

#ifndef PLAYIMAGE_H
#define PLAYIMAGE_H

#include <QWidget>

class PlayImage : public QWidget
{
    Q_OBJECT
public:
    explicit PlayImage(QWidget *parent = nullptr);

    void updateImage(const QImage& image);
    void updatePixmap(const QPixmap& pixmap);

signals:

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    QPixmap m_pixmap;
};

#endif // PLAYIMAGE_H

(2)PlayImage.cpp

#include "playimage.h"

#include <QPainter>

PlayImage::PlayImage(QWidget *parent) : QWidget(parent)
{

}

/**
 * @brief        传入Qimage图片显示
 * @param image
 */
void PlayImage::updateImage(const QImage& image)
{
    updatePixmap(QPixmap::fromImage(image));
}

/**
 * @brief        传入QPixmap图片
 * @param pixmap
 */
void PlayImage::updatePixmap(const QPixmap &pixmap)
{
    m_pixmap = pixmap;
    update();
}

/**
 * @brief        使用Qpainter显示图片
 * @param event
 */
void PlayImage::paintEvent(QPaintEvent *event)
{
    if(!m_pixmap.isNull())
    {
        QPainter painter(this);
#if 0
        // 经过粗略测试,QImage先缩放后转为QPixmap的方式在图像比较小时耗时少,图片越大耗时远大
        QPixmap pixmap = QPixmap::fromImage(m_image.scaled(this->size(), Qt::KeepAspectRatio));
        // 先将QImage转换为QPixmap再进行缩放则耗时比较少,并且稳定,不会因为缩放图片大小而产生太大影响
        QPixmap pixmap1 = QPixmap::fromImage(m_image).scaled(this->size(), Qt::KeepAspectRatio);
#endif
        QPixmap pixmap = m_pixmap.scaled(this->size(), Qt::KeepAspectRatio);
        int x = (this->width() - pixmap.width()) / 2;
        int y = (this->height() - pixmap.height()) / 2;
        painter.drawPixmap(x, y, pixmap);
    }
    QWidget::paintEvent(event);
}

3、使用自定义控件

在mainwindow.ui的设计界面,拖一个Widget到主界面,然后在Widget上点击右键,然后选择提升为,在提升的类名称处输入上面自定义控件的类名。

如果选择下面的全局包含,就不用再单独包含头文件了。

4、开启线程,进行视频解析

(1)mainwindow.cpp

开启线程

m_thread  =  new MyThread;
connect(m_thread,&MyThread::getPicFromFrame,this,&MainWindow::getPicfromThread);
m_thread->start();

接收并绘制图片

void MainWindow::getPicfromThread(QImage image)
{
    ui->widget->updateImage(image);
}

(2)绘制结果

差不多下面这个样子。

到此这篇关于QT+ffmpeg实现视频解析的示例详解的文章就介绍到这了,更多相关QT ffmpeg视频解析内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Qt+FFMPEG实现循环解码详解

    目录 一.结果 二.解码准备工作+循环解码相关操作 videodecode.h .cpp main.cpp 一.结果 可以设置延时函数-----遍历每一帧的信息进行打印 25(fps)*30(秒)=750帧 二.解码准备工作+循环解码相关操作 videodecode.h .cpp #ifndef VIDEODECODE_H #define VIDEODECODE_H #include <QObject> //当前C++兼容C语言 extern "C" { //avcodec

  • QT实现视频传输功能

    本文实例为大家分享了QT实现视频传输功能的具体代码,供大家参考,具体内容如下 前言 我们经常会进行视频的传输,这篇文章我们将讲解QT下的视频传输. 客户端 udp客户端,我们主要获取某个视频的文件,采用opencv读取后,分帧传输.我们将图片解码成base-64格式 参考代码 #include "ImageClient.h" #include<QDebug> #include<QBuffer> #include<QFileDialog> #inclu

  • 基于Qt实现视频播放器功能

    目录 一.功能介绍 二.代码 三.运行 一.功能介绍 能实现视频播放 二.代码 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMediaPlayer> #include <QMediaPlaylist> #include <QPushButton> #include <QSlider> #include <QV

  • C++ Qt实现音视频播放功能

    由于最近着手的Qt项目需要视频播放 自己做的时候踩了很多坑 避免以后踩坑 故在此记录实现过程  Qt版本 5.9 基于C++11 Qt核心组件与附加组件安装时请打钩 否则可能出现项目中缺少视频播放模块的问题 1.首先创建一个新项目 并且打开.pro项目设置文件 新项目目录结构 2.在.pro文件中添加模块  如下所示 这是我们音视频播放的基础模块 其他设置根据自己的项目需求自行添加或修改 修改完成后ctrl+s保存文件 QT += multimedia multimediawidgets //Q

  • QT+ffmpeg实现视频解析的示例详解

    目录 一.创建QT项目 二.引入ffmpeg 1.复制头文件和lib 2.复制bin文件 3.简单测试 三.视频解析 1.创建线程 2.创建自定义绘制控件 3.使用自定义控件 4.开启线程,进行视频解析 一.创建QT项目 首先安装了最新的Community版本,Creator是8.0.1版本了. 然后进行项目的创建. 得到的项目没有pro文件,而是CMakeLists.txt. 二.引入ffmpeg 从下面下载的ffmpeg-5.0.1-full_build-shared.7z. https:/

  • vue实现鼠标滑动预览视频封面组件示例详解

    目录 组件效果 组件设计 1.视频截取关键帧 2.鼠标移入封面时显示对应关键帧 3.视频和封面的状态切换 功能实现 1.视频截取关键帧图片列表 1.1 截取指定帧 1.2 截取stepNums张关键帧图片 2.鼠标移入封面时显示对应关键帧 2.1 鼠标移动事件监听 2.2 鼠标移出事件监听 3.视频和封面的状态切换 3.1 播放视频 3.2 视频暂停 组件使用 组件库引用 组件效果 https://www.jb51.net/Special/926.htm 组件设计 我们首先应该要对组件进行一个简

  • C++ Qt利用GPU加速计算的示例详解

    在 C++ 和 Qt 中,可以通过以下方式利用 GPU 进行加速计算: 使用 GPU 编程框架:可以使用类似 CUDA.OpenCL.DirectCompute 等 GPU 编程框架,这些框架提供了对 GPU 的访问和操作,可以使用 GPU 进行并行计算,从而加速计算速度. 使用图形 API:在 Qt 中,可以使用 QOpenGLFunctions 等 API 访问 GPU,这些 API 可以用于执行图形渲染.图像处理等任务,利用 GPU 进行计算. 使用高性能计算库:在 C++ 中,有一些高性

  • C++实现xml解析器示例详解

    目录 xml格式简单介绍 xml格式解析过程浅析 代码实现 实现存储解析数据的类——Element 关键代码1——实现整体的解析 关键代码2——解析所有元素 开发技巧 有关C++的优化 额外注意 xml格式简单介绍 <?xml version="1.0"?> <!--这是注释--> <workflow> <work name="1" switch="on"> <plugin name=&quo

  • ShardingSphere解析SQL示例详解

    目录 引言 解析Sql的入口 解析Sql 1. 将 SQL 解析为抽象语法树 2. 提取Sql片段 3. 填充Sql片段,生成解析结果 总结 引言 ShardingSphere的SQL解析,本篇文章源码基于4.0.1版本 ShardingSphere的分片引擎从解析引擎到路由引擎到改写引擎到执行引擎再到归并引擎,一步一步对分片操作进行处理,我们这篇文章先从解析引擎开始,深入分析一下Sql的解析引擎处理流程. 解析Sql的入口 SQLParseEngine这个类是sql解析引擎对应的类,通过看它的

  • Qt利用QJson实现解析数组的示例详解

    目录 前言 第一步:进行数据转换 第二步:将字符串转成QJsonDocument格式 第三步:解析json数据 前言 现在有这样一个json结构,需要使用QJson来解析,结构如下: "code": "0001", "descrip": "文本描述1详细描述", "id": "1", "title": "文本1标题", "type&quo

  • Golang 实现 RTP音视频传输示例详解

    目录 引言 RTP 数据包头部字段 Golang 的相关实现 结尾 引言 在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. 由音视频传输工作小组开发,1996 年首次发布,并提出了以下使用设想. 简单的多播音频会议 使用 IP 的多播服务进行语音通信.通过某种分配机制,获取多播组地址和端口对.一个端口用于音频数据的,另一个用于控制(RTCP)包,

  • Python实现解析yaml配置文件的示例详解

    目录 楔子 字典 数组 标量 引用 生成 yaml 文件 楔子 前面我们介绍了 ini 格式的配置文件,本次来看看 yaml,它的表达能力相比 ini 更加的强大.yaml 文件以 .yml 结尾,在介绍它的语法结构之前我们先来看看 yaml 的一些基本规则. 大小写敏感: 使用缩进表示层级关系,并且缩进只能用空格.不可以使用 tab 键.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可: # 表示注释,# 到行尾的所有字符都会被忽略: yaml 支持的数据结构有以下三种: 字典:键值对的集

  • Python实现解析ini配置文件的示例详解

    目录 楔子 ini 文件 特殊格式 小结 楔子 在开发过程中,配置文件是少不了的,只不过我们有时会将 py 文件作为配置文件(config.py),然后在其它的模块中直接导入.这样做是一个好主意,不过配置文件是有专门的格式的,比如:ini, yaml, toml 等等. 而对于 Python 而言,也都有相应的库来解析相应格式的文件,下面我们来看看 ini 文件要如何解析. ini 文件 先来了解一下 ini 文件的格式: [satori] name = 古明地觉 age = 16 where 

  • Python实现视频转换为字符画详解

    上次写了个华强买瓜字符视频的帖子,下面有人问如何保存,所以这次就写一个能将字符画视频保存下来的帖子,然而时不待我,华强纪元已经结束,现在是穿山甲的时代了. 首先读取视频,并转为字符.视频是从B站下载的,地址<激战江南>穿山甲名场面. 由于B站直接下载的视频为flv格式,而imageio并不支持,尽管可以用opencv来读取,但相比之下,用ffmepg转个码也不复杂,这样可以最大限度地利用华强买瓜的代码. 另外,视频素材过长不适合代码演示,所以从第2:10进行截取15s. 在命令行中输入 >

随机推荐