1. 程式人生 > >ffmpeg的demux流程簡析

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成員即可。