ffplay的源碼地址:http://ffmpeg.org/doxygen/trunk/ffplay_8c_source.html
ffplay中有兩個隊列一個PacketQueue, 一個FrameQueue,下面我們對隊列中AVPacket和AVFrame關系進行分析和說明。
一、AVPacket 和 AVFrame 結構含義
AVPacket
用於存儲壓縮的數據,分別包括有音頻壓縮數據,視頻壓縮數據和字幕壓縮數據。它通常在解復用操作后存儲壓縮數據,然后作為輸入傳給解碼器。或者由編碼器輸出然后傳遞給復用器。對於視頻壓縮數據,一個AVPacket通常包括一個視頻幀。對於音頻壓縮數據,可能包括幾個壓縮的音頻幀。
AVFrame
用於存儲解碼后的音頻或者視頻數據。AVFrame必須通過av_frame_alloc進行分配,通過av_frame_free釋放。
兩者之間的關系
av_read_frame得到壓縮的數據包AVPacket,一般有三種壓縮的數據包(視頻、音頻和字幕),都用AVPacket表示。
然后調用avcodec_send_packet 和 avcodec_receive_frame對AVPacket進行解碼得到AVFrame。
注:從 FFmpeg 3.x 開始,avcodec_decode_video2 就被廢棄了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame。
二、ffplay 隊列關系
ffplay中有三種PacketQueue,分別為視頻包隊列、音頻包隊列和字幕包隊列。
相應地也有三種FrameQueue,視頻幀隊列、音頻幀隊列和字幕幀隊列。
隊列的初始化工作是在stream_open函數中進行,分別通過packet_queue_init和frame_queue_init執行初始化操作。需要注意的是,初始化中PacketQueue沒有手動分配AVPacket結構,而是直接使用解碼過程中的AVPacket。FrameQueue中則是通過av_frame_alloc手動分配了AVFrame結構。
在read_thread函數中,通過av_read_frame函數讀取數據包,然后調用packet_queue_put將AVPacket添加到PacketQueue中。
在video_thread函數中,通過get_video_frame函數讀取數據幀,然后調用queue_picture將AVFrame添加到FrameQueue中。
那么兩個隊列是怎么聯系起來的呢?通過分析read_thread函數可以知曉:
首先,創建解復用和解碼所需要的數據結構。然后,分別通過stream_component_open函數打開三種數據流。最后,通過av_read_frame將解復用后的數據包分別添加到對應的PacketQueue中。在stream_component_open函數主要負責解碼工作,ffplay中為解碼工作專門設置了一個數據結構Decoder,Decoder結構中有一個成員queue,這個queue就是指的輸入的PacketQueue,通過decoder_init函數來指定PacketQueue。這個工作就是在stream_component_open中執行的。指定PacketQueue之后通過get_video_frame函數從PacketQueue中解碼出AVFrame結構,最后通過queue_picture函數將解碼得到的幀添加到FrameQueue。