ffmpeg源码分析——libavformat.a---mp4文件读取过程
目录
以下分析 mp4文件读取的时候,这个 AVInputFormat 结构体具体的注册初始化过程。
环境:ubuntu18.04 + ffmpeg4.1源码
这里假设已经下载好ffmpeg源码,编译通过,可以运行如下命令:
# ./ffmpeg -i test.mp4 -codec copy -bsf: h264_mp4toannexb -f h264 tmp.264 -report
这条命令的效果是,把 输入文件test.mp4 中的h264源数据复制输出到 tmp.264文件。将会得到一个tmp.264裸流文件。
命令解释:
-i test.mp4 输入
-codec copy 编码器,直接copy
-bsf:h264_mp4toannexb -bsf表示 BitStreamFilter ,比特流过滤器, h264_mp4toannexb 即一个过滤器的名称,其源码在libavcodec/h264_mp4toannexb.c 就ffmpeg中是一个特定的比特流过滤器。
-f h264 强制使用h264格式
-report 可要可不要,加上这个命令,会在当前目录生成一份程序的运行log报告。
初步分析其源码,ffmpeg.c文件。
ffmpeg.c 这里简单列一下其调用流程中主干函数。
(ffmpeg.c):
main.c()
1.0 ffmpeg_parse_options(argc, argv); /* parse options and open all input/output files */
2.0 transcode()// The following code is the main loop of the file converter
2.1 transcode_init()
2.2 transcode_step()
2.2.1 process_input()//read one packet and processed
2.2.1.1 get_input_packet() //read one packet
(utils.c) 2.2.1.1.1 av_read_frame()->read_frame_internal()->ff_read_packet()-> s->iformat->read_packet(s, pkt);
ffmpeg 既然能支持不同格式的视频输入文件,其中某些函数当然以动态注册的方式来匹配不同的输入。上述函数调用,在utils.c的av_read_frame即从输入文件中读取一帧数据,具体往下调用, 一直到s->iformat->read_packet(s, pkt); 即AVFormatContext (音视频格式上下文)结构体中的 struct AVInputFormat *iformat(音视频输入格式) 的函数 read_packet。 这个函数指针根据不同的输入类型,来注册。
以下分析 mp4文件读取的时候,这个 AVInputFormat 结构体具体的注册初始化过程。
(很多检查判断,可以自行添加打印信息具体确定流程,避免干扰主线分析)
ffmpeg.c ->
ffmpeg_opt.c:: ffmpeg_parse_options()
->open_files() //open input files
->utils.c :: avformat_open_input() // open the input file with generic avformat function
->init_input()
->av_probe_input_buffer2() // Probe a bytestream to determine the input format
->av_probe_input_format2()->av_probe_input_format3() //Guess file format
av_probe_input_format3()
{... format.c L: 160
while ((fmt1 = av_demuxer_iterate(&i))) //遍历所有 demuxer, 匹配合适的demuxer
...
}
在ffmpeg_opt.c open_input_file 调用 avformat_open_input()之后,添加如下调试信息:
if(file_iformat && file_iformat->name)
printf("wang track inputformate name file_iformat->name %s [%d%s]\n",file_iformat->name,__LINE__,__FUNCTION__);
if(ic->iformat && ic->iformat->name)
printf("wang track inputformate name ic->file_iformat->name %s [%d%s]\n",ic->iformat->name,__LINE__,__FUNCTION__);
对应输出
这个AVInputFormat *iformat 的name 为“mov,mp4,m4a,3gp,3g2,mj2”
即匹配到 libavformat/mov.c 中定义的 AVInputFormat ff_mov_demuxer
这里就可以把之前读取帧数据的 read_packet(s, pkt) 对应到这里 的 mov_read_packet
具体mp4文件怎么读取到一帧视频数据,即 mov_read_packet函数分析,待续。
附议:1.0 全局demuxer变量的定义。
format.c :: av_probe_input_format3()匹配demuxer解复用器的时候,遍历demuxer_list 结构体数组,其中存储所有的demuxer,全部是全局变量,其中某些demuxer, 却只找到external的外部声明,却不能直接查找到其定义。比如 ff_h264_demuxer
用grep命令匹配一下所有文件(包括编译出来的文件)
在 libavformat/h264dec.o 文件中匹配到这个变量符号,查看 这个文件,发现这样的一个宏定义:
FF_DEF_RAWVIDEO_DEMUXER(h264, "raw H.264 video", h264_probe, "h26l,h264,264,avc", AV_CODEC_ID_H264)
这个宏定义FF_DEF_RAWVIDEO_DEMUXER,展开来看,就是用来定义不同的AVInputFormat 变量的。上述就定义了变量
AVInputFormat ff_h264_demuxer;