x264阅读记录-3

 

14. x264_macroblock_encode函数-1 算法

这个函数主要根据已经选定的模式来对宏块残差进行编码。 数组

1)若是是P_SKIP模式,那么调用x264_macroblock_encode_pskip函数 less

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文件。 spa

最后还要调用x264_macroblock_encode_skip函数,在这个函数中主要设置CBP值。 指针

(2)若是是B_SKIP模式,先是调用x264_mb_mc函数完成运动补偿,而后调用x264_macroblock_encode_skip来进行CBP设置。 code

3)接下来是帧内模式的编码,包括帧内I_16x16,帧内I_8x8和帧内I_4x4。在作每种模式的编码以前都要先计算预测帧。分别调用相应的预测函数 predict_16x16predict_8x84个块)和predict_4x416个块)。 orm

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文件中。 blog

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_4x4 DCT并加到参考帧上——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)。

这样就完成了熵编码。

相关文章
相关标签/搜索