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]),未经作者授权同意,请勿转载。