FFMPEG学习之旅(三)——FFMPEG 和QT实现简单直播播放器(1)

分类: KMPlayer资讯 发布时间:2019-02-03 12:53

前面通过学习初步了解了FFMPEG的功能以及如何在VS中使用FFMPEG进行开发环境的搭建,今天我们尝试通过FFMPEG和QT5 来实现一个非常简单的播放器,用来实时播放网络上的直播流。先来看一下,实现的效果图:

  • 准备工作:

(1)下载并安装QT ,关于QT的配置及使用,大家可以百度了解相关情况;

  • 相关类的设计:

为了实现上面的播放器,考虑进行各模块的封装,以便于后期的修改维护,同事减少模块之间的耦合性;总体上分为四个类,分别为XData,XDataThread,XPlay,XRtmpPlay,在VS2015里,类类型图和各类之间的关系如图所示:

各类的具体功能如下:

XData :数据封装类,实现音视频数据流、音视频序列时间戳(PTS),以及数据大小的封装、释放。

XDataThread:继承者自QT的QThread,内部维护了一个List 队列,该队列实现对XData动态插入、删除;

XPlay:是XRtmpPlay 的父类,使用单件模式,实现播放初始化、播放、停播等的抽象类,具体功能由XRtmpPlay 实现。

一:XData类的实现

XData 类成员data,存放是从decode 出来的数据,注意如果是视频数据,则需要将解码后的图像转为AV_PIX_FMT_RGB24格式,否则在用QT的QImage 类的Format_RGB888进行图像的绘制时将出现崩溃,如果是音频,则需要进行音频的重采样,具体在下面的XRtmpPlay类中进行说明;XData 在填写音视频数据裸流的同时,还需要加入时间戳数据,主要是为了实现影视频同步播放。

extern"C"

{

#include

}

//数据释放

voidXData::Drop()

{

if (data)

delete data;

data = 0;

size = 0;

}

//数据分配,进行大小、PTS的设置

XData::XData(char *data, intsize, longlongp)

{

this->size = size;

this->pts = p;

this->data = newchar[size];

memcpy(this->data, data, size);

}

......

    二:XDataThread 类的实现:

       #include

#include

#include"XData.h"

#include

classXDataThread:publicQThread

{

public:

// 列表最大值,超出删除最老的数据

int maxList = 200;

//在列表结尾插入

virtualvoid Push(XDatad);

//读取列表中最早的数据,返回数据需要调用XData.Drop清理

virtualXData Pop();

//启动线程

virtualbool Start();

//退出线程,并等待线程退出(阻塞)

virtualvoid Stop();

virtualvoid Clear();

         XDataThread();

virtual ~XDataThread();

protected:

//存放交互数据插入策略先进先出

         std::list<XData> datas;

//交互数据列表大小

int dataCount = 0;

//互斥访问 datas;

QMutex mutex;

//处理线程退出

bool isExit = false;

};

/* XDataThread.CPP */

 voidXDataThread::Clear()

{

         mutex.lock();

while (!datas.empty())

         {

                   datas.front().Drop();

                   datas.pop_front();

         }

         mutex.unlock();

}

//在列表结尾插入

voidXDataThread::Push(XDatad)

{

         mutex.lock();

if (datas.size() > maxList)

         {

                   datas.front().Drop();

                   datas.pop_front();

         }

         datas.push_back(d);

         mutex.unlock();

}

//读取列表中最早的数据

XDataXDataThread::Pop()

{

         mutex.lock();

if (datas.empty())

         {

                   mutex.unlock();

returnXData();

         }

XData d = datas.front();

         datas.pop_front();

         mutex.unlock();

return d;

}

//启动线程

boolXDataThread::Start()

{

         isExit= false;

QThread::start();

returntrue;

}

//退出线程,并等待线程退出(阻塞)

voidXDataThread::Stop()

{

         isExit= true;

         wait();

}

XDataThread::XDataThread()

{

}    

三:XPlay 类的实现:

/*XPlay.h*/

classXPlay :publicXDataThread

{

Q_OBJECT

public:

char * outUrl="";

//窗体上播放时的视频尺寸,具体根据l bRanderVideo控件的大小进行自适应

int  vWidth=320;

int  vHeight=240;

//实际的视频尺寸

int  rWidth = 1280;

int  rHeight = 720;

//音频Sample 大小

int     out_audioChannels=2;

int     out_audioSmapleRate=44100;

int     out_audioSampleBit=16;

//音视频流索引

int  videoIndex=1;

int  audioIndex=0;

staticXPlay *Get(intindex);

         //初始化ffmpeg 相关对象,取得音视频解码器上下文信息等

virtualbool Init(char *url,intwidth,intheight) = 0;

virtualvoid Play()=0;

virtualvoid Stop()=0;

virtual ~XPlay();

protected:

         XPlay();

signals:

void randerPic(char * xData,intw,inth);   //图像渲染信号处理通知,具体由主窗体程序实现;

signals:

void playsound(char *xData,intlen);      //音频信号处理通知,具体由主窗体程序实现

};

   上面我们实现了三个类的设计,下一篇我们将介绍核心类XRtmpPlay的实现。