ffmpeg的內(nèi)部Video Buffer管理和傳送機(jī)制 本文主要介紹ffmpeg解碼器內(nèi)部管理Video Buffer的原理和過(guò)程,ffmpeg的Videobuffer為內(nèi)部管理,其流程大致為:注冊(cè)處理函數(shù)->幀級(jí)釋放->幀級(jí)申請(qǐng)->清空。 1 注冊(cè)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幀級(jí)的內(nèi)存申請(qǐng)和釋放調(diào)用
圖1幀級(jí)內(nèi)存申請(qǐng)和釋放的函數(shù)調(diào)用 2.1 FFAPI函數(shù)調(diào)用libavcodec相應(yīng)的codec(WMV3對(duì)應(yīng)的Codec是VC1)函數(shù)進(jìn)行解碼,過(guò)程中調(diào)用內(nèi)部buffer處理函數(shù)。其中buffer管理被統(tǒng)一封裝到Mpegvideo接口中(包括的codec有H.261, H.263, H.264, mpeg12, rv10,rv34, svq1和VC1) FFAPI_Decode() avcodec_decode_video2() avctx->codec->decode()//初始化過(guò)程中注冊(cè)codec,wmv3的解碼函數(shù)是 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()//通過(guò)調(diào)用get_buffer()申請(qǐng)當(dāng)前幀的video buffer。 MPV_frame_start() //首先調(diào)用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]); //調(diào)用s->avctx->get_buffer(),回調(diào)avcodec_default_release_buffer()
ff_alloc_picture() alloc_frame_buffer() s->avctx->get_buffer() //回調(diào)avcodec_default_get_buffer() 2.3MPV_frame_end() //完成視頻加邊等操作
3幀級(jí)的內(nèi)存申請(qǐng)和釋放處理方法 3.1內(nèi)部buffer數(shù)據(jù)結(jié)構(gòu) – 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; //記錄當(dāng)前內(nèi)部buffer的個(gè)數(shù),get_buffer和release_buffer時(shí)均需要對(duì)其進(jìn)行維護(hù)。 – void *internal_buffer;//初始化為數(shù)組InternalBuffer [INTERNAL_BUFFER_SIZE] – …… – } AVCodecContext; Codec通過(guò)維護(hù)internal_buffer_count和internal_buffer實(shí)現(xiàn)高效的內(nèi)存管理。 3.2參考幀管理相關(guān)數(shù)據(jù)結(jié)構(gòu) – typedef struct Picture{ – uint8_t *data[4]; – int linesize[4]; – uint8_t *base[4]; – int reference; – …… – } Picture; – typedef struct MpegEncContext{ – …… – Picture* picture; //初始化為數(shù)組Picture[INTERNAL_BUFFER_SIZE] – Picture* last_picture_ptr; //指向前一幀 – Picture* next_picture_ptr;; //雙向預(yù)測(cè)時(shí),指向后一幀 – Picture* current_picture_ptr;//指向當(dāng)前幀 – …… – } MpegEncContext; 3.3申請(qǐng)和釋放原理
圖2 內(nèi)存申請(qǐng)和釋放原理 (1)初始化時(shí)將internal_buffer全部清零 (2)釋放buffer時(shí),將釋放的buffer與最后一個(gè)有效buffer交換,而不是用av_free()釋放內(nèi)存。 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變成無(wú)效buffer,在下次get_buffer時(shí)能被申請(qǐng)到。 FFSWAP(InternalBuffer, *buf, *last); for(i=0; i<4; i++){ pic->data[i]=NULL; } } (3)申請(qǐng)buffer時(shí),檢查internal_buffer[internal_buffer_count]的基址是否非空,若非空則直接使用internal_buffer[internal_buffer_count];若空,使用av_malloc()函數(shù)進(jìn)行申請(qǐng)。 這樣處理的好處是避免了頻繁的調(diào)用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)決定輸出幀是在每幀解碼后,根據(jù)當(dāng)前幀的類(lèi)型和參考信息決定輸出幀。 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舉例——假設(shè)解碼IPBPB的非H.264碼流。 (1)初始化后的狀態(tài)如所示,IBC為ctx->internal_buffer_count,CurPtr為s->current_picture_ptr,LastPtr為s->last_picture_ptr,NextPtr為s->next_picture_ptr。 gpAVPicture指針為輸出圖像的指針。
圖3 初始化狀態(tài) (2)解碼第一個(gè)I幀,過(guò)程中不會(huì)不調(diào)用release_buffer(),get_buffer()得到picture[0] ,此時(shí)不輸出任何圖像。
圖4解碼第一個(gè)I幀后的狀態(tài) (3)解碼第一個(gè)P幀,過(guò)程中不調(diào)用release_buffer(),get_buffer()得到picture[1] ,輸出picture[0]。
圖5解碼第一個(gè)P幀后的狀態(tài) (4)解碼第一個(gè)B幀,過(guò)程中不調(diào)用release_buffer(),get_buffer()得到picture[2] ,輸出picture[2]。
圖6解碼第一個(gè)B幀后的狀態(tài) (5)解碼第二個(gè)P幀,調(diào)用release_buffer(&picture[2]),再調(diào)用get_buffer(),得到picture[2], 輸出picture[1]。
圖7解碼第二個(gè)P幀的狀態(tài) |
|
來(lái)自: shaobin0604@1... > 《Android》