1背景
上面这几篇,是从线程开始,加载视频流。上一篇处理了开始初始化线程,这一篇继续后续。
源码存放位置 github.com/xcyxiner/xp…
2从线程开始读取流
这次在之前的基础上,继续(分支 ffmpeg_play_7_11)
2.1打开媒体文件
先在hffplayer.h头文件添加fmt_opts
private:
AVFormatContext* fmt_ctx;
AVDictionary* fmt_opts;
然后在hffplayer.cpp的构造函数里初始化
HFFPlayer::HFFPlayer() {
fmt_opts=NULL;
fmt_ctx=NULL;
}
然后在hffplayer.cpp的open里添加如下代码
av_dict_set(&fmt_opts,"buffer_size","2048000",0);
ret = avformat_open_input(&fmt_ctx, ifile.c_str(), ifmt, &fmt_opts);
if (ret != 0) {
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
return ret;
}
运行调试,正常应该是能返回0
2.2 查找是否有流
添加如下代码
//查找是否有流
ret=avformat_find_stream_info(fmt_ctx,NULL);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
return ret;
}
调试并检查是否ret为0
2.3 获取视频流音频流索引
头文件添加如下代码
private:
int video_stream_index;
int audio_stream_index;
int subtitle_stream_index;
在open中添加如下代码
//获取视频流音频流索引
video_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
audio_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
subtitle_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_SUBTITLE,-1,-1,NULL,0);
if(video_stream_index<0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-20;
return ret;
}
调试并运行
2.4 根据索引获取视频流
AVStream* video_stream=fmt_ctx->streams[video_stream_index];
2.5 获取视频源文件编码,直接使用软件解码器
//获取视频源文件编码
AVCodecParameters* codec_param=video_stream->codecpar;
//解码器
const AVCodec* codec=NULL;
codec=avcodec_find_decoder(codec_param->codec_id);
if(codec==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-30;
return ret;
}
调试并运行
2.6 解码器上下文
先在头文件添加codec_ctx的成员变量
private:
AVFormatContext* fmt_ctx;
AVDictionary* fmt_opts;
AVCodecContext* codec_ctx;
AVDictionary* codec_opts;
然后在构造函数初始化
HFFPlayer::HFFPlayer() {
fmt_opts=NULL;
fmt_ctx=NULL;
codec_ctx = NULL;
codec_opts=NULL;
}
再在open中添加如下代码
// 解码器上下文
codec_ctx=avcodec_alloc_context3(codec);
if(codec_ctx==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-40;
return ret;
}
追加断点并调试
2.7 配置解码上下文的解码参数 以及引用计数
// 配置解码上下文的解码参数
ret=avcodec_parameters_to_context(codec_ctx,codec_param);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
return ret;
}
//配置引用计数
if(codec_ctx->codec_type==AVMEDIA_TYPE_VIDEO||
codec_ctx->codec_type==AVMEDIA_TYPE_AUDIO){
av_dict_set(&codec_opts,"refcounted_frames","1",0);
}
2.8 解码
//解码
ret=avcodec_open2(codec_ctx,codec,&codec_opts);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
return ret;
}