ffmpeg的demux流程简析

只是简单说说,没太多深入。

【avformat_open_input 流程】
这里我只说根据字节流来分析的过程,而不管扩展名啊什么的。
1、如果没申请,它会内部调用avformat_alloc_context帮你申请结构体;
2、如果有io的pb,它会自动加上AVFMT_FLAG_CUSTOM_IO这个flag;
3、然后调用init_input,这个执行probe,也就是获取到iformat;
4、然后为iformat的priv_data开辟空间;
5、然后执行ff_id3v2_read,这里处理下id3v2的tags,因为id3v2保存到文件头;
6、再之就是执行关键的iformat的read_header函数指针;
7、read_header成功就是返回成功了。

init_input:
1、直接执行av_probe_input_buffer2;
2、检测max_probe_size什么的;
3、执行av_probe_input_format2尝试获取到iformat;
av_probe_input_format2:
1、开循环查找format的list,即av_iformat_next;
2、对每个iformat的read_probe函数指针执行调用,判断有没有认的,这个返回score;
3、有认的,拿到score,然后接下来匹配扩展名啊,文件名啊什么的,取最大的score的format返回。

avformat_find_stream_info:
这个函数会不断的调用ff_check_interrupt,你可以提供中断,如果你觉得打开过久,则可以让其退出。
1、如果需要parse,则av_parser_init初始化parser;
2、根据codec_id,调用find_decoder查找decoder;
3、尝试调用avcodec_open2打开codec;
4、如果有codec,则希望从codec里面获取到参数信息,执行read_frame_internal读一个frame缓存;
5、执行try_decode_frame,对这个packet进行解码,然后从codec里面获取到解码后的参数什么的;
6、反正下面就是填充每个track的完整stream info。

【av_read_frame 流程】
1、判断一下有没有AVFMT_FLAG_GENPTS这个flag,有的话表示要求自动递增类的生成pts;
2、不然其实就是调用一下read_frame_internal方法,参数都一样;
3、调用av_init_packet初始化一下AVPacket;
4、接下来是一个while循环,关键是调用ff_read_packet来查找下一个packet,调用ff_read_packet的参数也跟read_frame_internal参数是一样的;
5、如果调用ff_read_packet成功,则根据ff_read_packet读到的下一个packet里面的stream_index取得st;
6、判断下st中的need_parsing这个flag,决定要不要执行分包,即调用packet的parser;
7、如果需要分包,则调用av_parser_init初始化parser(初始化只会执行一次),然后调用parse_packet来做进一步的parse;
8、如果不需要,则调用compute_pkt_fields后直接输出packet,compute_pkt_fields的作用其实就是对pts、dts进行一些保存修复和计算duration什么的;

下面来分析下ff_read_packet方法:
1、其实就是调用了iformat的read_packet指针,我们以mp4为例,看看下这个函数mov_read_packet;
2、执行了一下mov_find_next_sample,查找下一个sample的索引;
3、然后根据下一个sample索引中的文件pos,执行avio_seek,因为mp4的sample的存储可以不是连续性的;
4、然后调用av_get_packet,从IO数据中读取sample的大小的数据到AVPacket;
5、如果有ctts、根据ctts修正一下时间戳,返回。

【av_seek_frame 流程】
1、调用seek_frame_internal,参数一样;
2、判断有AVSEEK_FLAG_BYTE的flag,有的话直接返回seek_frame_byte,如果不支持byte_seek,则直接返回-1错误;
3、判断请求seek的stream_index为-1,则调用av_find_default_stream_index获取到默认的seek流,使用这个流来做seek;
4、执行一下ff_read_frame_flush把缓存队列清空,然后调用iformat->read_seek方法;

分析下read_seek方法,以mp4容器的mov_read_seek:
1、其实就是对每个流执行下mov_seek_stream,看下这个函数;
2、调用av_index_search_timestamp搜索关键桢索引,根据请求的时间,下面是这个方法的代码:
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp, int flags)
{ return ff_index_search_timestamp(st->index_entries, st->nb_index_entries,wanted_timestamp, flags); }
3、av_index_search_timestamp返回的是一个index下标,然后根据这个下标调整下内部的一些current成员即可。

你可能感兴趣的:(ffmpeg的demux流程简析)