Bootstrap

FFmpeg开发(八)——Qt视频播放器之多线程的使用(参考了暴风影音、迅雷影音)

FFmpeg开发(八)——Qt视频播放器之多线程的使用(参考了暴风影音、迅雷影音)

上篇文章介绍了:

FFmpeg开发(七)——Qt视频播放器之播放列表类(参考了暴风影音、迅雷影音)

本播放器系列相关的文章链接大家可以参考如下:

FFmpeg开发(四)——Qt实现一个视频播放器(参考了暴风影音、迅雷影音)

FFmpeg开发(五)——Qt视频播放器之封装FFmpeg类(参考了暴风影音、迅雷影音)

FFmpeg开发(六)——Qt视频播放器之封装音频类(参考了暴风影音、迅雷影音)

FFmpeg开发(七)——Qt视频播放器之播放列表类(参考了暴风影音、迅雷影音)

FFmpeg开发(八)——Qt视频播放器之多线程的使用(参考了暴风影音、迅雷影音)

FFmpeg开发(九)——Qt视频播放器之快进滑动条(参考了暴风影音、迅雷影音)

实现的额效果如下:

 

 我们知道Qt的界面主要是一个主线程,如果我们把解码的代码也在主界面类中实现的话,可能会导致主界面在播放视频的过程中出现卡顿的现象。所以我们一般会采用多线程的方式使用。

解码的代码创建一个继承QObject的类。然后在槽函数中实现视频解码:

#include <QObject>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = 0);
    ~Worker();

private:
    void work_read_video();
    void initData();

signals:
    void sig_finished();

public slots:
    void slot_dowork(bool bIsExit);
    void slot_closeWidget();

private:
    bool m_isExit;//线程未退出
};
#include "worker.h"
#include "ffmpeg.h"
#include "audioplay.h"
#include <QThread>
#include <list>
#include <QDebug>
using namespace std;

static list<AVPacket> videos;//用来存放解码前的视频帧
static int apts = -1;//音频的pts

Worker::Worker(QObject *parent) : QObject(parent)
{
    initData();
}

Worker::~Worker()
{

}

void Worker::initData()
{
    m_isExit = false;
}

void Worker::slot_dowork(bool bIsExit)
{
    work_read_video();
}

void Worker::work_read_video()
{
    char out[10000] = {0};


    while (!m_isExit)//线程未退出
    {

        if (!FFmpeg::getInstance()->m_isPlay)//如果为暂停状态,不处理
        {
            QThread::msleep(10);
            continue;
        }
        while (videos.size()>0)//确定list中是否有AVpacket包
        {
            AVPacket pack = videos.front();//每次取出list中的第一个AVPack包
            int pts = FFmpeg::getInstance()->getPts(&pack);//获得该包的pts
            if (pts > apts)//若视屏包大于音频包的pts,结束
            {
                break;
            }
            FFmpeg::getInstance()->decodeAVPacket(&pack);//解码视频帧
            //qDebug()<<"解码视频帧";
            av_packet_unref(&pack);//清理该AVPacket包
            videos.pop_front();//从list链表中删除
        }

        int free = AudioPlay::Get()->GetFree();//此时缓冲区的空间大小
        if (free < 10000)
        {
            QThread::msleep(1);
            continue;
        }

        AVPacket pkt = FFmpeg::getInstance()->readAVPacket();
        if (pkt.size <= 0)//未打开视频
        {
            QThread::msleep(10);
            continue;
        }
        if (pkt.stream_index == FFmpeg::getInstance()->m_audioStream)
        {
            apts = FFmpeg::getInstance()->decodeAVPacket(&pkt);//解码音频
            //qDebug()<<"解码音频帧";
            av_packet_unref(&pkt);//释放pkt包S
            int len = FFmpeg::getInstance()->ToPCM(out);//重采样音频
            AudioPlay::Get()->Write(out, len);//写入音频
            continue;
        }

        videos.push_back(pkt);
    }
}

void Worker::slot_closeWidget()
{
    m_isExit = true; //线程退出.
}

然后,在主线程中通过moveToThread函数放到新线程中:

    m_pVideoWorker = new Worker;
    m_pVideoWorker->moveToThread(&m_videoThread);
    m_videoThread.start();

    connect( &m_videoThread, &QThread::finished, m_pVideoWorker, &QObject::deleteLater );
    connect( this, SIGNAL( sig_palyVideo_work(bool) ), m_pVideoWorker, SLOT( slot_dowork(bool) ) );
    connect( m_pVideoWorker, SIGNAL( sig_finished() ), this, SLOT( slotPalyVideoFinishWork() ));

注意,主线程析构函数中要等待一下,才能关闭新创建的Worker线程:

    m_videoThread.quit();
    if(!m_videoThread.wait(3000)) //Wait until it actually has terminated (max. 3 sec)
    {
        m_videoThread.terminate(); //Thread didn't exit in time, probably deadlocked, terminate it!
        m_videoThread.wait(); //We have to wait again here!
    }

下一篇:

FFmpeg开发(九)——Qt视频播放器之快进滑动条(参考了暴风影音、迅雷影音)

本文原创作者:冯一川([email protected]),未经作者授权同意,请勿转载。

;