首页 > 代码库 > x264阅读记录-3

x264阅读记录-3

 

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中如果存在MVxy均不为0),那么调用pixel_avg;否则,调用mc_copypixel_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_16x16predict_8x84个块)和predict_4x416个块)。

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函数中,是对48x8块进行调用分别调用sub8x8_dct来完成残差计算和DCT变换的。而sub8x8_dct则是调用4sub4x4_dct分别对44x4块进行残差DCT变换计算的。具体到sub4x4_dct,是调用pixel_sub_wxh来计算得到残差,然后再进行DCT变换,采用264中经典的4x4DCT变换的算法。

DCT变换完成之后,对164x4块在for循环中进行处理:先拿出每个4x4块的DC系数(存放在一个数组dct4x4[0]中),然后对每个4x4块进行量化、扫描和反量化。分别由函数:x264_quant_4x4_trellisquant_4x4scan_zigzag_4x4h->quantf.dequant_4x4完成。

接下来,对所有4x4块的在数组dct4x4 [0]DCT系数进行:DCT变换、量化和扫描。分别由函数: h->dctf.dct4x4dcquant_4x4_dcscan_zigzag_4x4full完成。

然后,为了重建帧的需要,对DC系数进行反DCT变换、反量化(为什么不是先反量化后反变换,待解决)。分别由函数h->dctf.idct4x4dcx264_mb_dequant_4x4_dc完成。对于直流系数,对每个4x4子块进行反DCT、反量化并加到预测宏块上得到重建的帧。由函数 h->dctf.add16x16_idct完成。

B. 对于I_8x8模式,将宏块分成48x8来分别进行。先调用predict_8x8函数来计算预测值,然后调用函数x264_mb_encode_i8x8进行编码。在该函数中,8x8DCT变换——h->dctf.sub8x8_dct8函数,量化—— x264_quant_8x8_trellisquant_8x8函数、扫描——h->zigzagf.scan_8x8函数、反量化—— h->quantf.dequant_8x8函数和反DCT变换并加到预测宏块上——h->dctf.add8x8_idct8函数。

 

C.164x4块分别进行。先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_trellisquant_4x4scan_zigzag_4x4full h->quantf.dequant_4x4h->dctf.add4x4_idct完成。

4)如果是帧间模式编码,

A. 先调用x264_mb_mc进行运动补偿。根据宏块类型进行相应的运动补偿:使用列表0h->mb.i_type == P_L0)的16x16预测模式,根据h->mb.i_partition的类型(D_16x16D_16x8D_8x16),分别调用 x264_mb_mc_0xywh函数进行前向宏块运动补偿。x264_mb_mc_0xywh分别对亮度和色度进行运动补偿: h->mc.mc_lumah->mc.mc_chroma函数。如果宏块类型(h->mb.i_type)为P_8x8B_8x8。对48x8块,分别调用x264_mb_mc_8x8进行运动补偿。其根据字块类型(h->mb.i_sub_partition[i8])分别进行运动补偿。D_L0_8x8D_L0_8x4D_L0_4x8 D_L0_4x4类型,调用x264_mb_mc_0xywh进行前向宏块运动补偿。D_L1_8x8D_L1_8x4D_L1_4x8 D_L1_4x4类型,调用x264_mb_mc_1xywh进行后向宏块运动补偿。D_BI_8x8D_BI_8x4D_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_0xywhx264_mb_mc_1xywhx264_mb_mc_01xywh函数。如果宏块类型(h->mb.i_type)为B_SKIPB_DIRECT。调用直接模式8*8块运动补偿函数x264_mb_mc_direct8x8。如果宏块类型(h->mb.i_type)为其它B帧模式。先初始化参考列表。根据子块类型(h->mb.i_partition为,D_16x16D_16x8D_8x16和前向、后项、双向参考类型,分别调用x264_mb_mc_0xywh x264_mb_mc_1xywhx264_mb_mc_01xywh函数。

B. 运动补偿之后,进行编码。如果是无损编码(h->mb.b_lossless ==1),对164x4块,分别进行残差计算和z字扫描,由函数sub_zigzag_4x4full完成。如果要进行8x8子块编码( h->mb.b_transform_8x8),调用h->dctf.sub16x16_dct8对每个8x8块先计算残差,再DCT变换。然后对48x8块,分别调用x264_denoise_dct去噪、x264_quant_8x8_trellisquant_8x8量化、字扫描。然后是反量化、将残差加到参考宏块上。由h->quantf.dequant_8x8h->dctf.add8x8_idct8完成。然后对48x8块,分别调用x264_denoise_dct去噪、x264_quant_8x8_trellisquant_8x8量化、scan_zigzag_8x8full Z字扫描。然后是反量化、将残差加到参考宏块上。由h->quantf.dequant_8x8h->dctf.add8x8_idct8完成。

C. 如果不进行8x8子块编码。先用h->dctf.sub16x16_dct先算p_src-p_dst宏块残差,再对每个4x4模块进行 dct变换。然后对164x4块进行去噪——x264_denoise_dct、量化x264_quant_4x4_trellis quant_4x4Z字扫描——scan_zigzag_4x4full。然后是反量化——h->quantf.dequant_4x4DCT并加到参考帧上——h->dctf.add8x8_idct

D. 亮度编码完成后,进行色度编码。x264_mb_encode_8x8_chroma函数。对CbCr分量分别进行。

如果时无损编码,对44x4块进行残差计算、Z字扫描和DC系数计算。

非无损编码,用h->dctf.sub8x8_dct对每个4x4块进行残差计算和DCT转换。之后对每个4x4块,先取2x2DC系数,量——quant_4x4_chromaZ字扫描——h->zigzagf.scan_4x4ac。对2x2DC系数进行DCT变换、量化和Z扫描,分别由函数h->dctf.dct2x2dcquant_2x2_dcscan_zigzag_2x2_dc。接着是反DCT变换和反量化,h->dctf.idct2x2dcx264_mb_dequant_2x2_dc函数。h->quantf.dequant_4x4 4x4块反量化,h->dctf.add8x8_idct对每个4x4块反DCT变换并且加到色度预测帧上。

E. 亮度和色度编码完成后。计算亮度和色度模式和非零的个数,确定h->mb.i_cbp_chromah->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_8x8I_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_mvdL0L1上的参考帧写入码流。

F. 如果是除B_DIRECT之外所有其他模式,将参考帧和mvd写入码流。

 

接下来将亮度和色度CBP写入码流:x264_cabac_mb_cbp_lumax264_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_4x4I_8x8,先写入是否是8x8模式。然后对164x4块和48x8块,先调用 x264_mb_predict_intra4x4_mode获取最可能的预测模式,然后比较当前的预测模式和最可能的预测模式,将判断结果写入码流中,最后写入当前的预测模式值。

C. 如果宏块类型是I_16x16,将当前的预测模式写入码流。

D. 如果宏块类型是P_L0, 宏块子分区分别为D_16x16D_16x8D_8x16时,处理步骤相似,先将分区类型写入码流,然后预测运动向量(x264_mb_predict_mv)得到mvp,最后写入当前运动向量与mvp的差值(MVD)。

E. 如果宏块类型是P_8x8B_8x8时,过程类似。先写入类型,接着写入4个子宏块类型,参考帧信息,调用cavlc_mb8x8_mvd计算 mvdcavlc_mb8x8_mvd函数中根据不同的子分区类型(D_L0_8x8/D_L1_8x8/D_BI_8x8...)调用 cavlc_mb_mvd函数计算MVD并写入码流。cavlc_mb_mvd函数中也是计算mvpx264_mb_predict_mv),算mvd 并写入码流中。

F. 如果宏块类型是非B_DIRECT——B帧的非直接模式,可能会双向参考。之后根据两个参考帧列表结合子分区类型(D_16x16D_16x8D_8x16),写入参考帧信息,计算mvpx264_mb_predict_mv),计算mvd并写入码流中。

G. 如果宏块类型是B_DIRECT,就写个类型。

 

这样,在运动向量差写入完成后。写编码的块模式。写残差数据,分亮度残差数据和色度残差数据。

A. 如果类型是I_16x16,先计算qp-delta并写入码流,写亮度DCAC系数(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,CrDC系数(block_residual_write_cavlc),后写入AC系数(block_residual_write_cavlc)。

这样就完成了熵编码。