FFMPEG 3.4.2 - ffmpeg源代碼分析 (三)

1. Frame在Filters中的流動(dòng)

如下圖,這里涉及的是Frame從buffer filter到buffer_sink filter的流動(dòng)。圖中省略了一些filter,但不影響分析。

  • 一個(gè)pad link連接一個(gè)源filter和一個(gè)目的filter。Pad link中有一個(gè)frame queue。
  • FFMPEG的基本想法是pad link的源filter把frame推入pad link,再通知目的filter去處理。用ff_filter_frame()做這件事。
  • Filter的處理方式有兩種:
    • 一種是單輸入的,如split, crop。一個(gè)輸入準(zhǔn)備好就可以開始處理。單輸入Filter應(yīng)該實(shí)現(xiàn)filter_frame()函數(shù),在filter_frame()中調(diào)用ff_filter_frame()將frame推到下一個(gè)filter。
    • 另一種是多輸入的,如overlay。一個(gè)輸入準(zhǔn)備好還不能決定輸出,要先緩存,等到足夠的輸入才處理。多輸入filter應(yīng)該實(shí)現(xiàn)activate()函數(shù),前幾次activate()時(shí)緩存frame,最后一次才調(diào)用ff_filter_frame()。
  • 為避免函數(shù)調(diào)用嵌套過深,沒有采用迭代的方式,而是在循環(huán)中實(shí)現(xiàn)。具體做法是:filter有個(gè)ready值,缺省值為0;用ff_filter_frame()推入frame后,增大目的filter的ready值,比如改為300;目的filter處理后,將ready改回0;每一次循環(huán)時(shí)找出ready值最大的filter,調(diào)用ff_filter_frame()或activate()推動(dòng)下一輪處理。
    • 前面的圖顯示的是buffer filter將frame推入pad link后的情況。split.ready=300意味著下一步循環(huán)將是它處理。這里有frame的queue被標(biāo)記為更深的綠色。
    • 下面的圖顯示的是split處理后的情況。(實(shí)際上還有一個(gè)中間狀態(tài),后面會(huì)再提到)split.ready恢復(fù)到0。overlay.ready和crop.ready變成300。Frame就是這樣一步步往前推進(jìn)的。
    • 可以ready設(shè)成不同值來控制處理的先后順序。
  • Frame在FilterGraph中的流動(dòng)是從av_buffer_add_frame_flags()開始的。這時(shí)的Frame是已經(jīng)解碼的frame。
    • 注意buffer filter自己有一個(gè)frame queue。av_fifo_generic_write()將frame寫入buffer filter的queue。
    • 在request_frame()中,av_fifo_generic_read()從buffer filter的queue中讀出frame,調(diào)用ff_filter_frame()。如前所說,它將frame推入pad link,標(biāo)記下一個(gè)filter的ready值為300。這里是split filter。
  • 下一步調(diào)用push_frame()。Push_frame()實(shí)現(xiàn)了前面說的,在循環(huán)向前推進(jìn)Frame。
    • 如下圖,ff_filter_graph_run_once()(用黃色標(biāo)出)被循環(huán)調(diào)用。
    • 首先調(diào)用ff_filter_activate()。對(duì)于多輸入filter,調(diào)用它的activate();對(duì)于單輸入filter(沒有activate()),調(diào)用ff_filter_activate_default()。
    • 如前所說,在activate()中,多輸入filter或者緩存,或者進(jìn)行處理,并最終調(diào)用ff_filter_frame(),為下一輪循環(huán)做好準(zhǔn)備。
    • 在ff_filter_activate_default()中調(diào)用ff_filter_frame_to_filter()。首先調(diào)用ff_inlink_consume_frame()從pad link取出frame,然后調(diào)用ff_filter_frame_framed()。對(duì)于單輸入filter,調(diào)用它的filter_filter();對(duì)沒有activate()也沒有filter_frame()的filter,調(diào)用default_filter_frame()。(還不知道哪個(gè)filter是這樣的。)
    • 如前所說,單輸入filter處理后調(diào)用ff_filter_frame。
  • 注意ff_filter_frame_to_filter()。如果ff_filter_frame_framed()確實(shí)處理了frame,它會(huì)調(diào)用ff_filter_set_ready()。與ff_filter_frame()設(shè)置目的filter不同,它設(shè)置的是源filter。這樣做的目的,是再次推動(dòng)當(dāng)前filter,看看是不是有新的frame到達(dá)。這就是前面提到的中間狀態(tài):split已經(jīng)將Frame推入pad link,但它自己的ready值還是300。

2. 從InputStream得到Frame

  • Ffmpeg的轉(zhuǎn)換過程是在transcode()中完成的。
    • 它首先調(diào)用transcode_init(),完成一些初始化工作,如調(diào)用decoder的初始化函數(shù)。
    • 然后在一個(gè)循環(huán)里運(yùn)行transcode_step()。它涵蓋了真正的轉(zhuǎn)換過程。
  • FFMPEG的轉(zhuǎn)換過程是在transcode()中完成的。
    • choose_output()選擇一個(gè)frame時(shí)間最早的OutputStream。從output找到對(duì)應(yīng)的inputStream開始處理。
  • process_input()用于從InputStream讀取packet并處理。
    • get_input_packet()讀packet。然后調(diào)用process_input_packet()處理它。
    • decode_video()先調(diào)用decode()解碼得到AVFrame。avcodec_send_packet()和av_codec_receive_frame()的使用與ffplay一樣。
    • decode_video()再調(diào)用send_frame_to_filters()將AVFrame送入FiltreGraph。
    • 調(diào)用av_buffer_add_frame_flags()將frame推入buffer filter,開始frame在Filters中的流動(dòng)。
    • 注意處理第一個(gè)frame時(shí)還要做些初始化工作。 Ifilter_parameters_from_frame()用frame的參數(shù)初始化filter;更重要的是像前面提到的,如果FilterGraph還沒有初始化,要調(diào)用configure_filtergraph()進(jìn)行初始化。

3. 將Frame寫入OutputStream

  • Transcode_step()調(diào)用reap_filters()讀出AVFrame寫入OutputStream。
  • Av_buffersink_get_frame_flags()從buffer_sink filter讀出AVFrame。
  • do_video_out()寫AVFrame()。
    • av_codec_send_frame()和av_codec_receive_packet()調(diào)用encoder進(jìn)行編碼,得到packet。
    • out_packet(), write_packet() (ffmpeg.c)和av_interleaved_write_frame()寫入Packet。
    • write_packet() (mux.c)中,如果文件頭還沒有寫,則調(diào)用write_header_internal()寫文件頭,這里會(huì)調(diào)用ff_mp4_muxer的mov_write_header()函數(shù)。
      iv.調(diào)用ff_mp4_muxer()的mov_write_packet()函數(shù)寫packet。

相關(guān)鏈接

FFMPEG 3.4.2 - ffmpeg源代碼分析 (一)
FFMPEG 3.4.2 - ffmpeg源代碼分析 (二)
FFMPEG 3.4.2 - ffmpeg源代碼分析 (三)
FFMPEG 3.4.2 - ffmpeg源代碼分析 (四)- x264
FFMPEG 3.4.2 - ffplay源代碼分析 (一)
FFMPEG 3.4.2 - ffplay源代碼分析 (二)
FFMPEG 3.4.2 - ffplay源代碼分析 (三)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容