視頻壓縮編碼標準 MPEG-4 介紹
參考書籍<<Visual C++ 視頻/音頻編解碼技術>>
1、 MPEG-4 編碼技術
用于甚低數碼率的音頻/視頻編碼標準,它由一個數據比特數據流格式和相應的一組協議組成。
用于表示由自然或合成的音頻、視頻及對象數據組成的多媒體內容。
和MPEG-1、MPEG-2相比,MPEG-4中系統的概念發生了極大的變化,過去系統僅指整體結構、多路復用及同步功能,
而MPEG-4除此之外,又引入了視聽對象及描述、場景描述、傳輸多媒體集成框架(Delivery Multimedia Integration Framework, DMIF)以及上載碼流等概念
來支持基于內容的交換型多媒體應用。同時MPEG-4支持將自然信息和合成信息進行組合,包括圖像與自然景物、合成聲音與自然聲音的組合。
1、1 MPEG-4 基本介紹
MPEG-4之前有四個運動圖像編碼標準:
MPEG-1 、MPEG-2、H.261、H.263
這四個標準就是把視頻序列按時間先后分為一系列的幀,每一幀圖像又分成16X16的宏塊來進行運行補償和編碼。
這種基于是幀、塊和像素的編碼稱為第一代視頻編碼方案,在編碼過程中不考慮圖像的具體結構和內容,因而會產生以下主要問題:
將圖像固定地分成相同大小塊,在高縮比的情況下會產生嚴重的塊效應;
不能對圖像的內容進行查詢、編輯和回放等;
沒有利用人類視頻系統的特性。
1、1、1 基本原理
MPEG-4是基于圖像的內容進行編碼的,根據內容將圖像分成不同的視頻對象VO(Video Object)。
例如在可視電話系統中,經常將講話的人作為前景視頻對象,將其余部分作為背景視頻對象。
前景視頻對象由于包含重要的邊界和輪廓信息,而且紋理又是理解圖像的重要附加信息,因而在編碼過程中應盡可能地保留這部分信息。
在編碼過程中前景對象與背景對象采用不同的編碼策略。前景對象的編碼盡可能保留視頻對象的細節和平滑。對人們不關心的背景可以采用大壓縮比的編碼策略,甚至不
予傳輸,而是在解碼端用其他的背景拼接成新的背景。
MPEG-4視頻標準還將合成技術用于編碼。在視頻非編碼標準中定義了人臉部的合成編碼。通過定義人臉的模型以及動畫參數,在編碼過程中只傳輸這些動畫參數,不僅能
提高編碼效率,還能用于虛擬電視會議系統的實現。在合成編碼還實現了網格變形編碼,對視頻對象生成二維網格或三維網格模型,在編碼的大部分時間中只編碼傳輸網格
頂點的位置變化,在解碼端利用已知的紋理信息和傳來的網格頂點位置變化來合成編碼的視頻對象。
1、1、2 體系結構
MPEG-4標準通過定義框架、級、算法、工具 4層結構來分層描述具體應用中的編碼方案。
(1)一個框架是一類應用的完整編碼碼流語法描述,它是整個MPEG-4碼流語法的一個子集。
即使在給定框架的語法規定范圍內,依然可能由于碼流中參數的不同而導致編碼器和解碼器的功能和編碼效率產生很大的差別。
(2)為了處理這個問題,在每個框架中又通過定義級來規定碼流中各個參數的類型和限制。
(3)算法是為了完成框架所要求的功能而組織起來的一些工具集合。
1、1、3 主要特點
(1) 基于對象的功能。
MPEG-4標準的系統部分用來描述組成一幅畫的各個視頻對象之間的空間和時間的關系。
(2)形狀編碼
視頻對象可以是任意形狀的物體,因而定義了平面(Alpha Plane) 來描述對象的形狀和位置信息。
平面主要有二進制和灰度兩種。
二進制只確定某一像素點是否屬于當前的對象,灰度平面可以描述對象的透明度,將不同的對象混合起來,完成某些特技效果,通常對平面的編碼叫做形狀編碼。
(3)基于對象的編輯和交互式操作。
允許隨機訪問每個視頻對象,具體地講是能以0.5S的時間間隔訪問對象,可以單獨解碼對象的形狀信息,而不解碼對象的紋理信息,也能對視頻對象進行剪貼、平移和旋轉。
(4) 多輸入流多視點的編碼和解碼。
能有效地對來自不同的視頻源圖像進行編碼,并能根據配置文檔,在解碼端把這些對象的碼流同步地解碼出來組成一幅圖。在多視點立體視頻編輯的應用中,
MPEG-4能有效地利用同一場景中不同視點圖你信息的冗余性提高編碼效率,對同一個場景僅能支持4個視點的編碼。
(5) 碼率方式。
可以支持各種不同的傳輸速率下的編碼,其中包括小于64Kbit/s的低速碼率、碼率為64-384Kbit/s的高碼率編碼。
(6)不同分辨率的編碼。
提供了靈活的編碼策略,這些策略包括對編碼圖像時間分辨率、空間分辨率和解碼圖像的信噪比的控制。
時間分辨率編碼策略是每隔幾幀選一幀作為基幀,基幀是必須編碼傳輸的,只有傳輸帶寬允許或解碼端要求才編碼中間幀,使運動圖像看起來更平滑;
空間分辨率編碼策略是在編碼端對圖像進行抽樣使圖像尺寸變小.
信噪比編碼策略是根據傳輸帶寬的限制和實際應用的需要,降低解碼圖像的信噪比來提高壓縮率。
(7)在噪聲環境中的魯棒性。
MPEG-4對每個對象的關鍵數據如對象的頭信息和形狀信息提供更高的容錯保護,并提供一個可逆變長編碼碼表,能檢驗傳輸中產生的誤碼。
(8) 臉部動畫。
(9)網格編碼
可以對需要編碼的二維或三維視頻對象生成各種網格模型,例如三維的高度柵格、臉和身體的三維多邊形網格,以及用二維Delaunay網格來描述物體。
MPEG-4能有效地進行基于網絡模型的視頻對象運動信息編碼,包括編碼網格的拓撲形狀以及每個結點的運動矢量。
(10)全局運動估計和Sprite編碼。
塊匹配運動補償技術對攝像頭運行所產生的整幅圖像的變化不是很有效。因此MPEG-4定義了二維和三維全局運動模型,包括平移、旋轉、映射和投影等來補償。
1、2 MPEG-4 編碼結構說明
MPEG-4是基于內容的方式表示視頻數據的,正是這個特點使得MPEG-4具有高效率、基于內容交互(操作、編輯、訪問等)和基于內容分級擴展(空域分級、時域分級)等
優點。MPEG-4中引入了VO(video object)的概念實現基于內容的表示。VO的結構依賴于具體應用和實現系統,在要求超低比特率的情況下,VO可以是一個矩形幀(即傳統
MPEG-1,8263中的矩形幀),從而與原來的標準兼容;
在VM中,VO被定義為畫面中分割出來的不同物體,每個VO有3類信息來描述:運動信息、形狀信息和紋理信息。
編碼的第一步是VO的形成,先要從原始視頻流中
分割出VOL,
由編碼控制機制為不同的VO以及各個VO的3類信息分配碼率,
以及各個VO分別獨立編碼,
最后將和個VO的碼流復合成一個位流。
1、2、1 MPEG-4 視頻編碼的關鍵概念
(1) 視頻對象。
VO是指視頻流中用戶可以獲取和操作的個體。它由時間上連續的幀面序列構成。
在MPEG-4中,視頻對象又分為自然視頻對象和合成視頻對象。
自然視頻對象是指從自然圖像和視頻序列中分割出來的某一視頻區域;
合成視頻對象是指表面通過若干頂點和曲面定義的,由計算機產生2D/3D對象。
(2)視頻對象平面。
視頻對象平面(VOP)是指在某一時刻某一幀畫面的視頻對象。VOP的引入直接導致了基于內容的分級編解碼技術。
VOP編碼針對視頻對象(VO)形狀、運動、紋理等可以進行獨立編碼、存儲和傳輸。
(3)Sprite圖像。
Sprite圖像是指在一段VideoChip中所有可見的,屬于某一視頻對象的像素所組成的圖像,主要是針對背景視頻對象的特點而提出的。
在許多應用場合,背景視頻對象自身沒有任何局部運動,為了有效地編碼視頻對象,可以將其在一段時間內的內容拼接成一幅完整的背景圖像,
這樣的圖像就叫做Sprite圖像。
MPEG-4定義了兩種Sprite:
一種是靜態Sprite。即在整個編碼過程中保持不變的Sprite.例如:視頻電話和視頻會議中的背景全景圖。
另一種是動態Sprite,即在編碼過程中不斷動態更新產生的Sprite。
實現 Sprite編碼,必須滿足:
A、前景對象與背景對象要能很好的分開;
B、無痕跡地從一段視頻或一些圖像中拼接出Sprite圖像。
(4) MPEG視頻流邏輯結構。
一個完整的視頻序列通常由幾個視頻段(Video Session,VS)構成,每個VS由一個或多個VO組成,每個VO又由一個或多個視頻對象層(Video Object Layer, VOL)構成,
每個VOL代表一個層次,即基本層或增強層,每個層表示某一種分辨率。在每個層中,都有時間上連續的一系列VOP。
1、2、2 VOP編碼
視頻編碼器包括形狀編碼(對于任意形狀)、運動信息編碼和紋理編碼。
基本編碼方法為:首先對輸入的原圖像序列進行場景分析和對象分割,以劃分不同的VOP,得到各個VOP的形狀和位置信息,它可以用
Alpha平面來表示。對Alpha平面進行壓縮編碼和傳送,在接收端就可以恢復Alpha平面。提取的形狀和位置信息又用來控制VOP的運動和紋理編碼。
對運動編碼和紋理編碼仍然采用經典的運動預測/補償法。
輸入第N幀的VOP與幀存儲器中存儲的N-1幀的VOP進行比較,找到運行矢量,然后對兩幀VOP的差值進行量化、編碼。編碼后得到的紋理信息,與運動編碼器和形狀編碼器
輸出的運動信息和形狀信息復合形成該VOP的比特流層。不同視頻對象的VOP序列分別進行編碼,形成各自的比特流層,經復合后在信道上傳送。
傳送的順序依次為形狀信息、運動信息和紋理信息。
下圖是MPEG-4 Video編碼的基本框圖。
(1)形狀編碼。
VOP形狀信息有兩類:二值形狀和灰度形狀信息。二值形狀信息用0、1表示VOP的形狀。
0表示非VOP區域,1表示VOP區域。
二值形狀信息的編碼采用基于運動補償塊的技術,可以是無損或有損編碼。
灰度形狀信息用0-255之間的值來表示VOP的透明程序,其中0表示完全透明(相當于二值信息中的0);
255表示完全不透明(相當于二信信息中的1);灰度信息是二值信息的擴展,它可以用來表示透視的物體,并降低混迭現象。
灰度信息的編碼采用基于塊的運動補償DCT方法,屬于有損編碼。
MPEG-4中采用位圖法來表示這兩類形狀信息。VOP被限定在一個矩形窗口內,稱之為VOP窗口,窗口的長、寬均為16的整數倍,
同時保證VOP窗口中非VOP的宏數目最小。
位圖法實際上是一個邊框矩陣的編碼,矩陣被分成16X16的“形狀塊”,允許進行有損編碼,這通過對邊界信息進行子采樣實現,
同時允許使用宏塊的運動向量來做形狀的運行補償。
實驗證明,位圖表示法具有較高的編碼效率和較低的運算復雜度。
(2) 運動信息編碼
MPEG-4采用運動預測和運動補償技術來去除圖像信息中的時間冗余成分,而這些運動信息的編碼技術可視為現有標準向任意形狀的VOP的延伸。
VOP的編碼有3種模式,即幀內編碼模式(I-VOP)、幀間預測編碼模式(P-VOP)、幀間雙向預測編碼模式(B-VOP)。
VOP如形狀編碼一樣,外加了邊框,邊框分成16X16的宏塊,宏塊內是8X8的塊。
在MPEG-4中運動預測和運動補償可以是基于16X16像素宏塊的,也可以是基于8X8像素塊的。
為了適應任意形狀VOP,MPEG-4引入了圖像填充技術和多邊形匹配技術。圖像填充技術利用VOP內部的像素值來外推VOP外的像素值,以此獲得運行預測的參考值。
多邊形匹配技術則將VOP的輪廓宏塊的活躍部分包含在多邊形之內,以此來增加運動估值的有效性。
(3) 紋理編碼
紋理信息有兩種:內部編碼的I-YOP的像素值; 幀間編碼的P-VOP、B-VOP的運動估計殘差值。
仍然采用基于分塊的紋理編碼。VOP邊框仍被分成16X16的宏塊。
紋理編碼方式分三種情況:
一是VOP外、邊框內的塊,不編碼;
二是VOP內的塊,采用經典的DCT方法;
三是部分在VOP內,部分在VOP外的塊采用圖像填充技術來獲取VOP外的像素值,之后進行DCT編碼。
1、2、3 VOP分割
(1) 基于紋理的分割。
主要是應用模式識別的技術來聚類,但要注意分割結果適度。分割太粗,不能有效地壓縮;分得太細,就有可能是物體的各部分,這樣對于壓縮和基于內容的操作不利。
(2) 基于運動的分割。
將具有同一運動參數模型的區域聚類,從而達到分割的目的。這種方法可以分割出運動的物體,但會使得基于運動一致必的分割實現起來非常,效果也不太理想。
(3) 紋理和運動結合的分割。
在運行一致性表現得非常明顯的區域用運動分割,在一些細節或運動復雜區域仍采用紋理分割。
1、2、4 可分級編碼(Scalable Coding)
MPEG-4提供了基于對象的分級功能,并同時兼顧MPEG-2中的圖像分級功能。視頻分級編碼的目的在于:
(1)以不同的帶寬及不同的顯示能力進行視頻數據庫的瀏覽,以及在多媒體環境下視頻內容的多分辨率播放;
(2)提供分層的視頻流以適應按優先順序進行視頻傳播的要求。
提供了兩種分級方法:空域分級(Spatial Scalability)和時域分級(Temporal Scalability).通過視頻對象層(VOL)的數據結構實現了基于內容的分級編碼。
每一種分級編碼都至少有兩層VOL,低層稱為基本層,高層稱為增強層。
1、2、5 Sprite對象編碼
許多圖像序列中的背景本身實際上是靜止的,由于攝像機的運動才造成了它們的改變。可以通過圖像的鑲嵌技術將整個序列的背景圖像拼接成一個大的完整的背景,
這就是Sprite圖像。
Sprite也可以指圖像序列中保持不變的比較小的VOL,例如電視臺的臺標,由于Sprite圖像本身是不變的,所以只需傳輸一次,然后根據攝像機的運動參數在接收端
重建背景,這樣大大減少傳輸數據量。
1、3 MPEG-4編碼源碼詳細分析
1、3、1 入口函數
(1) encore() 函數。
這個函數通過param1輸入待壓縮的視頻幀,param1的實際類型是ENC_PARAM
然后把壓縮過的視頻幀放到param2中返回,
enc_opt表示調用方式,有3種調用方式:ENC_OPT_WRITE, ENC_OPT_INT 和 ENC_OPT_RELEASE,
分別表示正常壓縮、壓縮初始化和壓縮完成。
typedef struct _ENC_PARAM_ { int x_dim; // 待壓縮幀的x大小 int y_dim; // 待壓縮幀的y大小 float framerate;// 待壓縮幀序列本來的幀頻 long bitrate; // 壓縮后的目標幀率 long rc_period; // the intended rate control averaging period long rc_reaction_period; // 幀率控制的反向時間 long rc_reaction_ratio; // 低/高端幀頻控制的比例 long max_key_interval; // 關鍵幀之間的最大間隔 int max_quantizer; // 量化器的上限值 int min_quantizer; // 量化器的下限值 int search_range; // 運動估計的向前搜索范圍 } ENC_PARAM;
typedef struct _REFERENCE { unsigned long handle; float framerate; long bitrate; long rc_period; long rc_reaction_period; long rc_reaction_ratio; long max_key_interval; int x_dim, y_dim; int prev_rounding; int search_range; int max_quantizer; int min_quantizer; long seq; long curr_run; /* 最后關鍵幀前的當前運行 */ Vop *current; /* 要編碼的當前幀 */ Vop *reference; /* 參考幀 - 重構之前的幀 */ Vop *reconstruct; /* 中間重構的幀 - used in inter */ Vop *error; /* 中間的錯誤幀 - 用整型來保存預測錯誤 */ struct _REFERENCE *pnext; } REFERENCE;
要對視頻進行MPEG-4壓縮,首先要以ENC_OPT_INT方式調用encore()函數,
然后對待壓縮的每一個視頻幀都按順序以ENC_OPT_WRITE方式調用一次encore()函數,
最后以ENC_OPT_RELEASE方式調用一次encore()就可以了。
int encore(unsigned long handle, unsigned long enc_opt, void *param1, void *param2) { static REFERENCE *ref = NULL; static VolConfig *vol_config; // 下面的鏈表保存之前壓縮過的參考 REFERENCE *ref_curr, *ref_last = NULL; int x_dim, y_dim, size, length; int headerbits = 0; Vop *curr; ref_curr = ref_last = ref; while (ref_curr != NULL) { if (ref_curr->handle == handle) break; ref_last = ref_curr; ref_curr = ref_last->pnext; } // 如果沒有找到匹配的參考,則創建一個新的句柄 if (ref_curr == NULL) { if (enc_opt & ENC_OPT_RELEASE) return ENC_OK; ref_curr = (REFERENCE *)malloc(sizeof(REFERENCE)); ref_curr->handle = handle; ref_curr->seq = 0; ref_curr->curr_run = 0; ref_curr->pnext = NULL; if (ref) ref_last->pnext = ref_curr; else ref = ref_curr; } // 按要求初始化一個句柄 if (enc_opt & ENC_OPT_INIT) { #ifdef _RC_ ftrace = fopen("trace.txt", "w"); fflush(ftrace); #endif init_fdct_enc(); init_idct_enc(); // 初始化率控制器 ref_curr->framerate = ((ENC_PARAM *)param1)->framerate; ref_curr->bitrate = ((ENC_PARAM *)param1)->bitrate; ref_curr->rc_period = ((ENC_PARAM *)param1)->rc_period; ref_curr->rc_reaction_period = ((ENC_PARAM *)param1)->rc_reaction_period; ref_curr->rc_reaction_ratio = ((ENC_PARAM *)param1)->rc_reaction_ratio; ref_curr->x_dim = ((ENC_PARAM *)param1)->x_dim; ref_curr->y_dim = ((ENC_PARAM *)param1)->y_dim; ref_curr->max_key_interval = ((ENC_PARAM *)param1)->max_key_interval; ref_curr->search_range = ((ENC_PARAM *)param1)->search_range; ref_curr->max_quantizer = ((ENC_PARAM *)param1)->max_quantizer; ref_curr->min_quantizer = ((ENC_PARAM *)param1)->min_quantizer; ref_curr->current = AllocVop(ref_curr->x_dim, ref_curr->y_dim); ref_curr->reference = AllocVop(ref_curr->x_dim + 2 * 16, ref_curr->y_dim + 2 * 16); ref_curr->reconstruct = AllocVop(ref_curr->x_dim, ref_curr->y_dim); ref_curr->error = AllocVop(ref_curr->x_dim, ref_curr->y_dim); init_vop(ref_curr->current); init_vop(ref_curr->reference); init_vop(ref_curr->reconstruct); init_vop(ref_curr->error); ref_curr->reference->hor_spat_ref = -16; ref_curr->reference->ver_spat_ref = -16; SetConstantImage(ref_curr->reference->y_chan, 0); vol_config = (VolConfig *)malloc(sizeof(VolConfig)); init_vol_config(vol_config); vol_config->frame_rate = ref_curr->framerate; vol_config->bit_rate = ref_curr->bitrate; RateCtlInit(8 /* initial quant*/, vol_config->bit_rate / vol_config->frame_rate, ref_curr->rc_period, ref_curr->rc_reaction_period, ref_curr->rc_reaction_ratio); return ENC_OK; } // 釋放和句柄有關的參考 if (enc_opt & ENC_OPT_RELEASE) { if (ref_curr == ref) ref = NULL; else ref_last->pnext = ref_curr->pnext; if (ref_curr->current) FreeVop(ref_curr->current); if (ref_curr->reference) FreeVop(ref_curr->reference); if (ref_curr->reconstruct) FreeVop(ref_curr->reconstruct); if (ref_curr->error) FreeVop(ref_curr->error); free(ref_curr); free(vol_config); if (ftrace) { fclose(ftrace); ftrace = NULL; }; return ENC_OK; } // 對相關參數進行初始化 (need to be cleaned later) max_quantizer = ref_curr->max_quantizer; min_quantizer = ref_curr->min_quantizer; x_dim = ref_curr->x_dim; y_dim = ref_curr->y_dim; size = x_dim * y_dim; curr = ref_curr->current; curr->width = x_dim; curr->height = y_dim; curr->sr_for = ref_curr->search_range; curr->fcode_for = get_fcode(curr->sr_for); // 下面對輸入的參數進行變換 // 下面這個函數使用圖像數據由short int表示變成了由unsigned char 表示 // 因為這部分MPEG-4編程代碼之前的MoMuSys代碼是用short int存放數據的 YUV2YUV(x_dim, y_dim, ((ENC_FRAME *)param1)->image, curr->y_chan->f, curr->u_chan->f, curr->v_chan->f); // 調整當前圖像的取整方式 curr->rounding_type = 1 - ref_curr->prev_rounding; Bitstream_Init((void *)(((ENC_FRAME *)param1)->bitstream)); if (ref_curr->seq == 0) { headerbits = PutVoVolHeader(x_dim, y_dim, curr->time_increment_resolution, ref_curr->framerate); } #ifdef _RC_ fflush(ftrace); fprintf(ftrace, "\nCoding frame #%d\n", ref_curr->seq); #endif if (ref_curr->curr_run % ref_curr->max_key_interval == 0) { curr->prediction_type = I_VOP; #ifdef _RC_ fprintf(ftrace, "This frame is forced to be coded in INTRA.\n"); fprintf(ftrace, "It has been %d frame since the last INTRA.\n", ref_curr->curr_run); #endif } else curr->prediction_type = P_VOP; // 視頻對象的編碼函數 Code the image data (YUV) of the current image VopCode(curr, ref_curr->reference, ref_curr->reconstruct, ref_curr->error, 1, //enable_8x8_mv, (float)ref_curr->seq/ref_curr->framerate, // time vol_config); length = Bitstream_Close(); ((ENC_FRAME *)param1)->length = length; // 更新率控制參數 RateCtlUpdate(length * 8); ref_curr->prev_rounding = curr->rounding_type; ref_curr->seq ++; ref_curr->curr_run ++; if (curr->prediction_type == I_VOP) { ((ENC_RESULT *)param2)->isKeyFrame = 1; ref_curr->curr_run = 1; } else ((ENC_RESULT *)param2)->isKeyFrame = 0; return ENC_OK; }
(2) 視頻對象編碼函數VopCode().
上面encore()函數進行的編碼操作是由VopCode()函數完成的,函數對輸入的視頻對象平面進行形狀、紋理和運動信息編碼。
輸入Vop必須是有封閉界面的。函數包含對一個幀進行編碼的基本流程。
struct vop { /* Actual syntax elements for VOP (standard) */ Int prediction_type; /* VOP 預測類型 */ Int mod_time_base; /* VOP modulo time base (absolute) */ Float time_inc; /* VOP 時間增量 (相對于上一mtb) */ Int rounding_type; Int width; /* VOP 高 (smallest rectangle) */ Int height; /* VOP 寬 (smallest rectangle) */ Int hor_spat_ref; /* VOP 水平參考. (for composition) */ Int ver_spat_ref; /* VOP 豎直參考. (for composition) */ Int intra_dc_vlc_thr; Int quantizer; /* P-VOPs的VOP 量化器 */ Int intra_quantizer; /* I-VOPs的VOP 量化器 */ /* 從VOL拷貝的 Syntax 元素 */ Int time_increment_resolution; Int intra_acdc_pred_disable; /* VOL disable INTRA DC prediction */ Int sr_for; /* VOP 運動向向量的搜索范圍 */ Int fcode_for; /* VOP 運動向量的動態范圍 */ Int quant_precision; Int bits_per_pixel; /* 指向圖像 (YUVA) 和相關系 VOPs */ Image *y_chan; /* VOP texture 中的Y組件*/ Image *u_chan; /* VOP texture 中的U組件*/ Image *v_chan; /* VOP texture 中的V組件*/ }; typedef struct vop Vop;
Void VopCode(Vop *curr, // 編碼的視頻對象 Vop *reference, // 參考視頻對象平面 Vop *reconstruct, // 前的構造VOP Vop *error, Int enable_8x8_mv, // 8X8運行向量標志 Float time, // 幀間間隔時間 VolConfig *vol_config) { ImageF *mot_x=NULL, *mot_y=NULL; Image *MB_decisions=NULL; Int edge,f_code_for=1; Vop *error_vop=NULL; Int vop_quantizer; Float mad_P = 0., mad_I = 0.; Float IntraMBRatio = 1.; Int numberMB, i, IntraMB; edge = 0; f_code_for = curr->fcode_for; if (curr->prediction_type == P_VOP) { /* 進行運動估計和補償*/ MotionEstimationCompensation(curr, reference, enable_8x8_mv, edge ,f_code_for, reconstruct, &mad_P, &mot_x,&mot_y,&MB_decisions); /* 計算各宏塊在視頻對象平面內部的百分比 */ IntraMB = 0; numberMB = MB_decisions->x * MB_decisions->y; for (i = 0; i < numberMB; i ++) if (MB_decisions->f[i] == MBM_INTRA) IntraMB ++; IntraMBRatio = (float)IntraMB / (float)numberMB; #ifdef _RC_ fprintf(ftrace, "ME with MAD : %f\n", mad_P); fprintf(ftrace, "%4.2f of the MBs are I-MBs.\n", IntraMBRatio); #endif } else mad_P = SCENE_CHANGE_THREADHOLD * 2; if ((mad_P < SCENE_CHANGE_THREADHOLD / 3) || ((mad_P < SCENE_CHANGE_THREADHOLD) && (IntraMBRatio < MB_RATIO_THREADHOLD))) { // mad 滿足要求,繼續按P幀形成編碼 curr->prediction_type = P_VOP; error->prediction_type = P_VOP; #ifdef _RC_ fprintf(ftrace, "Coding mode : INTER\n"); #endif vop_quantizer = RateCtlGetQ(mad_P); curr->quantizer = vop_quantizer; error->quantizer = vop_quantizer; #ifdef _RC_DEBUG_ fprintf(stdout, "RC: >>>>> New quantizer= %d\n", vop_quantizer); #endif SubImage(curr->y_chan, reconstruct->y_chan, error->y_chan); SubImage(curr->u_chan, reconstruct->u_chan, error->u_chan); SubImage(curr->v_chan, reconstruct->v_chan, error->v_chan); BitstreamPutVopHeader(curr,time,vol_config); VopShapeMotText(error, reconstruct, MB_decisions, mot_x, mot_y, f_code_for, GetVopIntraACDCPredDisable(curr), reference, NULL/*mottext_bitstream*/); } else { // mad 太大. // 按I幀形式編碼 curr->prediction_type = I_VOP; curr->rounding_type = 1; #ifdef _RC_ fprintf(ftrace, "Coding mode : INTRA\n"); #endif // 因為上次計算mad是假定在內部編碼基礎上的,所以應該重新計算MAD值 if (mad_I == 0.) mad_I = (Float) compute_MAD(curr); vop_quantizer = RateCtlGetQ(mad_I); curr->intra_quantizer = vop_quantizer; curr->rounding_type = 1; BitstreamPutVopHeader(curr,time,vol_config); /* 內部編碼模式下對紋理進行編碼 */ VopCodeShapeTextIntraCom(curr, reference, NULL/*texture_bitstream*/ ); } if (MB_decisions) FreeImage(MB_decisions); if (mot_x) FreeImage(mot_x); if (mot_y) FreeImage(mot_y); ImageRepetitivePadding(reference->y_chan, 16); ImageRepetitivePadding(reference->u_chan, 8); ImageRepetitivePadding(reference->v_chan, 8); Bitstream_NextStartCode(); return; } /* CodeVop() */
(3) 運動信息編碼
這方面的代碼文件為mot_est_comp.h、 mot_est_comp.c、mot_est_mb.h和mot_est_mb.c, 包括運動估計和運行補償兩個部分。
(3、1)
運動估計和預測補償函數MotionEstimationCompensation。
功能:估計運動方向并進行運動補償運算。在高級模式估計運動向量,函數輸出為4個,包含運動向量橫縱坐標的圖像及其處理模式。
Void MotionEstimationCompensation ( Vop *curr_vop, /* <-- 當前視頻面對象 */ Vop *prev_rec_vop, /* <-- 重建的參考視頻對象平面(1/2 pixel)*/ Int enable_8x8_mv, /* <-- 8x8 運動向量 (=1) or only 16x16 運動向量 (=0)*/ Int edge, /* <-- 限制(==0)/非限制(==edge) mode*/ Int f_code, /* <-- 運動向量搜索范圍 1/2 pel: 1=32,2=64,...,7=2048*/ Vop *curr_comp_vop, /* <-> 進行運動補償的視頻對象平面*/ Float *mad, /* <-> ME/MC結果的容忍值*/ Image **mot_x, /* --> 運動向量水平坐標*/ Image **mot_y, /* --> 運動向量豎直坐標*/ Image **mode /* --> 每個個宏塊的模式*/ ) { Image *pr_rec_y; /* 參考圖像(重建后) */ Image *pi_y; /* Interp.ref.image Y */ Image *mode16; Image *mv16_w; Image *mv16_h; Image *mv8_w; Image *mv8_h; SInt *prev_ipol_y, /* 有邊界的參考原始圖像 */ *prev_orig_y; /* Ref. original image with edge (subimage) */ Int vop_width, vop_height; Int br_x; Int br_y; Int br_height; Int br_width; Int mv_h, mv_w; /*GETBBR*/ br_y=curr_vop->ver_spat_ref; br_x=curr_vop->hor_spat_ref; br_height=curr_vop->height; br_width=curr_vop->width; mv_h=br_height/MB_SIZE; mv_w=br_width/MB_SIZE; /* ** 假定pre_rec_vop和prev_vop初始值相等 */ vop_width=prev_rec_vop->width; vop_height=prev_rec_vop->height; /* ** 得到圖像并將它們插入 */ pr_rec_y = prev_rec_vop->y_chan; prev_orig_y = (SInt*)GetImageData(pr_rec_y); pi_y = AllocImage (2*vop_width, 2*vop_height, SHORT_TYPE); InterpolateImage(pr_rec_y, pi_y, GetVopRoundingType(curr_vop)); prev_ipol_y = (SInt*)GetImageData(pi_y); /* * 給所有運動向量和模式數據分配存儲空間 * */ mode16=AllocImage (mv_w,mv_h,SHORT_TYPE); SetConstantImage (mode16,(Float)MBM_INTRA); /* ** mv16 有2X2個重復的運動向量值,以便在CodeVopVotion和MotionEstimation * 之間共享運動向量的預測函數 */ mv16_w=AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE); mv16_h=AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE); mv8_w =AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE); mv8_h =AllocImage (mv_w*2,mv_h*2,FLOAT_TYPE); SetConstantImage (mv16_h,+0.0); SetConstantImage (mv16_w,+0.0); SetConstantImage (mv8_h,+0.0); SetConstantImage (mv8_w,+0.0); SetConstantImage (curr_comp_vop->u_chan, 0); SetConstantImage (curr_comp_vop->v_chan, 0); /* 對每一個宏塊計算它的運動向量和模式 */ MotionEstCompPicture( (SInt *)GetImageData(GetVopY(curr_vop)), //curr_vop, prev_orig_y, /* Y padd with edge */ prev_ipol_y, /* Y interpolated (from pi_y) */ (SInt*)GetImageData(prev_rec_vop->u_chan) + (vop_width/2) * (16/2) + (16/2), (SInt*)GetImageData(prev_rec_vop->v_chan) + (vop_width/2) * (16/2) + (16/2), prev_rec_vop->hor_spat_ref, prev_rec_vop->ver_spat_ref, vop_width,vop_height, enable_8x8_mv, edge, GetVopSearchRangeFor(curr_vop), f_code, GetVopRoundingType(curr_vop), br_x,br_y, /* pos. of the bounding rectangle */ br_width,br_height, /* dims. of the bounding rectangle */ (SInt*)GetImageData(curr_comp_vop->y_chan), (SInt*)GetImageData(curr_comp_vop->u_chan), (SInt*)GetImageData(curr_comp_vop->v_chan), mad, (Float*)GetImageData(mv16_w), (Float*)GetImageData(mv16_h), (Float*)GetImageData(mv8_w), (Float*)GetImageData(mv8_h), (SInt*) GetImageData(mode16) ); /* 將結果轉MOMUSYS格式 */ GetMotionImages(mv16_w, mv16_h, mv8_w, mv8_h, mode16, mot_x, mot_y, mode); /* 釋放動態申請的內存Deallocate dynamic memory */ FreeImage(mv16_w); FreeImage(mv16_h); FreeImage(mv8_w); FreeImage(mv8_h); FreeImage(mode16); FreeImage(pi_y); }
(3、2) 運動向量和預測誤差計算的函數MotionEstCompPicture().
這個函數在MotionEstimationCompensation()函數中調用到了。這個函數計算運動向量和預測器的誤差,并且完成運動補償計算。
運動向量一般為8X8和16X16大小,預測器誤差是針對整個視頻對象平面而言的,并且運動補償也是對整個視頻對象平面而言的。
Void MotionEstCompPicture ( SInt *curr, // 當前視頻對象平面 SInt *prev, // 用邊界填充的原始Y向量 SInt *prev_ipol_y,// SInt *prev_ipol_u,// 用邊界填充的原始U分量 SInt *prev_ipol_v,// 用邊界填充的原始V分量 Int prev_x, // 前一個視頻對象的水平位置位置的絕對值 Int prev_y, // 前一個視頻對象的豎直位置位置的絕對值 Int vop_width, // 前一個視頻對象的水平方向大小 Int vop_height, // 前一個視頻對象的豎直方向大小 Int enable_8x8_mv,// 如果運動向量為8x8,則為1; 如果運動向量為16X16,則為0 Int edge, // 圍繞參考視頻對象平面的邊界 Int sr_for, // 向前搜索的范圍 Int f_code, // 運動向量的搜索范圍 Int rounding_type,// 當前視頻對象環繞的類型 Int br_x, // 當前視頻對象水平空間位置的絕對值 Int br_y, // 當前視頻對象豎直空間位置的絕對值 Int br_width, // 當前視頻對象平面邊界的寬度 Int br_height, // 當前視頻對象平面邊界的高度 SInt *curr_comp_y,// 運動補償的當前Y值 SInt *curr_comp_u,// 運動補償的當前U值 SInt *curr_comp_v,// 運動補償的當前V值 Float *mad, // 當前ME/MC的誤差的容忍值 Float *mv16_w, // 16X16運動向量的水平預測值 Float *mv16_h, // 16X16運動微量的豎直預測值 Float *mv8_w, // 8X8運動向量的水平預測值 Float *mv8_h, // 8X8運動向量的豎直預測值 SInt *mode16 // 預測運動向量的模式 );
(3、3) 運動向量圖像編碼函數Bits_CountMB_Motion()。
它在VopCode中按P幀形成編碼時調用的VopShapeMotText函數中被調用。(在下面我們會講到VopShapeMotText函數)
功能:根據宏塊的編碼模式和Alpha平面圖像對運動圖像進行編碼。編碼后的數據接在位流后面。
返回值:Int mv_bits, 通過這個數據返回增加到位流的位數。
描述:
函數中不考慮圖像大小的合法性;
假設輸出圖像的空間已經被申請好了;
不對透明的宏塊進行編碼;
在不完全透明的宏塊里的8X8的透明塊的運動向量被轉成MV(0, 0),使它不會在重建塊和運動向量預測時被使用。
Int Bits_CountMB_Motion( Image *mot_h, /* <-- 運動向量的所有水平分量- per block */ Image *mot_v, /* <-- 運動向量的所有豎直分量 - per block */ Image *alpha, /* <-- alpha平面圖像 - per block */ Image *modes, /* <-- 宏塊的編碼模式 (SInt) - per MB*/ Int h, /* <-- 宏塊的水平坐標 */ Int v, /* <-- 宏塊的豎直坐標 */ Int f_code, /* <-- 運動向量是1/2或1/4 pel 單位 1=32,2=64,...,7=2048 */ /* <-- flag for quarter pel MC mode */ Int quarter_pel,/* 1/4 pel 單位MC模式標志 */ Image *bs, /* --> 輸出 (SInt) */ Int error_res_disable, Int after_marker, Int **slice_nb, Int arbitrary_shape ) { Int vdim, hdim; /* 宏塊的大小 */ Float *ph, *pv; /* 運動向量 */ SInt *pm; /* 模式 */ SInt *pa; /* Alpha */ Int mode; Int bits_mot = 0; /* From switch statement */ Int i, error_flag=0,mvx=0,mvy=0; Float pred_h, pred_v; Float diff_h, diff_v; Int bh, bv; Int local_f_code;/* MW QPEL 07-JUL-1998 */ Float subdim; /* MW QPEL 07-JUL-1998 */ vdim = (Int)modes->y; hdim = (Int)modes->x; ph=(Float*)GetImageData(mot_h); pv=(Float*)GetImageData(mot_v); pm=(SInt*)GetImageData(modes); pa=NULL;//(SInt*)GetImageData(alpha); /* MW QPEL 07-JUL-1998 >> */ /* 根據quarter_pel設置local_f_code和subdim的值 */ if (quarter_pel) { local_f_code = f_code+1; subdim = 4.0; } else { local_f_code = f_code; subdim = 2.0; } /* << MW QPEL 07-JUL-1998 */ switch (mode=MB_MODE(h,v)) { case MBM_INTRA: break; case MBM_INTER16: /* 進行預測 */ find_pmvs(mot_h,mot_v,modes,alpha,h,v,0,MBM_TRANSPARENT, /* MW QPEL 07-JUL-1998 */ quarter_pel,&error_flag,&mvx,&mvy,0); pred_h=((Float)mvx)/subdim; /* MW QPEL 07-JUL-1998 */ pred_v=((Float)mvy)/subdim; /* MW QPEL 07-JUL-1998 */ /* MW QPEL 07-JUL-1998 */ bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*(MBV_H(h,v) - pred_h)), bs); /* MW QPEL 07-JUL-1998 */ bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*(MBV_V(h,v) - pred_v)), bs); break; case MBM_INTER8: i=1; for (bv=0; bv<=1; bv++) for (bh=0; bh<=1; bh++) { find_pmvs(mot_h,mot_v,modes,alpha,h,v,i,MBM_TRANSPARENT, /* MW QPEL 07-JUL-1998 */ quarter_pel,&error_flag,&mvx,&mvy,0); pred_h=((Float)mvx)/subdim; /* MW QPEL 07-JUL-1998 */ pred_v=((Float)mvy)/subdim; /* MW QPEL 07-JUL-1998 */ i++; diff_h=BV_H(h,v,bh,bv)-pred_h; diff_v=BV_V(h,v,bh,bv)-pred_v; /* MW QPEL 07-JUL-1998 */ bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*diff_h), bs); /* MW QPEL 07-JUL-1998 */ bits_mot += WriteMVcomponent(local_f_code, (Int)(subdim*diff_v), bs); } break; } return bits_mot; }
Void /* MVP/Noel */ find_pmvs( Image *mot_x, /* x-motion vector field */ Image *mot_y, /* y-motion vector field */ Image *MB_decisions, /* MB modes */ Image *B_decisions, /* field with number of vectors per MB */ Int x, /* xpos of the MB in multiples of 16 (hor coord) */ Int y, /* ypos of the MB in multiples of 16 (ver coord) */ Int block, /* block 號 (0 if one vector per MB, 1..4 else) */ Int transparent_value, /* 透明度的值 (0=enc, 2=dec) */ Int quarter_pel, /* MW QPEL 06-JUL-1998 */ /* 預測量化器的標志 pel mc */ Int *error_flag, /* 看是否一個錯誤發生 */ Int *mvx, /* 水平預測運動向量 [ in half-pixels units ] */ Int *mvy, /* 豎直預測運動向量 [ in half-pixels units ] */ Int newgob ) { Float p1x,p2x,p3x; Float p1y,p2y,p3y; Int xin1, xin2, xin3; Int yin1, yin2, yin3; Int vec1, vec2, vec3; Int rule1, rule2, rule3; Int subdim; /* MW QPEL 06-JUL-1998 */ Float *motxdata = (Float *) GetImageData(mot_x); Float *motydata = (Float *) GetImageData(mot_y); Int xM = GetImageSizeX(mot_x); Int xB = xM; Int mb_mode, sum; /* MW QPEL 06-JUL-1998 >> */ if (quarter_pel) { subdim=4; } else { subdim=2; } /* << MW QPEL 06-JUL-1998 */ /* In a previous version, a MB vector (block = 0) was predicted the same way as block 1, which is the most likely interpretation of the VM. Therefore, if we have advanced pred. mode, and if all MBs around have only one 16x16 vector each, we chose the appropiate block as if these MBs have 4 vectors. This different prediction affects only 16x16 vectors of MBs with transparent blocks. In the current version, we choose for the 16x16 mode the first non-transparent block in the surrounding MBs */ switch (block) { case 0: vec1 = 1 ; yin1 = y ; xin1 = x-1; vec2 = 2 ; yin2 = y-1; xin2 = x; vec3 = 2 ; yin3 = y-1; xin3 = x+1; break; case 1: vec1 = 1 ; yin1 = y ; xin1 = x-1; vec2 = 2 ; yin2 = y-1; xin2 = x; vec3 = 2 ; yin3 = y-1; xin3 = x+1; break; case 2: vec1 = 0 ; yin1 = y ; xin1 = x; vec2 = 3 ; yin2 = y-1; xin2 = x; vec3 = 2 ; yin3 = y-1; xin3 = x+1; break; case 3: vec1 = 3 ; yin1 = y ; xin1 = x-1; vec2 = 0 ; yin2 = y ; xin2 = x; vec3 = 1 ; yin3 = y ; xin3 = x; break; case 4: vec1 = 2 ; yin1 = y ; xin1 = x; vec2 = 0 ; yin2 = y ; xin2 = x; vec3 = 1 ; yin3 = y ; xin3 = x; break; default: printf("Illegal block number in find_pmv (mot_decode.c)"); *error_flag = 1; *mvx=*mvy=0; return ; } if (block==0) { /* according to the motion encoding, we must choose a first non-transparent block in the surrounding MBs (16-mode) */ if (x>0 /*&& ValidCandidateMVP(x,y,xin1,yin1,vec1,xB,transparent_value, MB_decisions,dcsn_data)*/) rule1 = 0; else rule1 = 1; if ((y>0 && newgob==0) /*&& ValidCandidateMVP(x,y,xin2,yin2,vec2,xB,transparent_value, MB_decisions,dcsn_data)*/) rule2 = 0; else rule2 = 1; if ((x != xB/2 -1) && ((y>0 && newgob==0)) /*&& ValidCandidateMVP(x,y,xin3,yin3,vec3,xB,transparent_value, MB_decisions,dcsn_data)*/) rule3 = 0; else rule3 = 1; } else { /* check borders for single blocks (advanced mode) */ /* rule 1 */ /* left border */ if (((block == 1 || block == 3) && (x == 0)) /*|| /* left block/MB is transparent * (!ValidCandidateMVP(x,y,xin1,yin1,vec1,xB,transparent_value, MB_decisions,dcsn_data))*/) rule1 = 1; else rule1 = 0; /* rule 2 */ /* top border */ if (((block == 1 || block == 2) && (y == 0)) /*|| /* top block/MB is transparent * (!ValidCandidateMVP(x,y,xin2,yin2,vec2,xB,transparent_value, MB_decisions,dcsn_data))*/) rule2 = 1; else rule2 = 0; /* rule 3 */ if (((block == 1 || block == 2) && (x == xB/2 -1 || y == 0)) /*|| /* right & top border * /* right block/MB is transparent * (!ValidCandidateMVP(x,y,xin3,yin3,vec3,xB,transparent_value, MB_decisions,dcsn_data))*/) rule3 = 1; else rule3 = 0; } if (rule1 ) { p1x = p1y = 0; } else if (((mb_mode = ModeMB(MB_decisions,xin1,yin1)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11)) { /* MW QPEL 06-JUL-1998 */ sum = subdim*(BV(motxdata, xM, xin1, yin1, 0, 0) + BV(motxdata, xM, xin1, yin1, 1, 0)); p1x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; sum = subdim*(BV(motydata, xM, xin1, yin1, 0, 0) + BV(motydata, xM, xin1, yin1, 1, 0)); p1y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; } else { p1x = BV(motxdata, xM, xin1, yin1, vec1&0x1, vec1>>1 ); p1y = BV(motydata, xM, xin1, yin1, vec1&0x1, vec1>>1 ); } if (rule2) { p2x = p2y = 0 ; } else if (((mb_mode = ModeMB(MB_decisions,xin2,yin2)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11)) { /* MW QPEL 06-JUL-1998 */ sum = subdim*(BV(motxdata, xM, xin2, yin2, 0, 0) + BV(motxdata, xM, xin2, yin2, 1, 0)); p2x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; sum = subdim*(BV(motydata, xM, xin2, yin2, 0, 0) + BV(motydata, xM, xin2, yin2, 1, 0)); p2y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; } else { p2x = BV(motxdata, xM, xin2, yin2, vec2&0x1, vec2>>1 ); p2y = BV(motydata, xM, xin2, yin2, vec2&0x1, vec2>>1 ); } if (rule3 ) { p3x = p3y =0; } else if (((mb_mode = ModeMB(MB_decisions,xin3,yin3)) >= MBM_FIELD00) && (mb_mode <= MBM_FIELD11)) { /* MW QPEL 06-JUL-1998 */ sum = subdim*(BV(motxdata, xM, xin3, yin3, 0, 0) + BV(motxdata, xM, xin3, yin3, 1, 0)); p3x = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; sum = subdim*(BV(motydata, xM, xin3, yin3, 0, 0) + BV(motydata, xM, xin3, yin3, 1, 0)); p3y = (Float)((sum & 3) ? ((sum | 2) >> 1) : (sum >> 1))/(Float)subdim; } else { p3x = BV(motxdata, xM, xin3, yin3, vec3&0x1, vec3>>1 ); p3y = BV(motydata, xM, xin3, yin3, vec3&0x1, vec3>>1 ); } if (rule1 && rule2 && rule3 ) { /* all MBs are outside the VOP */ *mvx=*mvy=0; } else if (rule1+rule2+rule3 == 2) { /* two of three are zero */ /* MW QPEL 06-JUL-1998 */ *mvx=(Int) subdim*(p1x+p2x+p3x); /* MW QPEL 06-JUL-1998 */ *mvy=(Int) subdim*(p1y+p2y+p3y); /* MW QPEL 06-JUL-1998 */ } else { /* MW QPEL 06-JUL-1998 */ /* MW QPEL 06-JUL-1998 */ *mvx=(Int)(subdim*(p1x+p2x+p3x-MAX(p1x,MAX(p2x,p3x))-MIN(p1x,MIN(p2x,p3x)))); /* MW QPEL 06-JUL-1998 */ *mvy=(Int)(subdim*(p1y+p2y+p3y-MAX(p1y,MAX(p2y,p3y))-MIN(p1y,MIN(p2y,p3y)))); } #ifdef _DEBUG_PMVS_ fprintf(stdout,"find_pmvs (%2d,%2d, rule %1d%1d%1d) :\np1 %6.2f / %6.2f\np2 %6.2f / %6.2f\np3 %6.2f / %6.2f\n",x,y,rule1,rule2,rule3,p1x,p1y,p2x,p2y,p3x,p3y); #endif return; }
(4) 紋理編碼
這部分代碼所在的文件是 text_code.h、text_code.c.
(4、1)幀內紋理編碼函數VopCodeShapeTextIntraCom()(VOP內部紋理編碼)。本函數實現的是一個視頻對象平面的幀內紋理編碼,
它包括了組合形狀編碼模式和紋理編碼模式。
它在VopCode中按 I 幀形成編碼時調用的。
Void VopCodeShapeTextIntraCom( Vop *curr, // 當前要進行編碼的視頻對象 Vop *rec_curr, // 當前重建的視頻對象平面 Image *mottext_bitstream) { Int QP = GetVopIntraQuantizer(curr); Int Mode = MODE_INTRA; Int* qcoeff; Int i, j; Int CBP, COD; Int CBPY, CBPC; Int num_pixels = GetImageSizeX(GetVopY(curr)); Int num_lines = GetImageSizeY(GetVopY(curr)); Int vop_type; Int ***DC_store; // 為做 DC/AC 預測 存儲宏塊系數 Int MB_width = num_pixels / MB_SIZE; // 宏塊的寬 Int MB_height = num_lines / MB_SIZE; // 宏塊的高 Int m; Int ACpred_flag=-1; Int direction[6]; Int switched=0; Int DQUANT =0; Bits nbits, *bits; bits = &nbits; qcoeff = (Int *) malloc (sizeof (Int) * 384); #ifdef _RC_DEBUG_ fprintf(ftrace, "RC - VopCodeShapeTextIntraCom(): ---> CODING WITH: %d \n",QP); #endif for (i = 0; i < 6; i++) direction[i] = 0; /* 給3D矩陣分配空間,矩陣是用來跟蹤為DC或AC判斷預測服務的預測值 */ DC_store = (Int ***)calloc(MB_width*MB_height, sizeof(Int **)); for (i = 0; i < MB_width*MB_height; i++) { DC_store[i] = (Int **)calloc(6, sizeof(Int *)); for (j = 0; j < 6; j++) DC_store[i][j] = (Int *)calloc(15, sizeof(Int)); } Bits_Reset(bits); vop_type = PCT_INTRA; // 對宏塊循環 for (j = 0; j < num_lines/MB_SIZE; j++) { for (i = 0; i < num_pixels/MB_SIZE; i++) { DQUANT = 0; COD = 0; bits->no_intra++; // 對宏塊編碼 // curee rec_curr qcoeff是輸出參數 CodeMB(curr, rec_curr, NULL, i*MB_SIZE, j*MB_SIZE, num_pixels, QP+DQUANT, MODE_INTRA, qcoeff); m =0; DC_store[j*MB_width+i][0][m] = qcoeff[m]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_width+i][1][m] = qcoeff[m+64]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_width+i][2][m] = qcoeff[m+128]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_width+i][3][m] = qcoeff[m+192]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_width+i][4][m] = qcoeff[m+256]*cal_dc_scaler(QP+DQUANT,2); DC_store[j*MB_width+i][5][m] = qcoeff[m+320]*cal_dc_scaler(QP+DQUANT,2); for (m = 1; m < 8; m++) { DC_store[j*MB_width+i][0][m] = qcoeff[m]; DC_store[j*MB_width+i][1][m] = qcoeff[m+64]; DC_store[j*MB_width+i][2][m] = qcoeff[m+128]; DC_store[j*MB_width+i][3][m] = qcoeff[m+192]; DC_store[j*MB_width+i][4][m] = qcoeff[m+256]; DC_store[j*MB_width+i][5][m] = qcoeff[m+320]; } for (m = 0; m < 7; m++) { DC_store[j*MB_width+i][0][m+8] = qcoeff[(m+1)*8]; DC_store[j*MB_width+i][1][m+8] = qcoeff[(m+1)*8+64]; DC_store[j*MB_width+i][2][m+8] = qcoeff[(m+1)*8+128]; DC_store[j*MB_width+i][3][m+8] = qcoeff[(m+1)*8+192]; DC_store[j*MB_width+i][4][m+8] = qcoeff[(m+1)*8+256]; DC_store[j*MB_width+i][5][m+8] = qcoeff[(m+1)*8+320]; } CBP = FindCBP(qcoeff,Mode,64); /* 進行DC/AC預測,適當地量化系數的值*/ if (GetVopIntraACDCPredDisable(curr) == 0) { // 做 DC/AC 預測 ACpred_flag = doDCACpred(qcoeff, &CBP, 64, i, j, DC_store, QP+DQUANT, MB_width, direction,GetVopMidGrey(curr)); } else ACpred_flag = -1; switched = IntraDCSwitch_Decision(Mode, GetVopIntraDCVlcThr(curr), QP); if (switched) CBP = FindCBP(qcoeff,MODE_INTER,64); if (DQUANT) Mode=MODE_INTRA_Q;else Mode=MODE_INTRA; QP+=DQUANT; CBPY = CBP >> 2; CBPY = CBPY & 15; /* last 4 bits */ CBPC = CBP & 3; /* last 2 bits */ Bits_CountMB_combined (DQUANT, Mode, COD, ACpred_flag, CBP, vop_type, bits, mottext_bitstream,/*MB_transp_pattern*/NULL); /* 增加變量 intra_dcpred_diable */ MB_CodeCoeff(bits, qcoeff, Mode, CBP, 64, GetVopIntraACDCPredDisable(curr), NULL, mottext_bitstream, /*MB_transp_pattern*/NULL, direction, 1 /*通過GetVopErrorResDisable(curr)得到*/, 0 /*通過GetVopReverseVlc(curr)得到*/, switched, 0 /*表示當前視頻對象平面交替掃描控制變量是0 curr->alternate_scan*/); } } /* Free allocated memory for 3D matrix */ for (i = 0; i < MB_width*MB_height; i++) { for (j = 0; j < 6; j++) free(DC_store[i][j]); free(DC_store[i]); } free(DC_store); free ((Char*)qcoeff); }
主要調用了CodeMB對宏塊進行編碼
/***********************************************************CommentBegin****** * * -- CodeMB -- Code, 用substraction和增加的操作來編碼并重建宏塊 * * 輸入參數 : * Int x_pos 宏塊的x位置 * Int y_pos 宏塊的y位置 * UInt width Vop綁定的框的寬 (unpadded size) * Int QP 量化器參數 * Int Mode 宏塊編碼的模式 * * 輸出參數 : * Vop *curr 當前的Vop,未編碼 * Vop *rec_curr 當前Vop,已編碼和重建了 * Vop *comp 當前Vop,運動 compensated * Int *qcoeff 系數塊 (384 * sizeof(Int)) * ***********************************************************CommentEnd********/ Void CodeMB(Vop *curr, Vop *rec_curr, Vop *comp, Int x_pos, Int y_pos, UInt width, Int QP, Int Mode, Int *qcoeff) { Int k; Int fblock[6][8][8]; Int coeff[384]; Int *coeff_ind; Int *qcoeff_ind; Int* rcoeff_ind; Int x, y; SInt *current, *recon, *compensated = NULL; UInt xwidth; Int iblock[6][8][8]; Int rcoeff[6*64]; Int i, j; Int type; /* luma = 1, chroma = 2 */ // Int *qmat; SInt tmp[64]; Int s; int operation = curr->prediction_type; /* This variable is for combined operation. If it is an I_VOP, then MB in curr is reconstruct into rec_curr, and comp is not used at all (i.e., it can be set to NULL). If it is a P_VOP, then MB in curr is reconstructed, and the result added with MB_comp is written into rec_curr. - adamli 11/19/2000 */ Int max = GetVopBrightWhite(curr); /* This variable is the max value for the clipping of the reconstructed image. */ coeff_ind = coeff; qcoeff_ind = qcoeff; rcoeff_ind = rcoeff; for (k = 0; k < 6; k++) { switch (k) { case 0: x = x_pos; y = y_pos; xwidth = width; current = (SInt *) GetImageData (GetVopY (curr)); break; case 1: x = x_pos + 8; y = y_pos; xwidth = width; current = (SInt *) GetImageData (GetVopY (curr)); break; case 2: x = x_pos; y = y_pos + 8; xwidth = width; current = (SInt *) GetImageData (GetVopY (curr)); break; case 3: x = x_pos + 8; y = y_pos + 8; xwidth = width; current = (SInt *) GetImageData (GetVopY (curr)); break; case 4: x = x_pos / 2; y = y_pos / 2; xwidth = width / 2; current = (SInt *) GetImageData (GetVopU (curr)); break; case 5: x = x_pos / 2; y = y_pos / 2; xwidth = width / 2; current = (SInt *) GetImageData (GetVopV (curr)); break; default: break; } // 塊預測 BlockPredict (current, x, y, xwidth, fblock[k]); } for (k = 0; k < 6; k++) { s = 0; for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) tmp[s++] = (SInt) fblock[k][i][j]; #ifndef _MMX_ fdct_enc(tmp); #else fdct_mm32(tmp); #endif for (s = 0; s < 64; s++) coeff_ind[s] = (Int) tmp[s]; if (k < 4) type = 1; else type = 2; /* For this release, only H263 quantization is supported. - adamli */ BlockQuantH263(coeff_ind,QP,Mode,type,qcoeff_ind, GetVopBrightWhite(curr),1); BlockDequantH263(qcoeff_ind,QP,Mode,type,rcoeff_ind,1, 0, GetVopBitsPerPixel(curr)); for (s = 0; s < 64; s++) tmp[s] = (SInt) rcoeff_ind[s]; #ifndef _MMX_ idct_enc(tmp); #else Fast_IDCT(tmp); #endif s = 0; for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) iblock[k][i][j] = (Int)tmp[s++]; coeff_ind += 64; qcoeff_ind += 64; rcoeff_ind += 64; if (Mode == MODE_INTRA||Mode==MODE_INTRA_Q) for (i = 0; i < 8; i++) for (j = 0; j < 8; j ++) iblock[k][i][j] = MIN (GetVopBrightWhite(curr), MAX (0, iblock[k][i][j])); switch (k) { case 0: case 1: case 2: continue; case 3: recon = (SInt *) GetImageData (GetVopY (rec_curr)); if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopY (comp)); BlockRebuild (recon, compensated, operation, max, x_pos, y_pos, width, 16, iblock[0]); BlockRebuild (recon, compensated, operation, max, x_pos + 8, y_pos, width, 16, iblock[1]); BlockRebuild (recon, compensated, operation, max, x_pos, y_pos + 8, width, 16, iblock[2]); BlockRebuild (recon, compensated, operation, max, x_pos + 8, y_pos + 8, width, 16, iblock[3]); continue; case 4: recon = (SInt *) GetImageData (GetVopU (rec_curr)); if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopU (comp)); BlockRebuild (recon, compensated, operation, max, x_pos/2, y_pos/2, width/2, 8, iblock[4]); continue; case 5: recon = (SInt *) GetImageData (GetVopV (rec_curr)); if (operation == P_VOP) compensated = (SInt *) GetImageData (GetVopV (comp)); BlockRebuild (recon, compensated, operation, max, x_pos/2, y_pos/2, width/2, 8, iblock[5]); continue; } } return; }
(4、2)形狀紋理編碼函數VopShapeMotText()。該函數的功能是進行運動形狀編碼。
它在Vop_code函數中按P幀形成編碼時被調用的。
* 輸入參數 :
* Vop *curr : 當前要進行編碼的視頻對象平面
* Vop *rec_prev: 先前已重建的視頻對象平面
* Image *mot_x : 運動向量的x軸坐標值
* Image *mot_y : 運動向量的y軸坐標值
* Image *MB_decisions: 包含了每一個宏塊的編碼模式
* Int f_code_for: 運動向量的搜索范圍 1/2 pel: 1=32,2=64,...,7=2048
* Image* AB_SizeConversionDecisions:視頻對象平面轉為視頻宏塊過程中的塊大小
* Image* AB_first_MMR_values : 最大宏塊閥值
* Int intra_dcpred_disable : 不允許幀內DC判斷預測的控制變量
* VolConfig *vol_config : 設置信息
* Int rc_type : 數據傳輸率控制類型
*
* 輸出參數 :
* Vop *rec_curr : 當前重建的視頻對象平面
* Image *mottext_bitstream : 編碼后的比特流
* Bits *bits : 數據統計信
Void VopShapeMotText (Vop *curr, Vop *comp, Image *MB_decisions, Image *mot_x, Image *mot_y, Int f_code_for, Int intra_acdc_pred_disable, Vop *rec_curr, Image *mottext_bitstream ) { Int Mode=0; Int QP = GetVopQuantizer(curr); Int* qcoeff=NULL; Int i, j; Int CBP; Int COD; Int CBPY, CBPC; Int MB_in_width, MB_in_height, B_in_width, mbnum, boff; SInt p; SInt *ptr=NULL; Float *motx_ptr=NULL, *moty_ptr=NULL; Int num_pixels; Int num_lines; Int vop_type=PCT_INTER; Int ***DC_store=NULL; Int m, n; Int ACpred_flag=-1; Int direction[6]; Int switched=0; Int DQUANT=0; Bits nbits, *bits; bits = &nbits; qcoeff = (Int *) malloc (sizeof (Int) * 384); num_pixels = GetImageSizeX(GetVopY(curr)); num_lines = GetImageSizeY(GetVopY(curr)); MB_in_width = num_pixels / MB_SIZE; MB_in_height = num_lines / MB_SIZE; B_in_width = 2 * MB_in_width; for (i = 0; i < 6; i++) direction[i] = 0; #ifdef _RC_DEBUG_ printf("RC - VopShapeMotText(): ---> CODING WITH: %d \n",QP); #endif /* 給3D矩陣分配空間矩陣時用來跟蹤為DC或者AC判斷預測服務的預測值*/ DC_store = (Int ***)calloc(MB_in_width*MB_in_height, sizeof(Int **)); for (i = 0; i < MB_in_width*MB_in_height; i++) { DC_store[i] = (Int **)calloc(6, sizeof(Int *)); for (j = 0; j < 6; j++) DC_store[i][j] = (Int *)calloc(15, sizeof(Int)); } Bits_Reset (bits); vop_type = PCT_INTER; ptr = (SInt *) GetImageData(MB_decisions); motx_ptr = (Float *) GetImageData(mot_x); moty_ptr = (Float *) GetImageData(mot_y); for (j = 0; j < num_lines/MB_SIZE; j++) { for (i = 0; i < MB_in_width; i++) { switched=0; p = *ptr; // 獲得此宏塊的編碼模式 DQUANT = 0; /* 用默認的預測值對DC判斷進行填充 */ for (m = 0; m < 6; m++) { DC_store[j*MB_in_width+i][m][0] = GetVopMidGrey(curr)*8; for (n = 1; n < 15; n++) DC_store[j*MB_in_width+i][m][n] = 0; } switch (p) { case MBM_INTRA: Mode = (DQUANT == 0) ? MODE_INTRA : MODE_INTRA_Q; bits->no_intra++; break; case MBM_INTER16: Mode = (DQUANT == 0) ? MODE_INTER : MODE_INTER_Q; bits->no_inter++; break; case MBM_INTER8: Mode = MODE_INTER4V; bits->no_inter4v++; DQUANT = 0; /* 這里不能為8X8模式改變QP值*/ break; default: printf("invalid MB_decision value :%d\n", p); exit(0); } // 對宏塊進行編碼 CodeMB (curr, rec_curr, comp, i*MB_SIZE, j*MB_SIZE, num_pixels, QP + DQUANT, Mode, qcoeff); mbnum = j*MB_in_width + i; boff = (2 * (mbnum / MB_in_width) * B_in_width + 2 * (mbnum % MB_in_width)); CBP = FindCBP(qcoeff,Mode,64); if ((CBP == 0) && (p == 1) && (*(motx_ptr +boff) == 0.0) && (*(moty_ptr +boff) == 0.0)) { COD = 1; /* 跳過的宏塊 */ BitstreamPutBits(mottext_bitstream, (long) (COD), 1L); bits->COD ++; *ptr = SKIPP; Mode = MODE_INTER; } else { COD = 0; /* 已編碼的宏塊 */ if ((Mode == MODE_INTRA) || (Mode == MODE_INTRA_Q)) { /* 存儲以后預測所需要的量化系數值 */ m =0; DC_store[j*MB_in_width+i][0][m] = qcoeff[m]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_in_width+i][1][m] = qcoeff[m+64]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_in_width+i][2][m] = qcoeff[m+128]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_in_width+i][3][m] = qcoeff[m+192]*cal_dc_scaler(QP+DQUANT,1); DC_store[j*MB_in_width+i][4][m] = qcoeff[m+256]*cal_dc_scaler(QP+DQUANT,2); DC_store[j*MB_in_width+i][5][m] = qcoeff[m+320]*cal_dc_scaler(QP+DQUANT,2); for (m = 1; m < 8; m++) { DC_store[j*MB_in_width+i][0][m] = qcoeff[m]; DC_store[j*MB_in_width+i][1][m] = qcoeff[m+64]; DC_store[j*MB_in_width+i][2][m] = qcoeff[m+128]; DC_store[j*MB_in_width+i][3][m] = qcoeff[m+192]; DC_store[j*MB_in_width+i][4][m] = qcoeff[m+256]; DC_store[j*MB_in_width+i][5][m] = qcoeff[m+320]; } for (m = 0; m < 7; m++) { DC_store[j*MB_in_width+i][0][m+8] = qcoeff[(m+1)*8]; DC_store[j*MB_in_width+i][1][m+8] = qcoeff[(m+1)*8+64]; DC_store[j*MB_in_width+i][2][m+8] = qcoeff[(m+1)*8+128]; DC_store[j*MB_in_width+i][3][m+8] = qcoeff[(m+1)*8+192]; DC_store[j*MB_in_width+i][4][m+8] = qcoeff[(m+1)*8+256]; DC_store[j*MB_in_width+i][5][m+8] = qcoeff[(m+1)*8+320]; } if (intra_acdc_pred_disable == 0) ACpred_flag = doDCACpred(qcoeff, &CBP, 64, i, j, DC_store, QP+DQUANT, MB_in_width, direction,GetVopMidGrey(curr)); else ACpred_flag = -1; /* Not to go into bitstream */ } switched = IntraDCSwitch_Decision(Mode, GetVopIntraDCVlcThr(curr), QP); if (switched) CBP = FindCBP(qcoeff,MODE_INTER,64); CBPY = CBP >> 2; CBPY = CBPY & 15; /* 取最后 4 bits */ CBPC = CBP & 3; /* 取最后 2 bits */ Bits_CountMB_combined (DQUANT, Mode, COD, ACpred_flag, CBP, vop_type, bits, mottext_bitstream,/*MB_transp_pattern*/NULL); Bits_CountMB_Motion( mot_x, mot_y, NULL, MB_decisions, i, j, f_code_for, 0 /*quarter_pel*/, mottext_bitstream, 1 /*GetVopErrorResDisable(curr)*/, 0, (Int **)NULL, 0 /*GetVopShape(curr)*/); MB_CodeCoeff(bits, qcoeff, Mode, CBP, 64, intra_acdc_pred_disable, NULL, mottext_bitstream, /*MB_transp_pattern*/NULL, direction, 1/*GetVopErrorResDisable(curr)*/, 0/*GetVopReverseVlc(curr)*/, switched, 0 /*curr->alternate_scan*/); } ptr++; // 取下一宏塊的編碼模式 } /* for i loop */ } /* for j loop */ /* 釋放已分配的3D矩陣 */ for (i = 0; i < MB_in_width*MB_in_height; i++) { for (j = 0; j < 6; j++) free(DC_store[i][j]); free(DC_store[i]); } free(DC_store); free ((Char*)qcoeff); }
(4、3) DC/AC 系數預測函數MB_CodeCoeff()。 該函數的功能是進行DC/AC系數預測。
轉載請標明是引用于 http://blog.csdn.net/chenyujing1234