这篇博客记录一下使用多线程来对FFmpeg的demux模块和decode模块的降耦合,解封装和解码各自单独占用一个线程,正常来讲音频解码和视频解码应该各自占用一个线程,在这里为了简单就把他们合并为了一个线程,没加入视频和音频的渲染,读者可以根据情况自行添加。
运行效果:
- 启动之后两个线程成功进入,在媒体文件读取结束后安全停止线程。
所有的文件示意图:
XThread类:线程函数的基类,解封装模块和解码模块分别对其进行继承。
IDemux类:解封装模块的接口类,对XThread进行继承。
FFDemux类:解封装模块的具体实现类,继承IDemux类
IDecode类:解码模块的接口类,对XThread进行继承
FFDecode类:解码模块的实现类,对IDecode进行继承
XData类:数据类,主要用于解码和解封装之间的数据传递(AVPacket等)对AVPacket设计成列表,进行缓冲。
添加视频渲染的部分直接在IDecode的线程中把解码后的数据传递给SDL就可以了,可以自行添加。
Main函数
#include <stdio.h>
#include "FFDemux.h"
#include "FFDecode.h"
#include <windows.h>
extern "C"
{
#include <libavformat/avformat.h>
}
/*
* 之前的错误就是list的元素一直用的是AVPacket* ,现在改成XData
* 以前那种做法不能够将XData的isAudio正确传递到send和Recv函数,所以导致了错误。
*/
int main(int argc, char* argv[])
{
std::list<XData>* Lpkt =new std::list<XData>();
char url[] = "test.mov";
IDemux* dm = new FFDemux();
IDecode* de = new FFDecode();
de->pkt = Lpkt;
dm->packet = Lpkt;
if (!dm->Open(url))
{
return -1;
}
XData apara=dm->GetAPara();
bool re=de->Open(apara);
if (!re)
{
printf("de->Open(apara) failed!\n");
return -1;
}
XData vpara = dm->GetVPara();
re = de->Open(vpara);
if (!re)
{
printf("de->Open(vpara) failed!\n");
return -1;
}
//读取pkt线程开始
dm->Start();
//解码模块开始
de->Start();
while (true)
{
if (dm->stopthread)
{
//停止线程
dm->Stop();
while (!de->pkt->empty())
{
Sleep(1);//等待解码线程把剩余pkt解码完毕。
}
de->Stop();//停止解码线程运行
dm->Close();
de->Close();
apara.Drop();
vpara.Drop();
return 0;
}
Sleep(10);
}
}
其他的代码太多了,这里就不贴了,可以在下方的链接下载,包含一个单线程的视频播放器(有视频和音频的渲染),还包含本篇博文介绍的工程。下载使用vs打开即可运行。