自定义组件主要包括protocol和demuxer。协议有:ijkimp_ff_ijkio_protocol,ijkimp_ff_async_protocol,ijkimp_ff_ijktcphook_protocol,ijkimp_ff_ijkhttphook_protocol和ijkimp_ff_ijklongurl_protocol;demuxer为ijkimp_ff_ijklivehook_demuxer。
注册各种协议
消息循环处理函数media_player_msg_loop是在调用接口ijkmp_ios_create时候传到底层的,调用ijkmp_prepare_async时,创建了消息处理线程。media_player_msg_loop里面主要就是获取消息并处理消息。
在函数ffp_prepare_async_l调用stream_open,stream_open中创建了视频渲染线程,该线程主要是进行视频渲染工作,并对视频进行同步,同步相关逻辑主要在这个线程里面,同步的大概思路就是:有一个绝对时间作为同步起点,然后计算当前帧与上一帧时间差,然后与当前绝对时间基准源比较,如果不到时间,计算remaining_time,然后sleep等待。如果时间参考时钟不是视频源,还会时钟就行调整,如果当前视频帧落后于主时钟源,则需要减小下一帧画面的等待时间,如果视频帧超前,并且该帧的显示时间大于显示更新门槛,则显示下一帧的时间为超前的时间差加上上一帧的显示时间,如果视频帧超前,并且上一帧的显示时间小于显示更新门槛,则采取加倍延时的策略。
intffp_prepare_async_l(FFPlayer*ffp,constchar*file_name)
{
assert(ffp);
assert(!ffp->is);
assert(file_name);
if(av_stristart(file_name,"rtmp",NULL) ||
av_stristart(file_name,"rtsp",NULL)) {
// There is total different meaning for 'timeout' option in rtmp
av_log(ffp, AV_LOG_WARNING, "remove 'timeout' option for rtmp.\n");
av_dict_set(&ffp->format_opts,"timeout",NULL,0);
}
/* there is a length limit in avformat */
if(strlen(file_name) +1>1024) {
av_log(ffp,AV_LOG_ERROR,"%s too long url\n",__func__);
if (avio_find_protocol_name("ijklongurl:")) {
av_dict_set(&ffp->format_opts,"ijklongurl-url", file_name,0);
file_name ="ijklongurl:";
}
}
av_log(NULL, AV_LOG_INFO, "===== versions =====\n");
ffp_show_version_str(ffp, "ijkplayer", ijk_version_info());
ffp_show_version_str(ffp, "FFmpeg", av_version_info());
ffp_show_version_int(ffp, "libavutil", avutil_version());
ffp_show_version_int(ffp, "libavcodec", avcodec_version());
ffp_show_version_int(ffp, "libavformat", avformat_version());
ffp_show_version_int(ffp, "libswscale", swscale_version());
ffp_show_version_int(ffp, "libswresample", swresample_version());
av_log(NULL, AV_LOG_INFO, "===== options =====\n");
ffp_show_dict(ffp, "player-opts", ffp->player_opts);
ffp_show_dict(ffp, "format-opts", ffp->format_opts);
ffp_show_dict(ffp, "codec-opts ", ffp->codec_opts);
ffp_show_dict(ffp, "sws-opts ", ffp->sws_dict);
ffp_show_dict(ffp, "swr-opts ", ffp->swr_opts);
av_log(NULL, AV_LOG_INFO, "===================\n");
av_opt_set_dict(ffp, &ffp->player_opts);
if(!ffp->aout) {
ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);
if(!ffp->aout)
return-1;
}
#if CONFIG_AVFILTER
if(ffp->vfilter0) {
GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);
ffp->vfilters_list[ffp->nb_vfilters -1] = ffp->vfilter0;
}
#endif
VideoState*is =stream_open(ffp, file_name,NULL);
if(!is) {
av_log(NULL, AV_LOG_WARNING, "ffp_prepare_async_l: stream_open failed OOM");
return EIJK_OUT_OF_MEMORY;
}
ffp->is= is;
ffp->input_filename=av_strdup(file_name);
return0;
}
stream_open还创建了read_thread,read_thread里面主要有打开文件或流,然后并获取媒体信息。拿到信息后,初始化音视频解码器,并开创建了音视频解码线程。线程创建后,开始demuxer文件,并解除音视频pkt,并将pkt放到对应的音频、视频和字幕pkt队列里面。
音频解码线程和视频解码线程的主要工作是从音频和视频pkt队列里面取包并解码,并将解码数据送到缓冲区等待渲染。
ffpipeline主要就是运用c函数指针,实现动态选择编解码接口,并与在硬解和软解之间切换。
网友评论