您当前的位置: 首页 >  qt

龚建波

暂无认证

  • 3浏览

    0关注

    313博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Qt+FFmpeg解视频帧转为QImage

龚建波 发布时间:2022-09-21 13:53:01 ,浏览量:3

环境:Win10 + Qt5.15.2 + MSVC 64bit + FFmpeg4.4

解码流程:

图片来源: FFmpeg编程-解码流程_贝勒里恩的博客-CSDN博客_ffmpeg解码流程

其他参考:

博客:Qt+FFMPEG学习(一)视频帧转换为QImage_Italink的博客-CSDN博客_qstring转qimage

博客:使用Qt线程 + FFmpeg获取视频流并显示图像到窗口_友善啊,朋友的博客-CSDN博客_qt接收视频流

博客:FFMPEG开发快速入坑——音视频编码处理 - 知乎

遇到的小问题:

1.用 avcodec_receive_frame 获取解码后的帧数据,返回 AVERROR(EAGAIN) (值为-11),文档描述为:输出在此状态下不可用-用户必须尝试发送新输入,所以加了个 continue 循环执行 avcodec_send_packet,直到可用。

2.sws_scale (视频像素格式和分辨率转换)的输出可以直接用字节缓冲区接收,不一定非要把缓冲区 attach 到 AVFrame 上。

3.QImage、QByteArray这些容器如果直接用字节数据构造,是没有深拷贝一份的,需要自己分离。

代码链接:https://github.com/gongjianbo/LearnFFmpeg.git

主要代码:

#include "MainWindow.h"
#include "ui_MainWindow.h"
extern "C" {
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
}
#include 
#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    fprintf(stderr, "ffmpeg version:%s\n", av_version_info());

    connect(ui->pushButton, &QPushButton::clicked,
            this, [this]{
        const QString &filepath = QFileDialog::getOpenFileName(this);
        qDebug()height,
                                  guard.codecCtx->pix_fmt,
                                  guard.codecParam->width,
                                  guard.codecParam->height,
                                  AV_PIX_FMT_RGB24,
                                  SWS_BICUBIC, NULL, NULL, NULL);

    if (!guard.swsCtx)
        return image;

    while (true) {
        //读取码流中的一帧视频,或者若干帧音频
        //注:av_read_frame 每次循环后必须执行av_packet_unref(packet)进行释放
        //frame同理
        if(av_read_frame(guard.formatCtx, guard.packet) != 0)
            return image;

        if (guard.packet->stream_index == guard.streamIndex) {
            //发送编码数据包,将一个packet放入到队列中等待解码
            //注:ffmpeg内部会缓冲几帧,要想取出来就需要传递空的AVPacket进去
            result = avcodec_send_packet(guard.codecCtx, guard.packet);
            //当前状态下不接受输入-用户必须使用avcodec_receive_frame()读取输出
            if (result == AVERROR(EAGAIN)) {
                avcodec_receive_frame(guard.codecCtx, guard.frameIn);
                av_packet_unref(guard.packet);
                av_frame_unref(guard.frameIn);
                continue;
            } else if (result != 0) {
                return image;
            }
            //接收解码后数据,将解码后的数据拷贝给avframe
            //avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一一对应
            //如一些音频数据调用一次avcodec_send_packet之后,
            //可能需要调用多次avcodec_receive_frame才能获取全部的解码音频数据
            result = avcodec_receive_frame(guard.codecCtx, guard.frameIn);
            //输出在此状态下不可用-用户必须尝试发送新输入
            if (result == AVERROR(EAGAIN)) {
                av_packet_unref(guard.packet);
                av_frame_unref(guard.frameIn);
                continue;
            } else if (result != 0) {
                return image;
            }

            //视频像素格式和分辨率的转换
            //函数功能:1.图像色彩空间转换;2.分辨率缩放;3.前后图像滤波处理。
            //效率相对较低,不如libyuv或shader
            sws_scale(guard.swsCtx,
                      static_cast(guard.frameIn->data),
                      guard.frameIn->linesize,
                      0,
                      guard.codecParam->height,
                      guard.frameOut->data,
                      guard.frameOut->linesize);

            //guard.frameOut->data就是指向的guard.outBuf,可替换
            image = QImage(static_cast(guard.frameOut->data[0]),
                    guard.codecParam->width,
                    guard.codecParam->height,
                    QImage::Format_RGB888);
            //构造用的buf的内存,这里拷贝一份
            image = image.copy();

            qDebug()            
关注
打赏
1655829268
查看更多评论
0.0385s