超详细解析FFplay之数据读取线程( 五 )


 
int stream_component_open(VideoState *is, int stream_index);
stream_component_open这个函数比较长,逐步分析 。
/* 为解码器分配?个编解码器上下?结构体 */avctx = avcodec_alloc_context3(NULL);if (!avctx)return AVERROR(ENOMEM);/* 将码流中的编解码器信息拷?到新分配的编解码器上下?结构体 */ret = avcodec_parameters_to_context(avctx, ic->streams[stream_in dex]->codecpar);if (ret < 0)goto fail;// 设置pkt_timebaseavctx->pkt_timebase = ic->streams[stream_index]->time_base;总结来说就是,先通过 avcodec_alloc_context3 分配了解码器上下? AVCodecContex,然后通过avcodec_parameters_to_context 把所选流的解码参数赋给 avctx,最后设了 time_base 。
注意,还有一个函数,有这样一个区别 。
avcodec_parameters_to_context 解码时?,avcodec_parameters_from_context则?于编码 。
/* 根据codec_id查找解码器 */codec = avcodec_find_decoder(avctx->codec_id);// 获取指定的解码器名字,如果没有设置则为NULLswitch(avctx->codec_type){case AVMEDIA_TYPE_AUDIO :is->last_audio_stream = strea m_index;// 获取指 定的解码器名字forced_codec_name = audio_codec_name;break;case AVMEDIA_TYPE_SUBTITLE:// 获取指 定的解码器名字is->last_subtitle_stream = strea m_index;forced_codec_name = subtitle_codec_name;break;case AVMEDIA_TYPE_VIDEO :is->last_video_stream = strea m_index;// 获取指 定的解码器名字forced_codec_name = video_codec_name;break;}}//如果名字找到了,根据名字去找对应的解码器if (forced_codec_name)codec = avcodec_find_decoder_by_name(forced_codec_name);if (!codec) {if (forced_codec_name)av_log(NULL, AV_LOG_WARNING,"No codec could be found with name '%s'n", forced_codec_name);elseav_log(NULL, AV_LOG_WARNING,"No decoder could be found for codec %sn", avcodec_get_name(avctx->codec_id));ret = AVERROR(EINVAL);goto fail;}总结来说,这段主要是通过 avcodec_find_decoder 找到所需解码器(AVCodec) 。如果?户有指定解码器,则设置 forced_codec_name,并通过 avcodec_find_decoder_by_name 查找解码器 。找到解码器后(如果用户没有指定解码器,就需要通过寻找,然后匹配,所以一般在代码中,都是要去匹配),就可以通过 avcodec_open2 打开解码器了 。forced_codec_name对应到?频、视频、字幕不同的传?的解码器名字,如果有设置,?如ffplay -acodec aac xx.flv, 此时audio_codec_name被设置为"aac"(音频解码器名字),则相应的forced_codec_name为“aac” 。
最后,一个大的switch语句,去启动不同的解码线程 。
switch (avctx->codec_type) {case AVMEDIA_TYPE_AUDIO:sample_rate = avctx->sample_rate;nb_channels = avctx->channels;channel_layout = avctx->channel_layout;/* prepare audio output 准备?频输出*/if ((ret = audio_open(is, channel_layout, nb_channels, sampl e_rate, &is->audio_tgt)) < 0)goto fail;is->audio_hw_buf_size = ret;is->audio_src = https://www.isolves.com/it/cxkf/kj/2020-09-15/is->audio_tgt;is->audio_buf_size = 0;is->audio_buf_index = 0;/* init averaging filter 初始化averaging滤镜, ?audio master时 使? */is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB );is->audio_diff_avg_count = 0;/* 由于我们没有精确的?频数据填充FIFO,故只有在?于该阈值时才进?校正?频 同步*/is->audio_diff_threshold = (double)(is->audio_hw_buf_size) / is->audio_tgt.bytes_per_sec;// 获取audio的stream索引is->audio_stream = stream_index;// 获取audio的stream指针is->audio_st = ic->streams[stream_index];// 初始化ffplay封装的?频解码器decoder_init(&is->auddec, avctx, &is->audioq, is->continue_r ead_thread);if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOG ENSEARCH| AVFMT_NO_BYTE_SEEK)) && !is->ic->iformat->read_seek){ 26 is->auddec.start_pts = is->audio_st->start_time; 27 is->auddec.start_pts_tb = is->audio_st->time_base; 28 }总结来说,即根据具体的流类型,作特定的初始化 。但不论哪种流,基本步骤都包括了ffplay封装的解码器的初始化和启动解码器线程:
decoder_init 初始化解码器:
//绑定对应的解码器上下?
d->avctx = avctx;
//绑定对应的packet队列
d->queue = queue;
//设置条件变量
//绑定VideoState的continue_read_thread,当解码线程没有packet可读时唤醒read_thread赶紧读取数据 。
d->empty_queue_cond = empty_queue_cond;
//初始化start_pts
d->start_pts = AV_NOPTS_VALUE;
//初始化pkt_serial
d->pkt_serial = -1;
 
decoder_start 启动解码器
//启?对应的packet 队列
packet_queue_start
//创建对应的解码线程
SDL_CreateThread
注意:需要注意的是,对应?频??,这?还初始化了输出参数,这块在讲?频输出的时候再重点展开 。


推荐阅读