FFmpeg入门(二):使用Qt播放视频(多线程)

under Qt  tag FFmpeg    Published on May 14th , 2020 at 06:17 am

前言

根据FFmpeg入门(一)已经可以实现使用FFmpeg解码视频,储存为图片了。本文介绍如何用Qt+FFmpeg写一个视频播放器。

正文

0.引言

首先建立一个Qt Widget工程,具体方法自行百度。
关于Qt环境下部署FFmpeg见本站另一篇文章Qt——部署FFmpeg

1.使用多线程QThread的原因

做过图像界面开发的都知道,任何耗时的操作都不能放在主线程进行,一旦主线程阻塞了,那么体现出来的就是界面卡了。 而我们读取视频和解码视频是一个非常耗时的操作,因此需要另外开辟一个线程来专门做这件事。

2.Qt中多线程的使用

Qt里面线程的用法 则是写一个类继承QThread, 然后重载其run函数,把耗时的操作全部放入run函数。

class VideoPlayer : public QThread
{
    Q_OBJECT
 
public:
    explicit VideoPlayer();
    ~VideoPlayer();
 
protected:
    void run();
 
};

这里run函数里面就是写我们读取视频和解码视频的代码了;

读取和解码还是和前面说的一样的方法:

3.由于需要用控件显示,所以有了以下的改动:

把解码之后的YUV数据转换成了RGB32格式的数据:

///这里我们改成了 将解码后的YUV数据转换成RGB32    
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
 
numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
 
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB32,
            pCodecCtx->width, pCodecCtx->height);
             
             
....
...
..
 
sws_scale(img_convert_ctx,
                (uint8_t const * const *) pFrame->data,
                pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                pFrameRGB->linesize);

4.将转换后的RGB32数据存入QImage对象:

//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);

5.进程间利用Qt的信号与槽通信

由于我们不能够在子线程中操作界面,(操作界面只能在主线程中进行,几乎所有的图形界面开发都是这样设定),因此我们只能给主线程发送信号,信号带上这个QIMage,让主线程帮忙把这个图像显示出来。

a.申明信号
signals:    
    void sig_GetOneFrame(QImage); //没获取到一帧图像 就发送此信号
b.发送信号
//把这个RGB数据 用QImage加载                
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
emit sig_GetOneFrame(image);  //发送信号
c.主线程绑定并接收信号:
mPlayer = new VideoPlayer;    
connect(mPlayer,SIGNAL(sig_GetOneFrame(QImage)),this,SLOT(slotGetOneFrame(QImage)));
d.信号处理函数如下:
void MainWindow::slotGetOneFrame(QImage img){
    mImage = img;
    update(); //调用update将执行 paintEvent函数
}
e.重写paintEvent函数如下:
void MainWindow::paintEvent(QPaintEvent *event){
    QPainter painter(this);
    painter.setBrush(Qt::black);
    painter.drawRect(0, 0, this->width(), this->height()); //先画成黑色
 
    if (mImage.size().width() <= 0) return;
 
    ///将图像按比例缩放成和窗口一样大小
    QImage img = mImage.scaled(this->size(),Qt::KeepAspectRatio);
 
    int x = this->width() - img.width();
    int y = this->height() - img.height();
 
    x /= 2;
    y /= 2;
 
    painter.drawImage(QPoint(x,y),img); //画出图像
 
}

6.程序运行结果(多芬打钱)


本文由simyng创作, 采用知识共享署名4.0 国际许可协议进行许可,转载前请务必署名
  文章最后更新时间为:May 13th , 2020 at 10:17 pm