上一节成功装载了ffmpeg库,接下来就是媒体文件的载入检测,但是在实际实现时发现:
Bffmpeg加载媒体文件时,需要记录媒体文件相关信息:上下文、音视频索引等,这些需要定义成员变量保存在Bffmpeg类中,加载文件的方法如果定义成静态成员函数,那么将无法访问到保存的媒体信息变量,如果定义成普通成员函数,则需要实例化一个对象,但该对象在工程中只需要一个即可。基于以上考虑,最后选择采用单例模式来设计Bffmpeg。
1、为Bffmpeg添加单例模式
首先想要添加静态成员方法GetInstance用于获取实例对象,构造函数想要私有化,防止在其他地方被实例化,还需要添加单例实例对象和Bffmpeg对象锁:
class Bffmpeg : QThread
{
public:
static Bffmpeg* GetInstance(); /* 获取单例实例对象 */
private:
Bffmpeg();
static QAtomicPointer<Bffmpeg> Instance; /* 单例实例对象 */
static QMutex ffmpegMutex; /* Bffmpeg对象锁 */
};
GetInstance的实现如下:
/********************************
* Bffmpeg* Bffmpeg::GetInstance()
* 功能:获取单例实例对象
* 返回:单例实例对象
* *****************************/
Bffmpeg* Bffmpeg::GetInstance()
{
/*
testAndSetOrdered实现逻辑:
testAndSetOrdered(expectedValue, newValue)
currentValue = _instance; //currentValue为QAtomicPointer<Bffmpeg>模板定义的指针变量
if (currentValue == expectedValue) {
currentValue = newValue;
return true;
}
return false;
*/
if (Instance.testAndSetOrdered(NULL, NULL)) {
QMutexLocker lock(&ffmpegMutex);
Instance.testAndSetOrdered(NULL, new Bffmpeg);
}
return Instance;
}
2、媒体流文件检测
打开文件按钮点击回调函数需要改成单例模式来设计,代码修改后:
/********************************
* void MainWindow::on_Bopenfile_btn_clicked()
* 功能:打开文件按钮点击回调函数
* *****************************/
void MainWindow::on_Bopenfile_btn_clicked()
{
QString FilePath = QFileDialog::getOpenFileName(this, QString("媒体文件"), QString("."), QString("视频文件(*.mp4 *.flv *.avi);;所有文件(*.*)"));
if (FilePath.isEmpty()) {
BLOG("Media File empty");
return;
}
if (0 != Bffmpeg::GetInstance()->BLoadMediaFile(FilePath)) {
BLOG("Media File illegal");
return;
}
return;
}
为Bffmpeg添加媒体信息相关变量,媒体流上下文FormatContext,视频流索引Video_index和音频流索引Audio_index:
class Bffmpeg : QThread
{
public:
static Bffmpeg* GetInstance(); /* 获取单例实例对象 */
int BLoadMediaFile(QString FilePath); /* 媒体文件加载 */
private:
Bffmpeg();
static QAtomicPointer<Bffmpeg> Instance; /* 单例实例对象 */
static QMutex ffmpegMutex; /* Bffmpeg对象锁 */
AVFormatContext *FormatContext = NULL; /* 媒体流上下文 */
int Video_index; /* 视频流索引 */
int Audio_index; /* 音频流索引 */
};
继续扩充BLoadMediaFile方法:
/********************************
* int Bffmpeg::BLoadMediaFile(QString FilePath)
* 功能:媒体文件加载
* 成功返回:0
* 失败返回:errcode
* *****************************/
int Bffmpeg::BLoadMediaFile(QString FilePath)
{
int Ret = 0;
char errbuf[128] = {0};
if (NULL != FormatContext) {
avformat_close_input(&FormatContext);
}
FormatContext = avformat_alloc_context();
/* 打开多媒体数据并且获得一些相关的信息 */
Ret = avformat_open_input(&FormatContext, FilePath.toLocal8Bit().data(), NULL, NULL);
if (Ret != 0) {
av_strerror(Ret, errbuf, sizeof(errbuf));
BLOG("avformat_open_input fail, Ret[%d], Err[%s]", Ret, errbuf);
return Ret;
}
/* 读取一部分音视频数据并且获得一些相关的信息 */
Ret = avformat_find_stream_info(FormatContext, NULL);
if (Ret < 0) {
av_strerror(Ret, errbuf, sizeof(errbuf));
BLOG("avformat_open_input fail, Ret[%d], Err[%s]", Ret, errbuf);
avformat_close_input(&FormatContext);
return Ret;
}
/* 查找视频流索引 */
Video_index = av_find_best_stream(FormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if ((AVERROR_STREAM_NOT_FOUND == Video_index) || (AVERROR_DECODER_NOT_FOUND== Video_index)) {
BLOG("av_find_best_stream fail, Video_index[%d]", Video_index);
avformat_close_input(&FormatContext);
return -1;
}
/* 找视频解码器 */
AVCodec *Codec = avcodec_find_decoder(FormatContext->streams[Video_index]->codecpar->codec_id);
if (NULL == Codec) {
BLOG("avcodec_find_decoder fail");
avformat_close_input(&FormatContext);
return -1;
}
/* 打开解码器,初始化FormatContext内部解码器上下文,FormatContext->streams[Video_index]->codec内存在avformat_open_input调用时已分配 */
Ret = avcodec_open2(FormatContext->streams[Video_index]->codec, Codec, NULL);
if (Ret < 0) {
av_strerror(Ret, errbuf, sizeof(errbuf));
BLOG("avcodec_open2 fail, Ret[%d], Err[%s]", Ret, errbuf);
avformat_close_input(&FormatContext);
return Ret;
}
/* 查找音频流索引 */
Audio_index = av_find_best_stream(FormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if ((AVERROR_STREAM_NOT_FOUND == Audio_index) || (AVERROR_DECODER_NOT_FOUND== Audio_index)) {
BLOG("av_find_best_stream fail, Audio_index[%d]", Audio_index);
avformat_close_input(&FormatContext);
return -1;
}
/* 找音频解码器 */
Codec = avcodec_find_decoder(FormatContext->streams[Audio_index]->codecpar->codec_id);
if (NULL == Codec) {
BLOG("avcodec_find_decoder fail");
avformat_close_input(&FormatContext);
return -1;
}
/* 打开解码器,初始化FormatContext内部解码器上下文,FormatContext->streams[Audio_index]->codec内存在avformat_open_input调用时已分配 */
Ret = avcodec_open2(FormatContext->streams[Audio_index]->codec, Codec, NULL);
if (Ret < 0) {
av_strerror(Ret, errbuf, sizeof(errbuf));
BLOG("avcodec_open2 fail, Ret[%d], Err[%s]", Ret, errbuf);
avformat_close_input(&FormatContext);
return Ret;
}
return 0;
}