H.264標準寫得比較繁復,所以考慮在瀏覽完Whitepaper之后就開始研讀X264代碼。X264代碼風格還是比較清晰簡潔的。
根據對標準得理解,Picture Order Count在Slice解碼的一開始就被提及:
I0 B1 B2 P3 B4 B5 P6
I0 P3 B1 B2 P6 B4 B5
于是I0的POC是0,P3的POC是3,B1是1……
為了支持H264復雜的幀存機制,X264以專門的一個模塊frame.c進行處理。
common/frame.c中包括一組幀緩沖操作函數。包括對幀進行FILO和FIFO存取,空閑幀隊列的相應操作等。
以下逐個函數分析encoder.c中編碼一幀的函數x264_encoder_encode中有關frame的調用:
x264_reference_update
這個函數里最主要的工作的是將上一個參考幀放入參考幀隊列,并從空閑幀隊列中取出一幀作為當前的參考工作幀(即解碼操作的目的幀),即h->fdec。
x264_t結構體維護著CODEC的諸多重要信息,其中成員frames是一個指示和控制幀編碼過程的結構。其中current是已經準備就緒可以編碼的幀,其類型已經確定;next是尚未確定類型的幀;unused用于回收不使用的frame結構體以備今后再次使用。frames結構體中i_input指示當前輸入的幀的(播放順序)序號。i_delay設置為由B幀個數(線程個數)確定的幀緩沖延遲,在多線程情況下為i_delay = i_bframe + i_threads - 1。而判斷B幀緩沖填充是否足夠則通過條件判斷:h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads。
x264_encoder_encode每次會以參數送入一幀待編碼的幀pic_in,函數首先會從空閑隊列中取出一幀用于承載該新幀,而它的i_frame被設定為播放順序計數,如:fenc->i_frame = h->frames.i_input++。
x264_encoder_encode在根據上述判據確定B幀緩沖充滿的情況下才進行后續編碼工作。
當當前隊列(current隊列)可用幀為0時,需要對next隊列中的幀進行判決,需要進行如下過程:
1. 調用x264_slicetype_decide
這個函數確定當前條帶(幀)的類型
其中首先調用x264_ratecontrol_slice_type,依據碼率控制逐個求出next列表中所有幀的類型(雖然在當前并不全部用到,見后)。
隨后統計審查并調整next列表,保證IDR幀滿足有關最大關鍵幀間隔的要求的正常出現:即針對frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max作判斷。審查按順序針對所有被判定為B系或AUTO類型的幀進行(這些幀在審核過程中被確認為B幀),直到遇到第一個不是這樣的幀。
如果某個幀被指定為IDR,則一個GOP在它之前結束。
2. 而后,即將next列表中已經判定的一系列幀(先后是一些B幀和一個非B幀)轉移到current列表中。在這個過程中:
原始序列(播放順序)B0, B1, B2, P,轉移后的順序為P, B0, B1, B2。在使用bframe_pyramid模式時,中間的B幀要前置,即上述順序變為:P, B1, B0, B2。
此時,就可以從current隊列中取出一幀,進行編碼,現在記這幀叫h->fenc。
首先做幾項和幀有關的設置工作:
1. 如果f_enc是IDR,則將最近IDR序號標記h->frames.i_last_idr設置為i_frame。
2. 根據f_enc的類型確定NAL和SLICE類型相關參數。
3. 設置POC為2 * (h->fenc->i_frame - h->frames.i_last_idr)。并使得h->fdec和h->fenc的主要幀參數一致。
隨后進行以下一些過程:
x264_reference_build_list
在這個函數中,我們將遇到參考幀列表h->frames.reference和H.264很有特色的雙列表(h->fref0、h->fref1)。前者中放置了所有可用于參考的參考幀。
首先將所有reference列表中的幀按照POC和h->fenc的POC的大小關系不同復制到雙列表中,其中h->fref0放置POC較小的那些。然后對雙列表進行排序,使得h->fref0中的幀POC按從大到小排列,h->fref1按從小到大排列,于是這兩個列表中靠前的幀的POC比較接近當前幀h->fenc。在這里,一個特殊的情況是,對于P幀(只需要用到列表h->fref0做參考),其排序依據是i_frame_num而不是POC,因此需要對這種情況的列表h->fref0置位需重排序標記。最后對雙列表的長度做限制。
x264_ratecontrol_start
碼率控制初始化,在以后專門的話題中論述。
在初始化后,從碼率控制體中獲得全局QP值:i_global_qp = x264_ratecontrol_qp( h )。
如果當前是B條帶(幀),則調用x264_macroblock_bipred_init
該函數設置一些參考幀關系矩陣,關系矩陣以fref0的索引為行坐標,以fref1的索引為列坐標。主要包括:dist_scale_factor,bipred_weight,矩陣值主要由相應幀的POC決定。后續討論。
x264_slice_init
內部調用x264_slice_header_init,它對Header進行配置,含義以后討論。
bs_init
初始化碼流工作上下文。
隨后將幀類型表現到碼流中。接著寫SEI,SPS和PPS。
然后就是最關鍵的寫條帶(即主要的編碼工作)開始的地方:
x264_slices_write
這里通過x264_stack_align調用x264_slice_write,為了將棧做4字節對齊,以提高運行效率。
x264_slice_write函數是編碼一幀的關鍵函數,將在下一章中論述。
宏塊結構
這里主要有一下幾個過程:
1. 初始化h->stat.frame,即全部清零。
2. 寫條帶頭:x264_slice_header_write,即把剛才x264_slice_header_init設置的一些參數寫入。
3. 如果是CABAC編碼,則初始化CABAC。有關CABAC在后續相關章節討論。
4. 遍歷一幀中的所有宏塊,這是編碼的主要部分:
for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; )
其中sh.i_first_mb和sh.i_last_mb在x264_slice_header_init中賦值,分別是0和宏塊行列數乘積。
5. 最后輸出碼流尾部。
其間穿插一些必要的初始化和配置。
本章著重分析編碼的主要部分,包括一下過程:
需要注意的是,這里討論的宏塊是固定尺寸的,即Y分量計為16x16。(而運動宏塊的尺寸則是多變的)
1. 首先根據宏塊的序號得出宏塊的坐標。然后統計當前宏塊的碼流位置:
int mb_spos = bs_pos(&h->out.bs) + x264_cabac_pos(&h->cabac);
2. 然后出現一句:
if( i_mb_x == 0 )
??? x264_fdec_filter_row( h, i_mb_y );
這是對行進行去塊過程。詳細情況專門論述。
3. 隨后是x264_macroblock_cache_load,這里主要進行當前宏塊和相關信息的提取。這個函數比較大,也在專題中論述。
4. 接下去進行解碼分析操作
x264_macroblock_analyse
5. 以及解碼實體:
x264_macroblock_encode
6. 而后處理碼流輸出,也分CABAC和CAVLC兩種情況。
7. 接著出現x264_macroblock_cache_save,和x264_macroblock_cache_load對應,保存處理完的宏塊。
8. 更新宏塊統計
下面,首先看編碼主體x264_macroblock_encode的結構。
該函數按不同宏塊類型情況進行不同的處理。包括如下類型:
P_SKIP
B_SKIP
I_16x16 - 一次完成一個塊h->mb.pic.p_fdec[0]的編碼。
?? 用函數指針h->predict_16x16[i_mode]進行幀內預測
I_8x8?? - 分4次完成。h->mb.pic.p_fdec[0][8 * (i&1) + 8 * (i>>1) * FDEC_STRIDE]指定了當前位置。即:
?? [0][1]
?? [2][3]
?? 用函數指針完成4個宏塊的幀內預測
I_4x4 - 分16次完成。h->mb.pic.p_fdec[0][4 * block_idx_x[i] + 4 * block_idx_y[i] * FDEC_STRIDE]指定當前位置。
?? 根據block_idx_x和block_idx_y的定義,其順序是:
?? [0][1][4][5]
?? [2][3][6][7]
?? [8][9][c][d]
?? [a][b][e][f]
?? 用函數指針完成4個宏塊的幀內預測
Inter - 首先進行運動補償。見后續討論。
????????? 然后分多種情形:
?? 1. 無損壓縮情況:h->mb.b_lossless == TRUE(必須采用4x4DCT)
????? 根據上述I_4x4中描述的順序遍歷16個4x4塊。
????? 這里調用的函數是zigzag_sub_4x4_field,完成殘差。(無損情況直接發送殘差)
?? 2. 指定采用8x8DCT情況:h->mb.b_transform_8x8 == TRUE
????? 首先是作殘差并DCT的函數:sub16x16_dct8。
????? 然后對4個8x8的DCT系數塊進行處理:包括可能的去噪x264_denoise_dct,量化(標量/矢量)。有關量化也在專題中論述。量化完成后進行系數掃描(對角平衡的普通掃描)。
?? 3. 一般情況
?????
接著處理色差分量,如果是Intra的則進行預測。
最后寫Coded Block Pattern。
幀內預測
環路內濾波
H.264環路內濾波顧名思義在編碼側開啟后解碼部分必須跟隨開啟,因此是該視頻編碼方案的不可分割的組成部分。
??? 以下整理了Baseline情形下環路濾波的四種情形:
??? All cases that may exist for Baseline
??? Bs = 4: either is intra, MB edge
??? Bs = 3: either is intra, block edge
??? Bs = 2: both are inter, either is coded
??? Bs = 1: both are inter neither is coded, different ref pictures, either MV component is no smaller than 4
??? Bs = 0: o.w.
?
??? 以T264為例:
??? 環路濾波入口函數以光柵掃描順序對所有宏塊實施deblock_mb。
??? deblock_mb完成一個宏塊的濾波,它包含以下步驟:
??? 1. 水平相鄰宏塊濾波,即對與mb_xy-1宏塊的邊界濾波,以及內部3條縱向塊邊界濾波;
??? 2. 垂直相鄰宏塊濾波,即對與mb_xy-mb_stride宏塊的邊界濾波,以及內部3條橫向塊邊界濾波;
?
??? 每條邊界長度均為16個像素,等分成4份,每個對應一個最小塊大小的邊界,以此為單元進行濾波。
??? 對每條邊界調用get_strength,獲得這4個部分的Bs值。
本章討論的代碼主要位于common/predict.c中。
x264_macroblock_cache_load函數在每個宏塊解碼之前初始化某些狀態,在x264_slice_write函數的宏塊處理循環中被調用。
i_mb_xy:?? 當前宏塊的索引
i_mb_4x4:? 當前宏塊中第一個4x4塊的索引
i_mb_8x8:? 當前宏塊中第一個8x8塊的索引
i_top_y:?? 上方宏塊的y索引
i_top_xy:? 上方宏塊的索引
i_top_4x4: 當前宏塊中第一個4x4塊上方的4x4塊的索引
i_top_8x8: 當前宏塊中第一個8x8塊上方的8x8塊的索引
這里首先初始化和當前宏塊毗鄰的已解碼塊。
這里用到x264_scan8,它指示如下掃描結構:
?? 0 1 2 3 4 5 6 7
?0
?1?? 0 1?? 0 1 4 5
?2?? 2 3?? 2 3 6 7
?3???????? 8 9 C D
?4?? 0 1?? A B E F
?5?? 2 3
?在空缺部分恰好可以填入相應宏塊的毗鄰塊。
以下逐個各種分析預測情況:
對于16x16的塊:
predict_16x16
共7種預測模式:H, V, DC, P, LEFT, TOP, 128
predict_16x16_h
用左側相鄰16x16塊的右側像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_16x16_p
右上左下均值預測覆蓋(注意均值生成)
predict_16x16_dc
用左側邊沿和上方邊沿的均值作為DC預測值進行單一覆蓋
predict_16x16_dc_left
只用左側邊沿進行DC單一覆蓋
predict_16x16_dc_top
只用上方邊沿進行DC單一覆蓋
predict_16x16_dc_128
用128進行單一覆蓋
對于8x8的色差塊:
predict_8x8c_dc_128
用128進行單一覆蓋
predict_8x8c_dc_left
上下兩個4行分別以對應左邊沿4個像素均值單一覆蓋
predict_8x8c_dc_top
左右兩個4列分別以對應上邊沿4個像素均值單一覆蓋
predict_8x8c_dc
?? s0 s1
s2 b0 b1
s3 b2 b3
s?是邊沿4像素組
4個4x4塊分別用s0,s2均值,s1均值,s3均值和s1,s3均值單一覆蓋
predict_8x8c_h
用左側相鄰8x8塊的右邊沿像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_8x8c_p
右上左下均值預測覆蓋(注意均值生成)
對于4x4的塊:(這是H264文檔經常拿來作demo的)
predict_4x4_dc_128
用128進行單一覆蓋
predict_4x4_dc_left
用左邊沿4個像素均值單一覆蓋
predict_4x4_dc_top
用上邊沿4個像素均值單一覆蓋
predict_4x4_dc
用左側和上方邊沿共8個像素均值單一覆蓋
predict_4x4_h
用左側相鄰4x4塊的右邊沿像素沿水平方向覆蓋
predict_4x4_v
用上方相鄰4x4塊的底行像素沿豎直方向覆蓋
predict_4x4_ddl
右上至左下預測覆蓋(注意均值生成)
predict_4x4_ddr
左上至右下預測覆蓋(注意均值生成)
predict_4x4_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_4x4_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hu
左下到右上(偏右)預測覆蓋(注意均值生成)
對于8x8亮度塊:
edge中存放
predict_8x8_dc_128
用128進行單一覆蓋
predict_8x8_dc_left
用左側塊邊沿8個像素均值單一覆蓋
predict_8x8_dc_top
用上方塊邊沿8個像素均值單一覆蓋
predict_8x8_dc
用左側塊和上方塊邊沿共16個像素均值單一覆蓋
predict_8x8_h
用左側邊沿預測覆蓋
predict_8x8_v
用上方邊沿預測覆蓋
predict_8x8_ddl
右上到左下預測覆蓋(注意均值生成)
predict_8x8_ddr
左上到右下預測覆蓋(注意均值生成)
predict_8x8_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_8x8_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hu
左下到右上(偏右)預測覆蓋(注意均值生成)
以上“注意均值生成”指預測值一般由起點決定,起點由反向延長線確定,起點2倍權重和兩側點構成均值。
例如45度均值為以起始點對應45度位置的點作為中心(2倍權重),其兩側點作為補充形成的均值。
H.264標準規定,首先由macroblock層的mb_type導出intra塊的預測類型macroblock prediction mode,其中包含子塊尺度信息,有4x4,8x8和16x16三種。4x4和8x8按照標準由變換尺寸區分,4x4是最常用的模式。
在確定預測類型之后,對于含16個子塊的4x4模式,每個子塊的預測模式(即上述討論)由標準規定的判決算法給出:
<...>
根據對標準得理解,Picture Order Count在Slice解碼的一開始就被提及:
I0 B1 B2 P3 B4 B5 P6
I0 P3 B1 B2 P6 B4 B5
于是I0的POC是0,P3的POC是3,B1是1……
為了支持H264復雜的幀存機制,X264以專門的一個模塊frame.c進行處理。
common/frame.c中包括一組幀緩沖操作函數。包括對幀進行FILO和FIFO存取,空閑幀隊列的相應操作等。
以下逐個函數分析encoder.c中編碼一幀的函數x264_encoder_encode中有關frame的調用:
x264_reference_update
這個函數里最主要的工作的是將上一個參考幀放入參考幀隊列,并從空閑幀隊列中取出一幀作為當前的參考工作幀(即解碼操作的目的幀),即h->fdec。
x264_t結構體維護著CODEC的諸多重要信息,其中成員frames是一個指示和控制幀編碼過程的結構。其中current是已經準備就緒可以編碼的幀,其類型已經確定;next是尚未確定類型的幀;unused用于回收不使用的frame結構體以備今后再次使用。frames結構體中i_input指示當前輸入的幀的(播放順序)序號。i_delay設置為由B幀個數(線程個數)確定的幀緩沖延遲,在多線程情況下為i_delay = i_bframe + i_threads - 1。而判斷B幀緩沖填充是否足夠則通過條件判斷:h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads。
x264_encoder_encode每次會以參數送入一幀待編碼的幀pic_in,函數首先會從空閑隊列中取出一幀用于承載該新幀,而它的i_frame被設定為播放順序計數,如:fenc->i_frame = h->frames.i_input++。
x264_encoder_encode在根據上述判據確定B幀緩沖充滿的情況下才進行后續編碼工作。
當當前隊列(current隊列)可用幀為0時,需要對next隊列中的幀進行判決,需要進行如下過程:
1. 調用x264_slicetype_decide
這個函數確定當前條帶(幀)的類型
其中首先調用x264_ratecontrol_slice_type,依據碼率控制逐個求出next列表中所有幀的類型(雖然在當前并不全部用到,見后)。
隨后統計審查并調整next列表,保證IDR幀滿足有關最大關鍵幀間隔的要求的正常出現:即針對frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max作判斷。審查按順序針對所有被判定為B系或AUTO類型的幀進行(這些幀在審核過程中被確認為B幀),直到遇到第一個不是這樣的幀。
如果某個幀被指定為IDR,則一個GOP在它之前結束。
2. 而后,即將next列表中已經判定的一系列幀(先后是一些B幀和一個非B幀)轉移到current列表中。在這個過程中:
原始序列(播放順序)B0, B1, B2, P,轉移后的順序為P, B0, B1, B2。在使用bframe_pyramid模式時,中間的B幀要前置,即上述順序變為:P, B1, B0, B2。
此時,就可以從current隊列中取出一幀,進行編碼,現在記這幀叫h->fenc。
首先做幾項和幀有關的設置工作:
1. 如果f_enc是IDR,則將最近IDR序號標記h->frames.i_last_idr設置為i_frame。
2. 根據f_enc的類型確定NAL和SLICE類型相關參數。
3. 設置POC為2 * (h->fenc->i_frame - h->frames.i_last_idr)。并使得h->fdec和h->fenc的主要幀參數一致。
隨后進行以下一些過程:
x264_reference_build_list
在這個函數中,我們將遇到參考幀列表h->frames.reference和H.264很有特色的雙列表(h->fref0、h->fref1)。前者中放置了所有可用于參考的參考幀。
首先將所有reference列表中的幀按照POC和h->fenc的POC的大小關系不同復制到雙列表中,其中h->fref0放置POC較小的那些。然后對雙列表進行排序,使得h->fref0中的幀POC按從大到小排列,h->fref1按從小到大排列,于是這兩個列表中靠前的幀的POC比較接近當前幀h->fenc。在這里,一個特殊的情況是,對于P幀(只需要用到列表h->fref0做參考),其排序依據是i_frame_num而不是POC,因此需要對這種情況的列表h->fref0置位需重排序標記。最后對雙列表的長度做限制。
x264_ratecontrol_start
碼率控制初始化,在以后專門的話題中論述。
在初始化后,從碼率控制體中獲得全局QP值:i_global_qp = x264_ratecontrol_qp( h )。
如果當前是B條帶(幀),則調用x264_macroblock_bipred_init
該函數設置一些參考幀關系矩陣,關系矩陣以fref0的索引為行坐標,以fref1的索引為列坐標。主要包括:dist_scale_factor,bipred_weight,矩陣值主要由相應幀的POC決定。后續討論。
x264_slice_init
內部調用x264_slice_header_init,它對Header進行配置,含義以后討論。
bs_init
初始化碼流工作上下文。
隨后將幀類型表現到碼流中。接著寫SEI,SPS和PPS。
然后就是最關鍵的寫條帶(即主要的編碼工作)開始的地方:
x264_slices_write
這里通過x264_stack_align調用x264_slice_write,為了將棧做4字節對齊,以提高運行效率。
x264_slice_write函數是編碼一幀的關鍵函數,將在下一章中論述。
宏塊結構
這里主要有一下幾個過程:
1. 初始化h->stat.frame,即全部清零。
2. 寫條帶頭:x264_slice_header_write,即把剛才x264_slice_header_init設置的一些參數寫入。
3. 如果是CABAC編碼,則初始化CABAC。有關CABAC在后續相關章節討論。
4. 遍歷一幀中的所有宏塊,這是編碼的主要部分:
for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; )
其中sh.i_first_mb和sh.i_last_mb在x264_slice_header_init中賦值,分別是0和宏塊行列數乘積。
5. 最后輸出碼流尾部。
其間穿插一些必要的初始化和配置。
本章著重分析編碼的主要部分,包括一下過程:
需要注意的是,這里討論的宏塊是固定尺寸的,即Y分量計為16x16。(而運動宏塊的尺寸則是多變的)
1. 首先根據宏塊的序號得出宏塊的坐標。然后統計當前宏塊的碼流位置:
int mb_spos = bs_pos(&h->out.bs) + x264_cabac_pos(&h->cabac);
2. 然后出現一句:
if( i_mb_x == 0 )
??? x264_fdec_filter_row( h, i_mb_y );
這是對行進行去塊過程。詳細情況專門論述。
3. 隨后是x264_macroblock_cache_load,這里主要進行當前宏塊和相關信息的提取。這個函數比較大,也在專題中論述。
4. 接下去進行解碼分析操作
x264_macroblock_analyse
5. 以及解碼實體:
x264_macroblock_encode
6. 而后處理碼流輸出,也分CABAC和CAVLC兩種情況。
7. 接著出現x264_macroblock_cache_save,和x264_macroblock_cache_load對應,保存處理完的宏塊。
8. 更新宏塊統計
下面,首先看編碼主體x264_macroblock_encode的結構。
該函數按不同宏塊類型情況進行不同的處理。包括如下類型:
P_SKIP
B_SKIP
I_16x16 - 一次完成一個塊h->mb.pic.p_fdec[0]的編碼。
?? 用函數指針h->predict_16x16[i_mode]進行幀內預測
I_8x8?? - 分4次完成。h->mb.pic.p_fdec[0][8 * (i&1) + 8 * (i>>1) * FDEC_STRIDE]指定了當前位置。即:
?? [0][1]
?? [2][3]
?? 用函數指針完成4個宏塊的幀內預測
I_4x4 - 分16次完成。h->mb.pic.p_fdec[0][4 * block_idx_x[i] + 4 * block_idx_y[i] * FDEC_STRIDE]指定當前位置。
?? 根據block_idx_x和block_idx_y的定義,其順序是:
?? [0][1][4][5]
?? [2][3][6][7]
?? [8][9][c][d]
?? [a][b][e][f]
?? 用函數指針完成4個宏塊的幀內預測
Inter - 首先進行運動補償。見后續討論。
????????? 然后分多種情形:
?? 1. 無損壓縮情況:h->mb.b_lossless == TRUE(必須采用4x4DCT)
????? 根據上述I_4x4中描述的順序遍歷16個4x4塊。
????? 這里調用的函數是zigzag_sub_4x4_field,完成殘差。(無損情況直接發送殘差)
?? 2. 指定采用8x8DCT情況:h->mb.b_transform_8x8 == TRUE
????? 首先是作殘差并DCT的函數:sub16x16_dct8。
????? 然后對4個8x8的DCT系數塊進行處理:包括可能的去噪x264_denoise_dct,量化(標量/矢量)。有關量化也在專題中論述。量化完成后進行系數掃描(對角平衡的普通掃描)。
?? 3. 一般情況
?????
接著處理色差分量,如果是Intra的則進行預測。
最后寫Coded Block Pattern。
幀內預測
環路內濾波
H.264環路內濾波顧名思義在編碼側開啟后解碼部分必須跟隨開啟,因此是該視頻編碼方案的不可分割的組成部分。
??? 以下整理了Baseline情形下環路濾波的四種情形:
??? All cases that may exist for Baseline
??? Bs = 4: either is intra, MB edge
??? Bs = 3: either is intra, block edge
??? Bs = 2: both are inter, either is coded
??? Bs = 1: both are inter neither is coded, different ref pictures, either MV component is no smaller than 4
??? Bs = 0: o.w.
?
??? 以T264為例:
??? 環路濾波入口函數以光柵掃描順序對所有宏塊實施deblock_mb。
??? deblock_mb完成一個宏塊的濾波,它包含以下步驟:
??? 1. 水平相鄰宏塊濾波,即對與mb_xy-1宏塊的邊界濾波,以及內部3條縱向塊邊界濾波;
??? 2. 垂直相鄰宏塊濾波,即對與mb_xy-mb_stride宏塊的邊界濾波,以及內部3條橫向塊邊界濾波;
?
??? 每條邊界長度均為16個像素,等分成4份,每個對應一個最小塊大小的邊界,以此為單元進行濾波。
??? 對每條邊界調用get_strength,獲得這4個部分的Bs值。
本章討論的代碼主要位于common/predict.c中。
x264_macroblock_cache_load函數在每個宏塊解碼之前初始化某些狀態,在x264_slice_write函數的宏塊處理循環中被調用。
i_mb_xy:?? 當前宏塊的索引
i_mb_4x4:? 當前宏塊中第一個4x4塊的索引
i_mb_8x8:? 當前宏塊中第一個8x8塊的索引
i_top_y:?? 上方宏塊的y索引
i_top_xy:? 上方宏塊的索引
i_top_4x4: 當前宏塊中第一個4x4塊上方的4x4塊的索引
i_top_8x8: 當前宏塊中第一個8x8塊上方的8x8塊的索引
這里首先初始化和當前宏塊毗鄰的已解碼塊。
這里用到x264_scan8,它指示如下掃描結構:
?? 0 1 2 3 4 5 6 7
?0
?1?? 0 1?? 0 1 4 5
?2?? 2 3?? 2 3 6 7
?3???????? 8 9 C D
?4?? 0 1?? A B E F
?5?? 2 3
?在空缺部分恰好可以填入相應宏塊的毗鄰塊。
以下逐個各種分析預測情況:
對于16x16的塊:
predict_16x16
共7種預測模式:H, V, DC, P, LEFT, TOP, 128
predict_16x16_h
用左側相鄰16x16塊的右側像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_16x16_p
右上左下均值預測覆蓋(注意均值生成)
predict_16x16_dc
用左側邊沿和上方邊沿的均值作為DC預測值進行單一覆蓋
predict_16x16_dc_left
只用左側邊沿進行DC單一覆蓋
predict_16x16_dc_top
只用上方邊沿進行DC單一覆蓋
predict_16x16_dc_128
用128進行單一覆蓋
對于8x8的色差塊:
predict_8x8c_dc_128
用128進行單一覆蓋
predict_8x8c_dc_left
上下兩個4行分別以對應左邊沿4個像素均值單一覆蓋
predict_8x8c_dc_top
左右兩個4列分別以對應上邊沿4個像素均值單一覆蓋
predict_8x8c_dc
?? s0 s1
s2 b0 b1
s3 b2 b3
s?是邊沿4像素組
4個4x4塊分別用s0,s2均值,s1均值,s3均值和s1,s3均值單一覆蓋
predict_8x8c_h
用左側相鄰8x8塊的右邊沿像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_8x8c_p
右上左下均值預測覆蓋(注意均值生成)
對于4x4的塊:(這是H264文檔經常拿來作demo的)
predict_4x4_dc_128
用128進行單一覆蓋
predict_4x4_dc_left
用左邊沿4個像素均值單一覆蓋
predict_4x4_dc_top
用上邊沿4個像素均值單一覆蓋
predict_4x4_dc
用左側和上方邊沿共8個像素均值單一覆蓋
predict_4x4_h
用左側相鄰4x4塊的右邊沿像素沿水平方向覆蓋
predict_4x4_v
用上方相鄰4x4塊的底行像素沿豎直方向覆蓋
predict_4x4_ddl
右上至左下預測覆蓋(注意均值生成)
predict_4x4_ddr
左上至右下預測覆蓋(注意均值生成)
predict_4x4_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_4x4_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hu
左下到右上(偏右)預測覆蓋(注意均值生成)
對于8x8亮度塊:
edge中存放
predict_8x8_dc_128
用128進行單一覆蓋
predict_8x8_dc_left
用左側塊邊沿8個像素均值單一覆蓋
predict_8x8_dc_top
用上方塊邊沿8個像素均值單一覆蓋
predict_8x8_dc
用左側塊和上方塊邊沿共16個像素均值單一覆蓋
predict_8x8_h
用左側邊沿預測覆蓋
predict_8x8_v
用上方邊沿預測覆蓋
predict_8x8_ddl
右上到左下預測覆蓋(注意均值生成)
predict_8x8_ddr
左上到右下預測覆蓋(注意均值生成)
predict_8x8_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_8x8_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hu
左下到右上(偏右)預測覆蓋(注意均值生成)
以上“注意均值生成”指預測值一般由起點決定,起點由反向延長線確定,起點2倍權重和兩側點構成均值。
例如45度均值為以起始點對應45度位置的點作為中心(2倍權重),其兩側點作為補充形成的均值。
H.264標準規定,首先由macroblock層的mb_type導出intra塊的預測類型macroblock prediction mode,其中包含子塊尺度信息,有4x4,8x8和16x16三種。4x4和8x8按照標準由變換尺寸區分,4x4是最常用的模式。
在確定預測類型之后,對于含16個子塊的4x4模式,每個子塊的預測模式(即上述討論)由標準規定的判決算法給出:
<...>