ffmpeg 源代碼簡單分析 : av_read_frame()
http://blog.csdn.net/leixiaohua1020/article/details/12678577
ffmpeg中的av_read_frame()的作用是讀取碼流中的音頻若干幀或者視頻一幀。例如,解碼視頻的時候,每解碼一個視頻幀,需要先調 用 av_read_frame()獲得一幀視頻的壓縮數據,然后才能對該數據進行解碼(例如H.264中一幀壓縮數據通常對應一個NAL)。
對該函數源代碼的分析是很久之前做的了,現在翻出來,用博客記錄一下。
上代碼之前,先參考了其他人對av_read_frame()的解釋,在此做一個參考:
通 過av_read_packet(***),讀取一個包,需要說明的是此函數必須是包含整數幀的,不存在半幀的情況,以ts流為例,是讀取一個完整的 PES包(一個完整pes包包含若干視頻或音頻es包),讀取完畢后,通過av_parser_parse2(***)分析出視頻一幀(或音頻若干幀), 返回,下次進入循環的時候,如果上次的數據沒有完全取完,則st = s->cur_st;不會是NULL,即再此進入av_parser_parse2(***)流程,而不是下面的 av_read_packet(**)流程,這樣就保證了,如果讀取一次包含了N幀視頻數據(以視頻為例),則調用 av_read_frame(***)N次都不會去讀數據,而是返回第一次讀取的數據,直到全部解析完畢。
http://hi.baidu.com/cuihuanyubupt/item/75f21dfbd2f18e1fff358229
Ffmpeg解析media容器過程
1)調用av_read_frame函數
如果packet_buffer存在數據,根據pts返回AVPacket
如果packet_buffer不存在數據調用函數av_read_frame_internal
在 ffmpeg中實現了將format格式的packet,最終轉換成一幀幀的packet,並解析填充了packet的pts,dts,等信息,為最終解 碼提供了重要的數據,av_read_frame_internal,調用av_read_packet,每次只讀取一個包,然后直到parser完這個 包的所有數據,才開始讀取下一個包,parser完的數據被保存在parser結構的數據緩沖中,這樣即使av_read_packet讀取的下一包和前 一包的流不一樣,由於parser也不一樣,所以實現了av_read_frame_internal這個函數調用,可以解析出不同流的es流,而 av_read_frame_internal函數除非出錯否則必須解析出一幀數據才能返回
調用函數av_parser_parse2(***)分析出視頻一幀(或音頻若干幀)
2)ffmpeg對媒體容器格式封裝在AVInputFormat結構體中,如:
AVInputFormat ff_w64_demuxer = {
"w64",
NULL_IF_CONFIG_SMALL("SonyWave64 format"),
sizeof(WAVContext),
w64_probe,
w64_read_header,
wav_read_packet,
NULL,
wav_read_seek,
.flags = AVFMT_GENERIC_INDEX,
.codec_tag = (const AVCodecTag*const []){ff_codec_wav_tags, 0},
};
對格式定義了read_header,read_packet,read_seek函數,來讀數據
3)ffmpeg定義AVCodecParser用來對不同視頻編碼標准,分析出一個完整的一幀,如
AVCodecParser ff_h264_parser = {
{ CODEC_ID_H264 },
sizeof(H264Context),
init,
h264_parse,
close,
h264_split,
};
h.264的parser
4)h.264 decoder
AVCodec ff_h264_decoder = {
"h264",
AVMEDIA_TYPE_VIDEO,
CODEC_ID_H264,
sizeof(H264Context),
ff_h264_decode_init,
NULL,
ff_h264_decode_end,
decode_frame,
/*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |
CODEC_CAP_FRAME_THREADS |
CODEC_CAP_SLICE_THREADS,
.flush= flush_dpb,
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
.update_thread_context = ONLY_IF_THREADS_ENABLED(decode_update_thread_context),
.profiles = NULL_IF_CONFIG_SMALL(profiles),
};