1. 程式人生 > >x264閱讀記錄-3

x264閱讀記錄-3

https://www.cnblogs.com/xkfz007/p/3734510.html

 

14. x264_macroblock_encode函式-1

這個函式主要根據已經選定的模式來對巨集塊殘差進行編碼。

(1)如果是P_SKIP模式,那麼呼叫x264_macroblock_encode_pskip函式

在x264_macroblock_encode_pskip中先對亮度和色度進行運動補償,呼叫的函式函式h->mc.mc_luma和 h->mc.mc_chroma。這兩個是函式指標,根據需要對其進行初始化。一般常用的是函式mc_luma, 在mc.c檔案中。在mc_luma中如果存在MV(x和y均不為0),那麼呼叫pixel_avg;否則,呼叫mc_copy。pixel_avg位於mc.c檔案中,是1/4搜尋時需要臨時插值函式。 

dst[x] = ( src1[x] + src2[x] + 1 ) >> 1;   //利用相鄰半畫素和兩個畫素取平均插值

mc_copy位於mc.c檔案。

最後還要呼叫x264_macroblock_encode_skip函式,在這個函式中主要設定CBP值。

(2)如果是B_SKIP模式,先是呼叫x264_mb_mc函式完成運動補償,然後呼叫x264_macroblock_encode_skip來進行CBP設定。

(3)接下來是幀內模式的編碼,包括幀內I_16x16,幀內I_8x8和幀內I_4x4。在做每種模式的編碼之前都要先計算預測幀。分別呼叫相應的預測函式 predict_16x16,predict_8x8(4個塊)和predict_4x4(16個塊)。

A. 對於I_16x16,呼叫x264_mb_encode_i16x16進行編碼,如果是無損編碼h->mb.b_lossless被置上,則以4x4為單位,進行無失真壓縮的量化和掃描。若是有失真壓縮方式,先呼叫 h-> dctf. sub16x16_dct計算巨集塊殘差(p_src-p_dst)並進行4x4大小的DCT變換。 仍然,h-> dctf. sub16x16_dct是一個函式指標,其最常設定的值是sub16x16_dct,位於dct.c檔案中。

在sub16x16_dct函式中,是對4個8x8塊進行呼叫分別呼叫sub8x8_dct來完成殘差計算和DCT變換的。而sub8x8_dct則是呼叫4個sub4x4_dct分別對4個4x4塊進行殘差DCT變換計算的。具體到sub4x4_dct,是呼叫pixel_sub_wxh來計算得到殘差,然後再進行DCT變換,採用264中經典的4x4DCT變換的演算法。

在DCT變換完成之後,對16個4x4塊在for迴圈中進行處理:先拿出每個4x4塊的DC係數(存放在一個數組dct4x4[0]中),然後對每個4x4塊進行量化、掃描和反量化。分別由函式:x264_quant_4x4_trellis或quant_4x4、scan_zigzag_4x4和h->quantf.dequant_4x4完成。

接下來,對所有4x4塊的在陣列dct4x4 [0]中DCT係數進行:DCT變換、量化和掃描。分別由函式: h->dctf.dct4x4dc、quant_4x4_dc和scan_zigzag_4x4full完成。

然後,為了重建幀的需要,對DC係數進行反DCT變換、反量化(為什麼不是先反量化後反變換,待解決)。分別由函式h->dctf.idct4x4dc和x264_mb_dequant_4x4_dc完成。對於直流係數,對每個4x4子塊進行反DCT、反量化並加到預測巨集塊上得到重建的幀。由函式 h->dctf.add16x16_idct完成。

B. 對於I_8x8模式,將巨集塊分成4個8x8來分別進行。先呼叫predict_8x8函式來計算預測值,然後呼叫函式x264_mb_encode_i8x8進行編碼。在該函式中,8x8DCT變換——h->dctf.sub8x8_dct8函式,量化—— x264_quant_8x8_trellis或quant_8x8函式、掃描——h->zigzagf.scan_8x8函式、反量化—— h->quantf.dequant_8x8函式和反DCT變換並加到預測巨集塊上——h->dctf.add8x8_idct8函式。

 

C.對16個4x4塊分別進行。先h->predict_4x4[i_mode]進行幀內預測,得到預測幀。然後x264_mb_encode_i4x4編碼。在x264_mb_encode_i4x4中,如果是無失真壓縮h->mb.b_lossless置位。呼叫sub_zigzag_4x4full計算殘差,並掃描,返回。若是非無損編碼,進行DCT、量化、Z字掃描、反量化、反IDCT並加到預測幀上。分別由函式:h->dctf.sub4x4_dct、 x264_quant_4x4_trellis或quant_4x4、scan_zigzag_4x4full、 h->quantf.dequant_4x4和h->dctf.add4x4_idct完成。

(4)如果是幀間模式編碼,

A. 先呼叫x264_mb_mc進行運動補償。根據巨集塊型別進行相應的運動補償:使用列表0(h->mb.i_type == P_L0)的16x16預測模式,根據h->mb.i_partition的型別(D_16x16、D_16x8、D_8x16),分別呼叫 x264_mb_mc_0xywh函式進行前向巨集塊運動補償。x264_mb_mc_0xywh分別對亮度和色度進行運動補償: h->mc.mc_luma和h->mc.mc_chroma函式。如果巨集塊型別(h->mb.i_type)為P_8x8或B_8x8。對4個8x8塊,分別呼叫x264_mb_mc_8x8進行運動補償。 其根據字塊型別(h->mb.i_sub_partition[i8])分別進行運動補償。D_L0_8x8、D_L0_8x4、D_L0_4x8 和D_L0_4x4型別,呼叫x264_mb_mc_0xywh進行前向巨集塊運動補償。D_L1_8x8、D_L1_8x4、D_L1_4x8和 D_L1_4x4型別,呼叫x264_mb_mc_1xywh進行後向巨集塊運動補償。D_BI_8x8、D_BI_8x4、D_BI_4x8和 D_BI_4x4型別,分別呼叫x264_mb_mc_01xywh進行巨集塊雙向運動補償。D_DIRECT_8x8型別,呼叫x264_mb_mc_direct8x8 進行直接模式8*8塊運動補償。x264_mb_mc_direct8x8函式根據不同的條件呼叫x264_mb_mc_0xywh、x264_mb_mc_1xywh和x264_mb_mc_01xywh函式。如果巨集塊型別(h->mb.i_type)為B_SKIP或B_DIRECT。呼叫直接模式8*8塊運動補償函式x264_mb_mc_direct8x8。如果巨集塊型別(h->mb.i_type)為其它B幀模式。先初始化參考列表。根據子塊型別(h->mb.i_partition) 為,D_16x16、D_16x8、D_8x16和前向、後項、雙向參考型別,分別呼叫x264_mb_mc_0xywh、 x264_mb_mc_1xywh和x264_mb_mc_01xywh函式。

B. 運動補償之後,進行編碼。如果是無損編碼(h->mb.b_lossless ==1),對16個4x4塊,分別進行殘差計算和z字掃描,由函式sub_zigzag_4x4full完成。如果要進行8x8子塊編碼( h->mb.b_transform_8x8),呼叫h->dctf.sub16x16_dct8對每個8x8塊先計算殘差,再DCT變換。然後對4個8x8塊,分別呼叫x264_denoise_dct去噪、x264_quant_8x8_trellis或quant_8x8量化、字掃描。然後是反量化、將殘差加到參考巨集塊上。由h->quantf.dequant_8x8和h->dctf.add8x8_idct8完成。然後對4個8x8塊,分別呼叫x264_denoise_dct去噪、x264_quant_8x8_trellis或quant_8x8量化、scan_zigzag_8x8full Z字掃描。然後是反量化、將殘差加到參考巨集塊上。由h->quantf.dequant_8x8和h->dctf.add8x8_idct8完成。

C. 如果不進行8x8子塊編碼。先用h->dctf.sub16x16_dct先算p_src-p_dst巨集塊殘差,再對每個4x4模組進行 dct變換。然後對16個4x4塊進行去噪——x264_denoise_dct、量化x264_quant_4x4_trellis或 quant_4x4、Z字掃描——scan_zigzag_4x4full。然後是反量化——h->quantf.dequant_4x4和 反DCT並加到參考幀上——h->dctf.add8x8_idct。

D. 亮度編碼完成後,進行色度編碼。x264_mb_encode_8x8_chroma函式。對Cb和Cr分量分別進行。

如果時無損編碼,對4個4x4塊進行殘差計算、Z字掃描和DC係數計算。

非無損編碼,用h->dctf.sub8x8_dct對每個4x4塊進行殘差計算和DCT轉換。之後對每個4x4塊,先取2x2DC係數,量 化——quant_4x4_chroma、Z字掃描——h->zigzagf.scan_4x4ac。對2x2DC係數進行DCT變換、量化和Z字 掃描,分別由函式h->dctf.dct2x2dc、quant_2x2_dc和scan_zigzag_2x2_dc。接著是反DCT變換和反量 化,h->dctf.idct2x2dc與x264_mb_dequant_2x2_dc函式。h->quantf.dequant_4x4 對4x4塊反量化,h->dctf.add8x8_idct對每個4x4塊反DCT變換並且加到色度預測幀上。

E. 亮度和色度編碼完成後。計算亮度和色度模式和非零的個數,確定h->mb.i_cbp_chroma和h->mb.cbp的值。

 

 

15. x264_macroblock_encode函式-2

下面是該函式的呼叫情況,在上面對這個函式的分析中,已經對其中的大部分的函式都有了一個相對比較詳細的介紹,

 

16. x264_macroblock_write_cabac函式

在巨集塊編碼結束後,就要將編碼生成的內容寫到碼流中。如果支援CABAC編碼,就呼叫x264_macroblock_write_cabac。如果是CAVLC編碼,就呼叫x264_macroblock_write_cavlc。

先呼叫函式x264_cabac_mb_type來將巨集塊型別寫入碼流,然後根據巨集塊的型別進行碼流的寫入操作:

A. 如果是I_PCM型別,那麼直接對亮度和色度呼叫函式bs_write來將資料寫入碼流,然後返回。

B. 如果是幀內型別,如果採用了8x8DCT變換並且不是I_16x16,那麼就呼叫x264_cabac_mb_transform_size來將變換的尺寸寫入碼流,如果是I_8x8或I_4x4,需要將預測模式寫入碼流,然後是將色度預測模式寫入碼流。

C. 如果是幀間16x16, 16x8 和 8x16 ,則根據相應的型別,呼叫x264_cabac_mb_ref將參考幀寫入碼流,呼叫x264_cabac_mb_mvd將運動向量差寫入碼流

D. 如果是幀間P_8x8模式,首先將每一個子塊的模式寫入碼流x264_cabac_mb_sub_p_partition,然後將每一個塊用到的參考幀寫入碼流x264_cabac_mb_ref,最後在函式x264_cabac_mb8x8_mvd中將每一個8x8塊的mvd寫入碼流。

E. 如果是B_8x8模式,同樣寫入子塊模式x264_cabac_mb_sub_b_partition,然後前後參考幀寫入碼流x264_cabac_mb_ref,最後呼叫x264_cabac_mb8x8_mvd將L0和L1上的參考幀寫入碼流。

F. 如果是除B_DIRECT之外所有其他模式,將參考幀和mvd寫入碼流。

 

接下來將亮度和色度CBP寫入碼流:x264_cabac_mb_cbp_luma和x264_cabac_mb_cbp_chroma。

然後將qp_delta寫入碼流x264_cabac_mb_qp_delta, 同時根據巨集塊型別將殘差係數寫入碼流block_residual_write_cabac。

 

 

17.x264_macroblock_write_cavlc函式


首先,根據幀型別計算i_mb_i_offset的值。依據巨集塊型別(i_mb_type)寫相應的資料到流中:

A. 如果巨集塊型別是I_PCM,就把16x16個亮度值、2x8x8個色度值直接寫到碼流中。

B. 如果巨集塊型別是I_4x4或I_8x8,先寫入是否是8x8模式。然後對16個4x4塊和4個8x8塊,先呼叫 x264_mb_predict_intra4x4_mode獲取最可能的預測模式,然後比較當前的預測模式和最可能的預測模式,將判斷結果寫入碼流中, 最後寫入當前的預測模式值。

C. 如果巨集塊型別是I_16x16,將當前的預測模式寫入碼流。

D. 如果巨集塊型別是P_L0, 巨集塊子分割槽分別為D_16x16、D_16x8和D_8x16時,處理步驟相似,先將分割槽型別寫入碼流,然後預測運動向量(x264_mb_predict_mv)得到mvp,最後寫入當前運動向量與mvp的差值(MVD)。

E. 如果巨集塊型別是P_8x8或B_8x8時,過程類似。先寫入型別,接著寫入4個子巨集塊型別,參考幀資訊,呼叫cavlc_mb8x8_mvd計算 mvd。cavlc_mb8x8_mvd函式中根據不同的子分割槽型別(D_L0_8x8/D_L1_8x8/D_BI_8x8...)呼叫 cavlc_mb_mvd函式計算MVD並寫入碼流。cavlc_mb_mvd函式中也是計算mvp(x264_mb_predict_mv),算mvd 並寫入碼流中。

F. 如果巨集塊型別是非B_DIRECT——B幀的非直接模式,可能會雙向參考。之後根據兩個參考幀列表結合子分割槽型別(D_16x16、D_16x8、D_8x16),寫入參考幀資訊,計算mvp(x264_mb_predict_mv),計算mvd並寫入碼流中。

G. 如果巨集塊型別是B_DIRECT,就寫個型別。

 

這樣,在運動向量差寫入完成後。寫編碼的塊模式。寫殘差資料,分亮度殘差資料和色度殘差資料。

A. 如果型別是I_16x16,先計算qp-delta並寫入碼流,寫亮度DC和AC係數(block_residual_write_cavlc)。

B. 如果不是I_16x16,且亮度和色度有一個不是零(h->mb.i_cbp_luma != 0 || h->mb.i_cbp_chroma != 0)。先計算qp-delta並寫入碼流,然後呼叫x264_macroblock_luma_write_cavlc進行亮度巨集塊編碼。

 

對於色度殘差資料的cavlc編碼,先寫入Cb,Cr的DC係數(block_residual_write_cavlc),後寫入AC係數(block_residual_write_cavlc)。

這樣就完成了熵編碼。