1. 程式人生 > >ffmpeg的內部Video Buffer管理和傳送機制

ffmpeg的內部Video Buffer管理和傳送機制

本文主要介紹ffmpeg解碼器內部管理Video Buffer的原理和過程,ffmpeg的Videobuffer為內部管理,其流程大致為:註冊處理函式->幀級釋放->幀級申請->清空。

1 註冊get_buffer()和release_buffer()

FFAPI_InitCodec()

avcodec_alloc_context()

avcodec_alloc_context2()

avcodec_get_context_default2(AVCodecContext *s,...){

......

s->get_buffer = avcodec_default_get_buffer;

s->release_buffer = avcodec_default_release_buffer;

......

}

2幀級的記憶體申請和釋放呼叫

圖1幀級記憶體申請和釋放的函式呼叫

2.1 FFAPI函式呼叫libavcodec相應的codec(WMV3對應的Codec是VC1)函式進行解碼,過程中呼叫內部buffer處理函式。其中buffer管理被統一封裝到Mpegvideo介面中(包括的codec有H.261, H.263, H.264, mpeg12, rv10,rv34, svq1和VC1)

FFAPI_Decode()

       avcodec_decode_video2()

              avctx->codec->decode()//初始化過程中註冊codec,wmv3的解碼函式是

              vc1_decode_frame(){

       decode_vc1_header;

       MPV_frame_start();                                     //2.2.2

       vc1_decode_blocks();

       MPV_frame_end();                                     //2.2.3

}

2.2 MPV_frame_start()//通過呼叫get_buffer()申請當前幀的video buffer。

MPV_frame_start()

       //首先呼叫release_buffer()釋放非參考幀的video buffer

       for(i=0; i<MAX_PICTURE_COUNT; i++)

if(s->picture[i].data[0] && !s->picture[i].reference)

free_frame_buffer(s, &s->picture[i]); //呼叫s->avctx->get_buffer(),回撥avcodec_default_release_buffer()

       ff_alloc_picture()

              alloc_frame_buffer()

                     s->avctx->get_buffer()      //回撥avcodec_default_get_buffer()

2.3MPV_frame_end()                                          //完成視訊加邊等操作

3幀級的記憶體申請和釋放處理方法

3.1內部buffer資料結構

–   typedef struct InternalBuffer{

–       int last_pic_num;              

–       uint8_t *base[4];             

–       uint8_t *data[4];             

–       int linesize[4];           

–       int width, height;            

–       enum PixelFormat pix_fmt;   

–   }InternalBuffer;

–   typedef struct AVCodecContext {

–          ……

–   int internal_buffer_count; //記錄當前內部buffer的個數,get_buffer和release_buffer時均需要對其進行維護。

–   void *internal_buffer;//初始化為陣列InternalBuffer [INTERNAL_BUFFER_SIZE]

–   ……

–   } AVCodecContext;

Codec通過維護internal_buffer_count和internal_buffer實現高效的記憶體管理。

3.2參考幀管理相關資料結構

–   typedef  struct Picture{

–       uint8_t *data[4];

–       int linesize[4];

–       uint8_t *base[4];

–       int reference;

–       ……

–   } Picture;

–   typedef  struct MpegEncContext{

–       ……

–       Picture* picture;   //初始化為陣列Picture[INTERNAL_BUFFER_SIZE]

–       Picture* last_picture_ptr;      //指向前一幀

–       Picture* next_picture_ptr;;    //雙向預測時,指向後一幀

–       Picture* current_picture_ptr;//指向當前幀

–   ……

–   } MpegEncContext;

3.3申請和釋放原理

圖2 記憶體申請和釋放原理

(1)初始化時將internal_buffer全部清零

(2)釋放buffer時,將釋放的buffer與最後一個有效buffer交換,而不是用av_free()釋放記憶體。

avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic){

s->internal_buffer_count--;

    last = &((InternalBuffer*)s->internal_buffer)[s->internal_buffer_count];

    //將last buffer和要釋放的buffer交換,使last buffer變成無效buffer,在下次get_buffer時能被申請到。

FFSWAP(InternalBuffer, *buf, *last);

    for(i=0; i<4; i++){

        pic->data[i]=NULL;

    }

}

(3)申請buffer時,檢查internal_buffer[internal_buffer_count]的基址是否非空,若非空則直接使用internal_buffer[internal_buffer_count];若空,使用av_malloc()函式進行申請。

這樣處理的好處是避免了頻繁的呼叫malloc()和free(),從而提升了效率。

avcodec_default_get_buffer(AVCodecContext *s, AVFrame *pic){

       ……

       buf= &((InternalBuffer*)s->internal_buffer)[s->internal_buffer_count];

       get_size_info(size[]);

       buf->base[0, 1, 2] = av_malloc(size[0, 1, 2]);

       buf->data[0, 1, 2] = buf->base[0, 1, 2] + padding_offset[0, 1, 2];

       ……

}

(4)決定輸出幀是在每幀解碼後,根據當前幀的型別和參考資訊決定輸出幀。

if (s->pict_type == FF_B_TYPE || s->low_delay) {

*pict= *(AVFrame*)s->current_picture_ptr;

} else if (s->last_picture_ptr != NULL) {

*pict= *(AVFrame*)s->last_picture_ptr;

}

3.4舉例——假設解碼IPBPB的非H.264碼流。

(1)初始化後的狀態如所示,IBC為ctx->internal_buffer_count,CurPtr為s->current_picture_ptr,LastPtr為s->last_picture_ptr,NextPtr為s->next_picture_ptr。

gpAVPicture指標為輸出影象的指標。

圖3 初始化狀態

(2)解碼第一個I幀,過程中不會不呼叫release_buffer(),get_buffer()得到picture[0] ,此時不輸出任何影象。

圖4解碼第一個I幀後的狀態

(3)解碼第一個P幀,過程中不呼叫release_buffer(),get_buffer()得到picture[1] ,輸出picture[0]。

圖5解碼第一個P幀後的狀態

(4)解碼第一個B幀,過程中不呼叫release_buffer(),get_buffer()得到picture[2] ,輸出picture[2]。

圖6解碼第一個B幀後的狀態

(5)解碼第二個P幀,呼叫release_buffer(&picture[2]),再呼叫get_buffer(),得到picture[2], 輸出picture[1]。

圖7解碼第二個P幀的狀態