Demuxer解复用架构—FFmpeg源码分析
FFmpeg 的 Demuxer 解复用架构可以分为 3 个部分,以 MP4 为例,其他的格式也是类似的,如下:
1,avformat_open_input,打开输入文件
avformat_open_input()
里面的 init_input()
主要负责探测输入文件的格式,一旦确认了输入格式,就会把确定的 Demuxer
赋值给 s->iformat
,然后调这个 Demuxer
的 read_header
接口来读取解析头部。
MP4 的 read_headers
接口是 mov_read_header()
,FLV 的 read_headers
接口是 flv_read_header()
MP4 与 FLV 的 Demuxer
的定义如下:
AVInputFormat ff_mov_demuxer = {
.name = "mov,mp4,m4a,3gp,3g2,mj2",
.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
.priv_class = &mov_class,
.priv_data_size = sizeof(MOVContext),
.extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v",
.read_probe = mov_probe,
.read_header = mov_read_header,
.read_packet = mov_read_packet,
.read_close = mov_read_close,
.read_seek = mov_read_seek,
.flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS,
};
AVInputFormat ff_flv_demuxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.read_probe = flv_probe,
.read_header = flv_read_header,
.read_packet = flv_read_packet,
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
.priv_class = &flv_class,
};
提示:avformat_open_input()
读取的是头部信息,并不会去读取文件里面的编码数据。
2,avformat_find_streaminfo,提取各个流的信息
avformat_find_stream_info
这个名字其实非常恰当的,它做的任何事情,都是为了 find(找到),stream(流),info(信息)。
avformat_find_stream_info()
里面有可能会对编码数据进行解析,是因为,有些参数,例如 pix_fmt
(像素格式),在容器层可能是没有记录的,而是在编码层记录的,所以你必须解析编码层的数据才能拿到那些参数,然后放到 AVStream
的 codecpar
里。
avformat_find_streaminfo()
解码出来的 AVFrame
是直接丢弃的,但是他从输入文件读取的 AVPacket
会放进去 internal->packet_buffer
缓存里面,后面 av_read_frame() 会优先从 internal->packet_buffer
缓存里面拿 AVPacket
,而不是从文件里面读取。
可以在命令行用 -nobuffer
来禁止 packet_buffer
缓存,这样 avformat_find_streaminfo()
读取出来的 AVPacket
就会直接丢弃。这样做可以降低延迟,但是可能会导致解码器刚开始输出的数据花屏。
3,av_read_frame,读取 AVPacket 编码数据
av_read_frame()
与 read_frame_internal()
的区别在于,av_read_frame
会优选从 packet_buffer
缓存里面读数据,缓存里没数据再调 read_frame_internal()
从输入文件读数据。
后面就让我们学习,解复用(Demuxer)三部曲的具体细节。