• 沒有找到結果。

decoder_mbintra()

在文檔中 多核心管狀視訊解碼運算 (頁 36-45)

第三章 XVID CODEC

3.3 Decode for I-Frame (Intra Frame)

3.3.3 decoder_mbintra()

此程式為解出 intra block 的核心函數,圖27 為 decoder_mbintra() 的部分程式碼,

而圖28 為程式的流程圖,從流程圖可以看出,先前得到的 cbp 參數,是用來決定是否 需要進行Run-level 解碼的旗標。

27. decoder_mbintra() 片段程式碼

圖 28. decoder_mbintra() 流程圖

static void decoder_mbintra(dec, pMB, acpred_flag, cbp, bs,quant) { for (i = 0; i < 6; i++) {

uint32_t iDcScaler = get_dc_scaler(quant, i<4);

int16_t predictors[8]; // 係數預測值的暫存區 predict_acdc( .., predictors ,.. );

if (!acpred_flag) pMB->acpred_directions[i] = 0;

if (cbp & (1 << (5 - i)))

get_intra_block(bs,&data[i*64],direction, .. );

add_acdc( .., &data[i*64], iDcScaler, predictors, .. );

dequant_h263_intra(.,&data[i*64],quant,iDcScaler,.);

idct((short * const)&data[i*64]);

}

transfer_16to8copy(pY_Cur , &data[0*64] , stride);

transfer_16to8copy(pY_Cur + 8 , &data[1*64] , stride);

transfer_16to8copy(pY_Cur + next_block , &data[2*64] , stride);

transfer_16to8copy(pY_Cur + next_block + 8 , &data[3*64] , stride);

transfer_16to8copy(pU_Cur , &data[4*64] , stride2);

transfer_16to8copy(pV_Cur , &data[5*64] , stride2);

}

‹ 1.get_dc_scaler()

傳入值 quant 為量化參數 (Quantizer scale parameter : QP),而 i = 0 ~ 3 時,為 Luminance,i = 4 ~ 5 時,為 Chrominance,接著由表6 取得所對應的 DC 係數之量化參 數iDcScaler。

表 6. QP Ù dc_scaler 轉換表

Block type QP ≤ 4 5 ≤ QP ≤ 8 9 ≤ QP ≤ 24 25 ≤ QP Luma 8 2× QP QP + 8 (2× QP)-16 Chroma 8 (QP + 13)/2 (QP + 13)/2 QP-6

‹ 2. predict_acdc()

此函數用來計算DC 係數以及部分 AC 係數之預測值。首先,先針對左、上及左上 區塊的係數預測值做初始化,其默認值 (default values) 為 DC 係數 1024,其餘的 14 個 AC 係數設為 0,然後開始判斷左、上及左上的 block 此時是否位於邊界上,若位於邊界,

其相鄰block 的係數預測值則使用默認值,若不在邊界上,則使用相鄰 block 的係數預 測值,由於亮度 (Luma) 是由四個 block 所組成,所以選擇的相鄰 block 之係數預測值,

特別要經過一些指標上的偏移,以圖29 為例,當想要取得 Y2的相鄰block 之係數預測 值時,左邊block 的係數預測值位於 left MB 的 Y3位置,上面block 的係數預測值則為

29. 相鄰 block 的示意圖

current MB 的 Y0位置,而程式碼的實現如圖30 的 case2 所示,而 case 5 的程式碼,為 取得V 的相鄰 block 之係數預測值。若想取得 current V 的相鄰 block 之係數預測值時,

由於V 只有一個 block,且每一個 MB 的係數預測值是依照 Y0、Y1、Y2、Y3、U、V 的 Y0

Y2

Y1

Y3

diag top

left current D2

L2

MBPRED_SIZE = 15

30. predict_acdc() 片段程式碼

順序所排列,因此從程式碼可以看出,均是固定跳過五個block 的指標偏移來取得 V 的 相鄰block 之係數預測值。

取得相鄰的左、上及左上 block 的係數預測值之後,如圖31 左邊的 pseudo code,

經由選擇較小的DC 差,來決定預測方向,如果左邊和左上 block 的 DC 係數預測值相

DCB

if |DCA DCB| < |DCB - DCC| predict from block C else

predict from block A

31. DC、AC 係數的預測

減較小,預測方向為垂直方向 (dec->mbs[x + y * mb_width].acpred_directions[i] = 1,

其中i 代表 4y1u1v 的 block 旗標) ,此時 block X 將選擇 block C 為預測目標,表示

case 2: 若左邊是邊界,此時 left 是 NULL if(left){

pLeft = left + 3 * MBPRED_SIZE; // 如圖 28 的 L2

pDiag = left + MBPRED_SIZE; // 如圖 28 的 D2

}

pTop = current;

top_quant = current_quant;

break;

. . . case 5:

if(left) pLeft = left + 5 * MBPRED_SIZE; // 跳過五個 block if(top) pTop = top + 5 * MBPRED_SIZE;

if(diag) pDiag = diag + 5 * MBPRED_SIZE;

break;

block X 的 DC 以及 AC 的第一橫列之係數預測值將由 block C 取得,反之,當預測方向 為水平方向時 (dec->mbs[x + y * mb_width].acpred_direction[i] = 2),block X 的 DC 以及 AC 的第一直欄之係數預測值則是從 block A 取得,因此,此函數的目的,是取得八個 係數預測值 (一個 DC、七個 AC)。

‹ 3. get_intra_block()

經由查看 cbp 參數之後,決定是否要做 Run-level Decode (RLD),解碼的一開始,

會先決定存放 block 係數的掃描順序,選擇掃描方式的依據在於何種方式可以達到較佳 的壓縮效率,而掃描方式這邊分為三種,Z 字形 (Zig-Zag)、交錯式水平 (Alternate- Horizontal) 及交錯式垂直 (Alternate-Vertical) 掃描,一般都是選用 Z 字形掃描

(dec->mbs[x + y * mb_width].acpred_directions[i] = 0),因為 DCT 之後的係數大小會按 照頻率的高低來作排列,而且,越是高頻訊號,值為零的機率越高,因此,並可以利用 此特性配合Z 字形掃描使得出現零值的訊號連續出現,接著使用變動長度編碼

(Variable-Length Coding : VLC) 來達到最高的壓縮率,但是,如果在計算預測值的函數 中 (predict_acdc),預測方向為水平時 (dec->mbs[x + y * mb_width].acpred_directions[i]

= 1),此時的掃描方式就為交錯式垂直掃描,圖32 為不同掃描方式的示意圖。

32. 係數掃描方式

決定好掃描方式之後,便開始從位元流中取出 RLD 所需要的參數,係數的解碼利 用查表來實現,其相關的表為 DCT3D [ 2 ] [ 4096 ],且表所儲存的資料結構為

REVERSE_EVENT,其結構成員如表 7,從結構成員可以看出,有四個最主要的參數,

last 代表是否為最後一個不為零的係數,0 代表不是、1 代表是,run 的值代表該係數的

前面有幾個零,level 就是此係數真正的值,而 len 值代表該 VLC 的 Huffman Code 碼 長,len 的用途在於更新位元流的位置,藉由這四個參數就可以解出整個 block 的所有 係數值。DCT3D陣列前面的2 代表 intra 或 inter,後面的 4096 儲存了 0 ~ 212 - 1 的所有 值,因為最長的 VLC 長度為 12-bits,所以最長的 VLC 為最大限制,且每個 VLC 均外 加1-bit 儲存正負號。

7. EVENT 和 REVERSE_EVENT 的結構成員

資料結構名稱 type name 資料結構名稱 type name EVENT uint8_t last REVERSE_EVENT uint8_t len

uint8_t run EVENT event

int8_t level

和係數相關的 (run , level , last) 共有 102 種組合,這些組合儲存在 coeff_tab 陣列 中,圖33 為 coeff_tab 的部分陣列元素,因此,DCT3D 透過這 102 種組合對陣列作初

圖 33. coeff_tab 部分陣列元素

始化,初始化的程式碼如圖34 所示,下一段將詳細說明如何利用 102 種組合來建立 DCT3D 陣列的所有元素,而DCT3D為 RLD 解碼所需要的 VLC table。

VLC_TABLE const coeff_tab[2][102] = { // intra = 0

VLC EVENT typedef struct { code len last run level VLC vlc;

{ { { 2, 2}, { 0, 0, 1} }, EVENT event;

{ {15, 4}, { 0, 0, 2} }, } VLC_TABLE;

{ {21, 6}, { 0, 0, 3} }, . . },

// intra = 1 typedef struct { { { { 2, 2}, { 0, 0, 1} }, uint32_t code;

{ {15, 4}, { 0, 0, 3} }, uint8_t len;

{ {21, 6}, { 0, 0, 6} }, . . }, } VLC;

};

以圖33 圈起來的部分為例,此時 intra = 0,code = 2 (即 Huffman Code = 10),len = 2 (Huffman Code length) 時,已知最長的 Huffman Code 碼長為 12,但是目前碼長為 2,

因此剩下的 10-bits 便可任意存放 0 或 1,轉換成整數即為 0 ~ 210-1,因此,DCT3D [0]

[index] .len = 2 且 DCT3D [0] [index] .event = {0 , 0 , 1},其中 index = 10xxxxxxxxxx,

換成整數為 2048 + (0 ~ 210-1),程式實現部分如圖34。

34. 建立DCT3D 陣列的部分程式碼

DCT3D陣列建立好之後,解 RLD 的步驟,如同先前解 mcbpc (get_mcbpc_intra) 函 數提到的查表法,例如:當目前為 intra,且從位元流中取出的 12-bits 位元資訊為 010101-xxxxxx 時 (xxxxxx 中的 x 表示任意填入 0 或 1),將 12-bits 轉成陣列位置 index,

透過DCT3D 查表得到 DCT3D [1][index].len = 6,DCT3D [1][index].event={ 0, 0, 6 },其 中 index = 010101xxxxxx,接著,就可以透過這些參數來進行解碼。

‹ 4. add_acdc()

將先前計算出來的係數預測值 predictors [8],加上 RLD 之後的第一行或第一欄的 係數,並且將加完之後的DC 係數乘上 iDcScaler 參數,然後把 DC 係數以及 AC 係數 的第一行和第一欄 (總共 15 個值),儲存在 blocks 的 dec->mbs.pred_values[6][15] 結構 中,以提供給接下來未解碼的block,計算係數預測值之用。

‹ 5. dequant_h263_intra()

此函數為進行反量化 (Inverse Quantization : IQ) 運算的部份,至於反量化的原理是 將各個原始係數除上量化參數後,取最接近的整數,而此量化後的整數就是我們需要傳

for(intra = 0 ; intra < 2 ; intra++){

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

for(j = 0 ; j < (1<<(12- coeff_tab[intra][i].vlc.len)) ; j++){

DCT3D[intra][(coeff_tab[intra][i].vlc.code <<

(12-coeff_tab[intra][i].vlc.len))| j ].len

= coeff_tab[intra][i].vlc.len;

DCT3D[intra][(coeff_tab[intra][i].vlc.code <<

(12-coeff_tab[intra][i].vlc.len)) | j ].event

= coeff_tab[intra][i].event;

} } }

index = 10 - xxxxxxxxxx

送的資料,而量化的目的是希望透過量化這個步驟,在影像品質能夠接受的情況下,將 else if ( coefficient < -2048 ) coefficient = - 2048 where DCQ and ACQ are quantized DC and AC coefficient

+ +

特性,因此,這邊所採用的 DCT 則是利用此特性的 Chen’s Algorithm,此演算法是一 個非常有效率的快速演算法,圖 36 為 Chen’s Algorithm 的示意圖。

由於它的可分離之特性,此解碼步驟則拆成兩部份,porting 在兩顆不同的 SPE 上 運作。

‹ 7. transfer_16to8copy()

結束反 DCT 轉換之後,就結束整個完整的 intra block 係數值的解碼,因此,該 函數便將已解碼的係數值,更新到目前畫面 (Current Frame) 的暫存區中,但是,更新 之前,還須對所有係數值經過一些轉換處理。每個 block 的係數都會從 16-bit 轉換成 8-bit 大小,如果係數大於 255 就取 255,如果係數小於 0 就取 0,其他的值就保持 原值,轉換結束之後,便存放該係數到所對應的暫存區,而 Y、U、V 的暫存區分別位 於 dec->cur.y , dec->cur.u , dec->cur.v 中,等所有係數都轉換且更新結束之後,整個 I-Frame 的解碼就結束了,因此,該程式為 porting 至 SPE 的最後一個解碼部份。

在文檔中 多核心管狀視訊解碼運算 (頁 36-45)

相關文件