天天看點

H.264難點問題分析

2011年4月23日22:22:12

H.264編碼後碼流的生成

H.264難點問題分析

H.264 比較全的編碼架構

H.264難點問題分析
H.264難點問題分析

2011年4月23日22:23:35

H.264中的PB幀編碼

H.264難點問題分析
H.264難點問題分析
H.264難點問題分析

在針對連續動态圖像編碼時,将連續若幹幅圖像分成P,B,I三種類型,P幀由在它前面的P幀或者I幀預測而來,它比較與它前面的P幀或者I幀之間的相同信 息或資料,也即考慮運動的特性進行幀間壓縮。P幀法是根據本幀與相鄰的前一幀(I幀或P幀)的不同點來壓縮本幀資料。采取P幀和I幀聯合壓縮的方法可達到 更高的壓縮且無明顯的壓縮痕迹。

在H.264編碼中,I幀是内部編碼幀,不需要參考其它幀,P幀需要前向的I幀作為參考,B是雙向預測幀,需要前 向和後向的I或者P幀作為其參考幀。由于幀與幀之間的參考關系比較複雜,彼此之間互相關聯,對幀編碼的簡單并行是行不通的。是以,尋找共用參考幀的可編碼 幀成為實作幀級并行的關鍵。

假設編碼序列中設定的B幀個數為2,其具體視訊編碼序列為IBBPBBPBBP…,依照I、P和B幀之間的參考關系, 可以把連續的視訊序列按照BBP的樣式分割成一個個單元序列。經過分析可以看出一個BBP單元序列中的兩個B幀由于共用前後的兩個P幀作為參考幀,可以實 施并行。同時,這個BBP單元序列中的P幀又作為下一個BBP單元序列中P幀的參考,是以前一單元序列中的兩個B幀加上下一個單元序列中的P幀就可以實施 三幀同時并行。以上是B幀設定參數為2時幀級并行的基本思路,可以得出原本執行一幀的時間現在可以用來執行三幀,理論加速比基本可以達到3,等于B幀設定 參數加1。

當設定編碼序列中B幀個數可變時,幀級并行的線程數取決于B幀的個數,B幀數越大,并行加速比越高,在處理器足夠的情況下,理論上獲得 的最大加速比等于B幀設定參數加1。由于在并行過程中,建立編碼線程需要配置設定記憶體,線程之間的資料傳輸也會消耗資源,實際加速比會小于理論加速比,再加上 處理器個數資源的限制,會在某個B幀的個數上獲得一個峰值加速比.

2011年4月23日22:26:49

H.264中運動矢量和多參考幀運動估計

一個宏塊中的運動矢量

H.264難點問題分析

每一個塊具有一個運動矢量,這句話我們可以這麼了解, 塊的大小不定, 從16x16~4X4, 是以,最多可以有16個不同的MV,但是

H.264難點問題分析

,是以對于一個宏塊肯定是攜帶16個MV, 隻是可能其中一些MV是相同的.

多參考幀運動估計

H.264難點問題分析

2011年4月23日22:34:25

關于H264幀間色度塊預測的問題

老畢書上在第6章這麼寫的:

H.264難點問題分析

是以幀間色度塊不用預測,

H.264難點問題分析

2011年4月23日22:36:14

JM86如何設定編碼器分片參數:

H.264難點問題分析

2011年4月23日22:43:52

關于SliceMode中的CALLBACK模式的解釋

H.264難點問題分析
H.264難點問題分析

2011年4月23日22:44:02

JM8.6中I幀,P幀的了解

H.264難點問題分析
H.264難點問題分析

通過下面的代碼我們可以發現

H.264難點問題分析

對于一幀, 如果設定這一幀進行幀内編碼,即intra=1,那麼這一幀不會再進行幀間預測,這一幀即為I幀, 但是, 如果設定某一幀要進行幀間預測, 即intra=0,那麼這個時候幀内預測和幀間預測都要進行, 從上面的代碼可以看出, 進行幀間預測是包含着一個if判斷語句中的, 而幀内預測是沒有條件的, 是任何情況下都要進行的. 之後jm代碼會比較幀内預測與幀間預測兩者之間的優劣, 選擇一種使率率失真函數最優的方法. 換句話說, 可以認為intra其實是一個标志(flag), 用來标示是否要進行幀間預測. 同樣我們也可以看到, 對于p幀中的宏塊, 幀内預測和幀間預測都要進行的, 最後也有可能使用幀内預測. 是以p幀内的宏塊包括幀内預測和幀間預測的宏塊.

對于上面需要一些更正

H.264難點問題分析
H.264難點問題分析
H.264難點問題分析

2011年4月20日15:16:43

JM8.6中對資料分割的一點解釋

分析currslice->partarr[partmap[SE_MVD]] (關于資料分割的實作)

Currslice指目前slice

Partarr是一個datapartition數組

Partmap :const int* partmap = assignse2partition[input->partition_mode];

Int * assignse2partition[2] ;

H.264難點問題分析

Static int assignse2partition_nodp[SE_MAX_ELEMENTS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

Static int assignse2partition_DP[SE_MAX_ELEMENTS] = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 2, 2, 2, 2, 0, 0, 0, 0 } ;

Typedef enum {

SE_HEADER,

SE_PTYPE,

SE_MBTYPE,

SE_REFFRAME,

SE_INTRAPREDMODE,

SE_MVD,

SE_CBP_INTRA,

SE_LUM_DC_INTRA,

SE_CHR_DC_INTRA,

SE_LUM_AC_INTRA,

SE_CHR_AC_INTRA,

SE_CBP_INTER,

SE_LUM_DC_INTER,

SE_CHR_DC_INTER,

SE_LUM_AC_INTER,

SE_CHR_AC_INTER,

SE_DELTA_QUANT_INTER,

SE_DELTA_QUANT_INTRA,

SE_BFRAME, SE_EOS,

SE_MAX_ELEMENTS //!< number of maximum syntax elements

} SE_type; // substituting the definitions in elements.h

從上面可以看出SE_type枚舉類型裡定義了20種句法元素,若存在資料分割,則根據assignse2partition_DP數組來定義句法元素的重要性; 若不存在資料分割情況,則assignse2partition_nodp數組可知把這些句法元素歸于一類。

Partmap的作用是把目前的句法元素映射到某一種資料分割類型中,其值為0,1,2,這樣就可以形成一個partarr的datapartition數組

當沒有資料分割時,這些元素實際全存到了currslice->partarr[0]中。

2011年4月20日15:18:50

關于寫比特流的問題

H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析

通過上面的對比, 我們可以發現store_coding_state函數和reset_coding_state函數基本上完全一緻, 對于cs_mb, store_coding_state函數将img->currentslice變量中的一些需要儲存的量存儲在cs_mb中, 然後等到進行編碼完成後, 要恢複現場, 利用reset_coding_state函數将cs_mb中儲存的相關量恢複到變量img->currentslice中, 便于下面的利用.

從上面的截圖我們也可以看出, 對于非CABAC編碼的狀況, 主要是儲存的bitstream

H.264難點問題分析

而對于CABAC編碼的狀況, 還需要儲存一些相關的上下文資訊.

是以, 我們需要看看bitstream結構中所包含的一些量

H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析
H.264難點問題分析

從上面的截圖, 我們可以看到cs_mb和cs_b8應該分别是一個宏塊和一個8x8塊對應的一些bitstream資訊, cs_cm更像一個中間量, 其他的量都沒有使用過. 一般cs_cm和cs_mb和cs_b8這些存儲目前編碼狀态的變量, 使用的主要原因就是在RDO方式下需要真正的進行一遍編碼, 是以需要儲存一下目前狀态, 等代價計算完畢後,需要恢複現場, 再計算其他模式的代價.

If (valid[P8x8])

{

Cost8x8 = 0;

//===== store coding state of macroblock =====

Store_coding_state (cs_mb); //第一次出現

Store_coding_state

假設現在是第二幀, 現在用來存儲比特流的結構體裡資料情況如下

Img->currentslice->partarr[0].bitstream->byte_pos 0x00000003

Img->currentslice->partarr[0].bitstream->bits_to_go 0x00000007

在這時候執行store_coding_state (cs_mb),把

Img->currentslice->partarr[0].bitstream的内容賦給了cs_mb->bitstream[0]

接着進入for (cbp8x8=cbp_blk8x8=cnt_nonz_8x8=0, block=0; block<4; block++)循環 1

進入for (min_cost8x8=(1<<20), min_rdcost=1e30, index=(bframe?0:1); index<5; index++)循環 2

在循環2裡

執行函數rdcost_for_8x8blocks之前,

執行store_coding_state (cs_cm) 把img->currentslice->partarr[0].bitstream的内容賦給了cs_cm->bitstream[0]

執行rdcost_for_8x8blocks函數之後 //這個函數包含了編碼與寫比特到img->currentslice->partarr[0].bitstream中

Img->currentslice->partarr[0].bitstream->byte_pos 0x000000012

Img->currentslice->partarr[0].bitstream->bits_to_go 0x00000008

H.264難點問題分析

若本模式的率失真更小, 則store_coding_state (cs_b8), 把img->currentslice->partarr[0].bitstream的内容賦給了cs_b8->bitstream[0]

然後reset_coding_state (cs_cm); 把cs_cm->bitstream[0]内容賦給img->currentslice->partarr[0].bitstream 即保證每次比較8*8塊的率失真時,将比特流情況設定成

Img->currentslice->partarr[0].bitstream->byte_pos 0x00000003

Img->currentslice->partarr[0].bitstream->bits_to_go 0x00000007

(可以這樣看, 在進行rdcost_for_8x8blocks之前, 要儲存一下目前的img->currentslice->partarr[0].bitstream到cs_cm中去, 因為在這個函數中有對img->currentslice->partarr[0].bitstream的修改, 是以從rdcost_for_8x8blocks函數傳回後, 由于需要計算下一個P8x8模式的rdcost, 是以要恢複成之前的狀态, 這樣保證了對每種P8x8模式進行計算rdcost之前的img->currentslice->partarr[0].bitstream狀态是一樣的. 同時, 如果發現目前模式的rdcost比較小, 則需要儲存一下bitstream, 儲存在了cs_b8中)

由此可見循環2完成後, 本8*8塊的最佳比特流将存在cs_b8->bitstream[0]中(其實還包括slice頭部比特流和其他的前面的最佳8*8塊比特流)

循環2完成後,執行reset_coding_state (cs_b8);

H.264難點問題分析

将cs_b8->bitstream[0]内容賦給img->currentslice->partarr[0].bitstream, 保證在把第下個8*8 的最佳比特流放在前一個8*8的最佳比特流之後

如此下來,1和2都循環完之後,一個宏塊的最佳編碼比特流就存到了img->currentslice->partarr[0].bitstream中

接下來在for (ctr16x16=0, index=0; index<7; index++)循環中

在這個循環之前,執行reset_coding_state (cs_mb); 把cs_mb->bitstream[0]的内容

賦給了img->currentslice->partarr[0].bitstream

此時

Img->currentslice->partarr[0].bitstream->byte_pos 0x00000003

Img->currentslice->partarr[0].bitstream->bits_to_go 0x00000007

即回到未寫宏塊比特流之前

(for (ctr16x16=0, index=0; index<7; index++)的循環裡比較的是0, 1, 2, 3, P8x8, I16MB, I4MB這幾種模式

而P8*8的最佳rdcost在for (min_cost8x8=(1<<20), min_rdcost=1e30, index=(bframe?0:1); index<5; index++)裡已經比較得到 是以for (ctr16x16=0, index=0; index<7; index++)它實際上是比較Skip,16*16,16*8,8*16,8*8,8*4,4*8,4*4,I4MB,I16MB這些模式的rdcost進而選擇出有最小rdcost的模式)

執行if (rdcost_for_macroblocks (lambda_mode, mode, &min_rdcost))

在rdcost_for_macroblocks (lambda_mode, mode, &min_rdcost)裡對Skip,16*16,16*8,8*16模式編碼(每次循環對不同的模式編碼)

編碼完成之後, 寫比特流之前store_coding_state (cs_cm); 把

Img->currentslice->partarr[0].bitstream的内容賦給了cs_cm->bitstream[0] (a)

之後writembheader (1); writemotioninfo2nal ();writecbpandlumacoeff ();writechromacoeff ();此時

Img->currentslice->partarr[0].bitstream->byte_pos 0x000000037

Img->currentslice->partarr[0].bitstream->bits_to_go 0x00000003

即編完一個宏塊模式後的比特流情況

然後reset_coding_state (cs_cm);

把cs_cm->bitstream[0]内容賦給img->currentslice->partarr[0].bitstream (b)

由上可看出,實際上(a)和(b)操作時相反的,最後img->currentslice->partarr[0].bitstream裡面實際上隻存有slice頭部資訊

所有store_coding_state和reset_coding_state操作都是為比較率失真而設定的,

在encode_one_macroblock 結束後,img->currentslice->partarr[0].bitstream裡實際上隻有slice頭部資訊,但在這個函數裡得到了最佳的宏塊編碼模式,

将宏塊編碼資訊寫入img->currentslice->partarr[0].bitstream的是在write_one_macroblock函數 裡

Writembheader (0);

// Do nothing more if copy and inter mode

If ((IS_INTERMV (currmb) || IS_INTRA (currmb) ) ||

((img->type==B_SLICE) && currmb->cbp != 0) )

{

Writemotioninfo2nal ();

Writecbpandlumacoeff ();

Writechromacoeff ();

}

實際上是用到的img-> mv[block_x][block_y][list_idx][refindex][mv_mode][X/Y]

在求每種分割模式的最佳運動矢量時,将他們儲存到了best8x8fwref[mode][k]中,k=0~3 即best8x8fwref[mode][k]儲存了每種模式的最佳參考幀

在對各種模式進行比較時

For (ctr16x16=0, index=0; index<7; index++)

If (valid[mode])

{

Setmodesandrefframeforblocks (mode);

Setmodesandrefframeforblocks中

Enc_picture->ref_idx[LIST_0][img->block_x+i][img->block_y+j] = (IS_FW ? Best8x8fwref[mode][k] : -1);

将本模式的最佳參考幀指派給enc_picture->ref_idx

在rdcost_for_macroblocks的lumaresidualcoding ()中,将會用到enc_picture->ref_idx求出預測值 [具體來說是利用enc_picture->ref_idx給出的參考幀,可以得到預測值]

在store_macroblock_parameters (mode);中将率失真小的那個模式的最佳參考幀存到frefframe[j][i]中

而後有一個函數set_stored_macroblock_parameters ()

有enc_picture->ref_idx[LIST_0][img->block_x+i][img->block_y+j] = frefframe[j][i];

也就是說最終的最佳模式的最佳參考幀就存到了enc_picture->ref_idx中

Enc_picture->mv[LIST_0][img->block_x+i][img->block_y+j][0] =

Img->all_mv[i][j][LIST_0][frefframe[j][i]][currmb->b8mode[i/2+(j/2)*2]][0]; enc_picture->mv[LIST_0][img->block_x+i][img->block_y+j][1] =

Img->all_mv[i][j][LIST_0][frefframe[j][i]][currmb->b8mode[i/2+(j/2)*2]][1];

最佳的運動矢量也存到了enc_picture->mv中

在write_one_macroblock()中每8x8塊的最佳模式是由currmb->b8mode[i]參數來傳遞的[具體說是函數writemotioninfo2nal()中調用的writereferenceframe的參數],這個參數的值是在void encode_one_macroblock ()中的 set_stored_macroblock_parameters函數中的

For (i=0; i<4; i++)

{

Currmb->b8mode[i] = b8mode[i];求得的,

而b8mode[i]是在

If (rdcost_for_macroblocks (lambda_mode, mode, &min_rdcost))

{

Store_macroblock_parameters (mode);

中記錄的那個具有最小的率失真的模式

Distortion的計算是綜合了色度和亮度的distortion, 色度部分的計算也是在rdcost_for_macroblocks函數和rdcost_for_8x8blocks函數中的,

但JM8.5中得到色度分量的分像素運動矢量時好像有點問題,在 onecomponentchromaprediction4x4函數中!!

色度分量的運動矢量不需要搜尋判決, 是根據亮度分量的運動矢量乘以2得到的(JM85中好像沒乘2?)

色度分量的最佳模式是對應亮度分量的最佳模式除以2。 如亮度最佳模式是8*16,則色度最佳模式是4*8。

2011年4月24日9:28:20

2011年4月20日21:22:53

H.264中最優運動矢量殘差的輸出

最優運動矢量的求解是在encode_one_macroblock函數裡面,是以該函數執行完畢運動矢量及分割模式也就相應的确定了,這裡我們對這一塊作一下簡要的分析。

運動矢量的寫碼流是在write_one_macroblock裡的,它的一個大緻的函數調用關系是:

write_one_macroblock----->writeMotionInfo2NAL----->writeMotionVector8x8,就是這樣了,在writeMotionInfo2NAL裡:

H.264難點問題分析

這裡的兩個循環主要是周遊一個宏塊裡的大的分割塊,分割模式一共有1,2,3,(4,5,5,7)這些種,後四種又統稱為P8*8模式,每個宏塊的運動矢量數目是這樣的,模式1有一個運動矢量,模式2,3有兩個,P8*8則根據8*8子塊的分割模式細分。我們進writeMotionVector8x8函數去看看。

H.264難點問題分析

上面被選中的那一句就是求運動矢量殘差了,等号右邊的兩個也就是相應的最佳運動矢量和預測運動矢量,這裡也有兩個循環,是循環P8*8模式裡的小分塊的。

我們隻要在這裡把curr_mvd輸出來就能得到運動矢量殘差資料了,若想得到最優運動矢量則輸出等号右邊一項即可。

這裡存在一個問題,如果在writeMotionVector8x8函數裡直接輸出運動矢量資訊當然也是可以的,但在encode_one_macroblock函數裡進行模式判決的時候調用了RDCost_for_macroblock以及RDCost_for_8*8block,而這兩個函數裡也分别調用了writeMotionInfo2NAL,是以在進行模式選擇時也把一些其它模式求解出的運動矢量殘差資料輸出來,這個問題怎麼解決呢。

其實在函數RDCost_for_macroblock和函數RDCost_for_8*8block中調用writeMotionInfo2NAL後也進行過寫碼流的操作(主要是為了得到真正的編碼比特數), 但是從總體上看, 利用store_coding_state和reset_coding_state兩個函數, 其實最後還是恢複到沒有寫碼流之前的狀态.

2011年4月25日20:06:43

JM8.6中對MV的預測

BlockMotionSearch函數對不同幀間模式block type (1-16x16 ... 7-4x4)進行運動搜尋.

從該函數中,我們可以發現,有一個局部變量

H.264難點問題分析

,通過下面的語句将img->pred_mv與pred_mv聯系了起來,

H.264難點問題分析

這樣其實通過調用函數SetMotionVectorPredictor來計算運動矢量的預測值(MVpred), 代碼中向SetMotionVectorPredictor函數傳遞了pred_mv這個整型指針(指向img->pred_mv具體要儲存的地方), 這樣在函數SetMotionVectorPredictor中求出運動矢量的預測值,其實就存在了img->pred_mv中了.

H.264難點問題分析
H.264難點問題分析

具體在函數SetMotionVectorPredictor中預測MV的求解如下:

對水準和垂直兩個方向進行循環

H.264難點問題分析

具體的計算如下

H.264難點問題分析

根據不同的mvPredType計算得出相應的pred_vec, 然後存在pmv[hv]中.

2011年4月21日11:01:18

JM8.6中對上層塊模式預測的實作

下面的幾個變量是定義的儲存上層塊預測的結果

H.264難點問題分析

上面這個三個數組應該都是和快速搜尋有關的.

具體的pred_MV_uplayer是在函數BlockMotionSearch中,如代碼:

H.264難點問題分析
H.264難點問題分析

但是看代碼可以發現上層塊預測隻在FMEable的情況下才使用的.在函數BlockMotionSearch中有對各個搜尋函數的調用.

由于pred_MV_uplayer是一個全局變量, 是以在函數FastIntegerPelBlockMotionSearch中有使用pred_MV_uplayer,如下

H.264難點問題分析

對于模式1[16x16]是沒有上層塊預測的,是以隻有在模式2-7的情況下可以使用的

在函數FastSubPelBlockMotionSearch中也有使用

H.264難點問題分析

總結上面的,我們可以發現,在進行求MVD的時候, 我們是使用利用中值預測求出來的MV的預測值, 沒有使用上層塊預測的結果,而上層塊預測的結果是隻在運動搜尋中使用的.

2011年4月20日9:26:54

dct_luma_16x16流程的問題

H.264難點問題分析
H.264難點問題分析

2011年4月20日10:03:04

JM8.6與T264碼率控制的不同

H.264難點問題分析

2011年4月24日17:38:36

率失真曲線

率失真(RD)曲線 反映了不同編碼器的編碼性能好壞。

一般RD曲線都是以碼率(Kbps)做為橫坐标,以PSNR(dB)作為縱坐标做出來一條曲線,曲線上的點一般是采用QP=28,32,36,40這四個QP下的編碼碼率和編碼品質 (QPi, butrate,psnr)。

曲線點越高,表明性能越好。

2011年4月25日20:24:30

H.264中的ASO與FMO

H.264難點問題分析

ASO指的是片在傳輸時的先後順序可以随意變化, 而FMo是指在一幀圖像中宏塊的屬于不同的片的次序比較靈活

H.264難點問題分析

2011年5月7日11:08:22

對于listX可以這麼了解StorablePicture *listX[6][ref_frame]; 即6是6個參考清單,ref_frame是參考幀的數目,每一個listX[i][j]中存的是StorablePicture*指針

H.264難點問題分析

是以我們可以這麼了解,listX是6個清單, 每一個清單中存放的是一個指針(StorablePicture**), 該指針指向一個記憶體區域, 這個記憶體區域存儲是該表中參考幀, 但是我們不直接存參考幀, 我們在這兒存儲的是參考幀的指針(StorablePicture*)

至于6個清單中到底存放了多少個參考幀, 這個是由數組lsitXsize決定的, 這個lsitXsize[6]數組就是用來存儲對應清單大小的

2011年5月9日22:06:22

H.264中DCT變換中所使用的蝶形算法

然而,4X4的矩陣運算如果按正常算法的話仍要進行64次乘法運算和48次加法,這将耗費較多的時間,于是在H.264中,有一種改進的算法(蝶形算法)可以減少運算的次數。這種矩陣運算算法構造非常巧妙,利用構造的矩陣的整數性質和對稱性,可完全将乘法運算轉化為加法運算。

H.264難點問題分析
H.264難點問題分析
H.264難點問題分析

變換過程在JM中代碼實作如下:

// Horizontal transform水準變換,其實就是左乘Cf,

for (j=0; j < BLOCK_SIZE && !lossless_qpprime; j++)

{

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

{

i1=3-i;

m5[i]=img->m7[i][j]+img->m7[i1][j];

m5[i1]=img->m7[i][j]-img->m7[i1][j];

}

img->m7[0][j]=(m5[0]+m5[1]);

img->m7[2][j]=(m5[0]-m5[1]);

img->m7[1][j]=m5[3]*2+m5[2];

img->m7[3][j]=m5[3]-m5[2]*2;

}

// Vertical transform垂直變換,其實就是右乘CfT

for (i=0; i < BLOCK_SIZE && !lossless_qpprime; i++)

{

    for (j=0; j < 2; j++)

{

j1=3-j;

m5[j]=img->m7[i][j]+img->m7[i][j1];

m5[j1]=img->m7[i][j]-img->m7[i][j1];

}

img->m7[i][0]=(m5[0]+m5[1]);

img->m7[i][2]=(m5[0]-m5[1]);

img->m7[i][1]=m5[3]*2+m5[2];

img->m7[i][3]=m5[3]-m5[2]*2;

}

2011年5月16日10:03:48

YUV420

Y'UV420p (and Y'V12 or YV12)

Y'UV420p is a planar format, meaning that the Y', U, and V values are grouped together instead of interspersed. The reason for this is that by grouping the U and V values together, the image becomes much more compressible. When given an array of an image in the Y'UV420p format, all the Y' values come first, followed by all the U values, followed finally by all the V values.

The Y'V12 format is essentially the same as Y'UV420p, but it has the U and V data reversed: the Y' values are followed by the V values, with the U values last. As long as care is taken to extract U and V values from the proper locations, both Y'UV420p and Y'V12 can be processed using the same algorithm.

As with most Y'UV formats, there are as many Y' values as there are pixels. Where X equals the height multiplied by the width, the first X indices in the array are Y' values that correspond to each individual pixel. However, there are only one fourth as many U and V values. The U and V values correspond to each 2 by 2 block of the image, meaning each U and V entry applies to four pixels. After the Y' values, the next X/4 indices are the U values for each 2 by 2 block, and the next X/4 indices after that are the V values that also apply to each 2 by 2 block.

Translating Y'UV420p to RGB is a more involved process compared to the previous formats. Lookup of the Y', U and V values can be done using the following method:

size.total = size.width * size.height;
           
y = yuv[position.y * size.width + position.x];
           
u = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total];
           
v = yuv[(position.y / 2) * (size.width / 2) + (position.x / 2) + size.total + (size.total / 4)];
           
rgb = Y'UV444toRGB888(y, u, v);
           

Here "/" is Div not division.

H.264難點問題分析

As shown in the above image, the Y', U and V components in Y'UV420 are encoded separately in sequential blocks. A Y' value is stored for every pixel, followed by a U value for each 2×2 square block of pixels, and finally a V value for each 2×2 block. Corresponding Y', U and V values are shown using the same color in the diagram above. Read line-by-line as a byte stream from a device, the Y' block would be found at position 0, the U block at position x×y (6×4 = 24 in this example) and the V block at position x×y + (x×y)/4 (here, 6×4 + (6×4)/4 = 30).

繼續閱讀