目錄
以下分析 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;