FFmpeg编程--Qt显示FFmpeg解码的图片

一、前言

之前讲过通过FFmpeg解码视频,将视频帧保存为图片,这次我们来将解码的图片播放起来;

思路:

  • 首先,解码还是十步走战略,但是这个不能放在主线程里,耗时操作会卡住界面,所以我们将解码操作放入子线程中运行;
  • 其次,子线程无法直接修改主界面UI,所以通过信号槽将子线程解码的QImage传递到主界面显示;
  • 最后,主界面将传递过来的图片实时更新到QLabel上即可;

二、效果展示

Qt显示FFmpeg解码的图片


三、详细代码

解码线程类

#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H

extern "C"{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
}

#include <stdio.h>

#include <QThread>
#include <QImage>
#include <QDebug>

class VideoPlayer : public QThread
{
    Q_OBJECT
public:
    VideoPlayer();

protected:
    void run();

signals:
    void sig_post_oneFrame(const QImage& image);
};

#endif // VIDEOPLAYER_H

#include "videoplayer.h"

VideoPlayer::VideoPlayer()
{

}

void VideoPlayer::run()
{
    //变量定义
    //==================================================================================
    char *file_path = "C:/Users/wangjichuan/Desktop/2.mp4";   //文件路径

    ///=====第一步=====
    //初始化FFMPEG  调用了这个才能正常使用编码器和解码器
    av_register_all();
    ///===================================================================

    ///=====第二步=====
    AVFormatContext *pFormatCtx;    //描述了一个媒体文件或媒体流的构成和基本信息
    pFormatCtx = avformat_alloc_context();  //分配一个解封装上下文指针

    //打开文件
    if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) { //文件信息存储到文件上下文中,后续对它进行处理即可
        printf("无法打开文件");
        return;
    }
    ///===================================================================

    ///=====第三步=====
    //探寻文件中是否存在信息流
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("文件中没有发现信息流");
        return;
    }

    //循环查找视频中包含的流信息,直到找到视频类型的流
    //便将其记录下来 保存到videoStream变量中
    //这里我们现在只处理视频流  音频流先不管他
    int videoStream = -1;
    int i;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    //如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        printf("文件中未发现视频流");
        return;
    }
    ///===================================================================

    ///=====第四步=====
    AVCodecContext *pCodecCtx;      //描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息
    AVCodec *pCodec;                //存储编解码器信息的结构体

    //查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;    //获取视频流中编码器上下文
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     //获取视频流的编码器

    if (pCodec == NULL) {
        printf("未发现编码器");
        return;
    }
    ///===================================================================

    ///=====第五步=====
    //打开解码器
    //==================================================================================
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("无法打开编码器");
        return;
    }
    ///===================================================================

    ///=====第六步=====
    static struct SwsContext *img_convert_ctx;  //用于视频图像的转换
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_BGR32, SWS_BICUBIC, NULL, NULL, NULL);
    ///===================================================================

    ///=====第七步=====
    int numBytes;
    numBytes = avpicture_get_size(AV_PIX_FMT_BGR32, pCodecCtx->width,pCodecCtx->height);
    ///===================================================================

    ///=====第八步=====
    AVFrame *pFrame, *pFrameRGB;    //存储音视频原始数据(即未被编码的数据)的结构体
    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();

    uint8_t *out_buffer;            //缓存
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_BGR32,
            pCodecCtx->width, pCodecCtx->height);
    ///===================================================================

    ///=====第九步=====
    AVPacket *packet;               //保存了解复用(demuxer)之后,解码(decode)之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加的信息
    int ret, got_picture;

    int y_size = pCodecCtx->width * pCodecCtx->height;
    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据

    av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息

    while (1)
    {
        if (av_read_frame(pFormatCtx, packet) < 0) {
            break; //这里认为视频读取完了
        }

        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

            if (ret < 0) {
                printf("decode error.");
                return;
            }

            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const * const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);

                QImage image(pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, pFrameRGB->linesize[0], QImage::Format_RGBX8888);
                emit sig_post_oneFrame(image);
            }
        }
        av_free_packet(packet);
    }
    ///===================================================================

    ///=====第十步=====
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
    ///===================================================================
}


主窗体类

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

#include "videoplayer.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QLabel* label;
    VideoPlayer* m_player;

public slots:
    void slot_get_oneFrame(const QImage& image);


private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    label = new QLabel(this);
    this->setCentralWidget(label);

    m_player = new VideoPlayer;
    connect(m_player,SIGNAL(sig_post_oneFrame(QImage)),this,SLOT(slot_get_oneFrame(QImage)));
    m_player->start();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::slot_get_oneFrame(const QImage& image)
{
    label->setPixmap(QPixmap::fromImage(image));
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值