宏块划分方式
一副图像(帧,非场图像,x264支持宏块级场编码,这里以帧图像为例说明)按从左到右、从上到下16x16的方式划分宏块,对于图像宽度不是16的倍数的情况,会扩展至16的倍数,然后通过sps的crop参数表示出需要裁剪的区域。
宏块是H.264标准中基本的编码单元,其基本结构包含一个16x16的亮度像素块和两个8x8的色度像素块。
组成部分:一个宏块由一个 16×16 亮度像素和附加的一个 8×8 Cb 和一个 8×8 Cr 彩色像素块组成
一帧图片被划分为宏块,宏块大小为16x16像素
16x16的宏块根据预测类型的不同里面会划分出子块
帧内预测采用的大小可能为16x16或者4x4
帧间预测采用的可能有7种不同的形状:16×16、16×8、8×16、8×8、8×4、4×8和4×4。
I_PCM宏块,不预测,无残差,不转换,无量化等操作,直接将原始的YUV像素数据经编码后(差分编码)写入码流中。
所以IPCM宏块是完全无损的。
- mb_type
根据上面宏块的解码流程可以都看到,根据mb_type可以将宏块分为I_PCM宏块和非I_PCM宏块。
当宏块为I_PCM类型时(mb_type = 25):
当宏块为非I_PCM类型时,根据切片类型的不同,宏块有不同的类型
具体见宏块类型
pcm_alignment_zero_bit | 在PCM情况下会要求进行字节对齐 |
---|---|
pcm_sample_luma | 亮度的PCM,一共有16x16个 |
pcm_sample_chroma | 色度的PCM,根据yuv的格式不同会有不同的个数,一般的4:2:0格式有8x8x2个 |
sub_mb_pred | 子宏块预测的语法结构,子宏块为8x8大小的宏块,也就是说一个宏块有4个子宏块,在这个语法结构的内部会进行4次子宏块预测 |
transform_size_8x8_flag | 宏块所采用的变换方法0:4x4DCT;1:8x8DCT。变换方法请参考H.264 Transform。按照上述语法描述,只有满足了以下条件码流中才会出现这个语法元素,如果该语法元素没出现在码流中则默认为0。 - Intra:总开关transform_8x8_mode_flag为1,并且宏块类型为I_NxN(参考h.264宏块与子宏块类型,如果是I_8x8则为1,如果是I_4x4则为0)。 - Inter:总开关transform_8x8_mode_flag为1,并且宏块的预测模式块不小于8x8。另外如果宏块采用了直接预测方式,并且预测模式为B_Direct_16x16,则需要direct_8x8_inference_flag为1。(此时码流中transform_size_8x8_flag的值为0或者1,是在编码端由算法自行决定的)。 - Intra_16x16以及I_PCM的情况下码流中不存在该语法元素。 |
mb_pred | 宏块预测的语法结构,宏块预测与子宏块预测的语法结构是相斥的,一个宏块的组成结构要么采用的是宏块预测的结构,要么4个子宏块都是子宏块的预测结构 |
coded_block_pattern | 用于表示当前宏块内的4个8x8子块编码对其中的哪个残差系数进行编码。 该值仅在宏块为非I_16x16模式时才存在,因为在I_16x16模式相关信息已经在mb_type中体现 简称CBP,用来反映该宏块编码中残差情况的语法元素。CBP共有6位,其中前面2位代表UV分量,描述如下表所示;后面4位是Y分量,分别代表宏块内的4个8x8子宏块,如果任意一位为0,表明对应的8x8块中所有变换系数level(transform coefficient levels 也就是对像素残差进行变换、量化后的矩阵内的值,以后统称level)全部都是0,否则表明对应的8x8块中的变换系数level不全为0。另外需要注意的是,如果当前宏块的预测模式是Intra_16x16,则不会存在这个元素,此时CBP会由mb_type来表示,请参考h.264宏块与子宏块类型。CBP的主要作用是加快解码速度,当一个块的残差都为0时,就不用对这个块进行残差解码了。 ![]() |
mb_qp_delta | 用来计算当前宏块的QP,QP=pic_init_qp_minus26 + 26 + slice_qp_delta + mb_qp_delta |
residual | 像素残差编码的语法结构 |
按照macroblock_layer的语法结构上看,宏块能粗略分成三种结构:PCM、sub_mb_pred(子宏块预测)、mb_pred(宏块预测)。另外,虽然skip宏块并不在macroblock内描述,它也是宏块的一种结构。
mb_pred( mb_type ) { C Descriptor
if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 | |
MbPartPredMode( mb_type, 0 ) = = Intra_8x8 | |
MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) {
if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 )
for( luma4x4BlkIdx=0; luma4x4BlkIdx<16; luma4x4BlkIdx++ ) {
prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] 2 u(1) | ae(v)
if( !prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] )
rem_intra4x4_pred_mode[ luma4x4BlkIdx ] 2 u(3) | ae(v)
}
if( MbPartPredMode( mb_type, 0 ) = = Intra_8x8 )
for( luma8x8BlkIdx=0; luma8x8BlkIdx<4; luma8x8BlkIdx++ ) {
prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] 2 u(1) | ae(v)
if( !prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] )
rem_intra8x8_pred_mode[ luma8x8BlkIdx ] 2 u(3) | ae(v)
}
if( ChromaArrayType = = 1 | | ChromaArrayType = = 2 )
intra_chroma_pred_mode 2 ue(v) | ae(v)
} else if( MbPartPredMode( mb_type, 0 ) != Direct ) {
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
if( ( num_ref_idx_l0_active_minus1 > 0 | |
mb_field_decoding_flag != field_pic_flag ) &&
MbPartPredMode( mb_type, mbPartIdx ) != Pred_L1 )
ref_idx_l0[ mbPartIdx ] 2 te(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
if( ( num_ref_idx_l1_active_minus1 > 0 | |
mb_field_decoding_flag != field_pic_flag ) &&
MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 )
ref_idx_l1[ mbPartIdx ] 2 te(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
if( MbPartPredMode ( mb_type, mbPartIdx ) != Pred_L1 )
for( compIdx = 0; compIdx < 2; compIdx++ )
mvd_l0[ mbPartIdx ][ 0 ][ compIdx ] 2 se(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
if( MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 )
for( compIdx = 0; compIdx < 2; compIdx++ )
mvd_l1[ mbPartIdx ][ 0 ][ compIdx ] 2 se(v) | ae(v)
}
}
prev_intra4x4_pred_mode_flag rem_intra4x4_pred_mode |
如果当前宏块采用的是intra4x4的预测方式,则可能会存在这个语法元素,它的含义请参考Intra Luma Prediction |
---|---|
prev_intra8x8_pred_mode_flag rem_intra8x8_pred_mode |
如果当前宏块采用的是intra8x8的预测方式,则可能会存在这个语法元素,它的含义请参考Intra Luma Prediction |
intra_chroma_pred_mode | intra chroma的预测模式,只有当前宏块的Luma部分采用intra预测时才会存在这个语法元素,它的含义请参考Intra Chroma Prediction |
ref_idx_l0 | 前向参考图像索引。如果当前宏块为inter预测,并且他的预测方式并非后向预测(即可能为前向预测或双向预测),则会存在这个语法元素 |
ref_idx_l1 | - 后向参考图像索引。如果当前宏块为inter预测,并且他的预测方式并非前向预测(即可能为后向预测或双向预测),则会存在这个语法元素 |
mvd_l0 | 前向向量残差。如果当前宏块为inter预测,并且他的预测方式并非后向预测(即可能为前向预测或双向预测),则会存在这个语法元素 |
mvd_l1 | 后向向量残差。如果当前宏块为inter预测,并且他的预测方式并非前向预测(即可能为后向预测或双向预测),则会存在这个语法元素 |
下图分别是几个mb_pred结构的例子
- 在intra16x16的宏块模式下,intra16x16的宏块信息是被包含在mb_type里面的,因此mb_pred结构内只有chroma相关的信息
- 在intra8x8的宏块模式下,共有四个子宏块,因此分成4个部分
- 在intra4x4的宏块模式下,共有16个4x4块,因此分成16部分
- 如果是Pslice的inter宏块,并且宏块采用16x8的分割方式,那么宏块会被分割成两部分,因此会有两个refIdx以及两个mvd
- 如果是Bslice的inter宏块,并且宏块采用16x16的分割方式,那么宏块不会被分割,如果这个没被宏块采用的是双向预测方式,那么会有前、后向的refIdx以及mvd
- 如果是Bslice的inter宏块,并且宏块采用8x16的分割方式,那么宏块会被分割成两部分,如果第一部分采用的是前向预测方式,第二部分采用的是双向预测方式,那么mb_pred内会有两个前向、一个后向refIdx以及mvd
sub_mb_pred
**
sub_mb_pred( mb_type ) { C Descriptor
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
sub_mb_type[ mbPartIdx ] 2 ue(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
if( ( num_ref_idx_l0_active_minus1 > 0 | |
mb_field_decoding_flag != field_pic_flag ) &&
mb_type != P_8x8ref0 &&
sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L1 )
ref_idx_l0[ mbPartIdx ] 2 te(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
if( ( num_ref_idx_l1_active_minus1 > 0 | |
mb_field_decoding_flag != field_pic_flag ) &&
sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L0 )
ref_idx_l1[ mbPartIdx ] 2 te(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L1 )
for( subMbPartIdx = 0;
subMbPartIdx < NumSubMbPart( sub_mb_type[ mbPartIdx ] );
subMbPartIdx++)
for( compIdx = 0; compIdx < 2; compIdx++ )
mvd_l0[ mbPartIdx ][ subMbPartIdx ][ compIdx ] 2 se(v) | ae(v)
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L0 )
for( subMbPartIdx = 0;
subMbPartIdx < NumSubMbPart( sub_mb_type[ mbPartIdx ] );
subMbPartIdx++)
for( compIdx = 0; compIdx < 2; compIdx++ )
mvd_l1[ mbPartIdx ][ subMbPartIdx ][ compIdx ] 2 se(v) | ae(v)
}
sub_mb_type | 子宏块模式,子宏块大小为8x8,因此一个宏块内有4个子宏块,也就会有4种子宏块模式,具体请参考h.264宏块与子宏块类型 |
---|---|
ref_idx_l0 ref_idx_l1 |
描述同mb_pred,不过需要注意的一点是,由于在8x8的子宏块中,分块(2个8x4块,4个4x4块等)是共用参考图像的,也就是说整个宏块最多也就只包含四个ref_idx |
mvd_l0 mvd_l1 |
描述同mb_pred |
下面是一个sub_mb_pred语法结构的例子。假设处于Bslice,左边的表格分别代表四个子宏块的模式。在该sub_mb_pred结构中
- 头部保存的是4个子宏块各自的子宏块类型
- 接下来是前向refIdx,第一个子宏块的预测方式为Bi,因此有前向refIdx;第二个子宏块的预测方式为L0,也有前向refIdx;第三个子宏块为直接预测,没有refIdx;第四个子宏块为L0,有前向refIdx
- 然后是后向refIdx,只有第一个子宏块的Bi会包含后向refIdx
- 然后是前向mvd,第一个子宏块分割方式为4x8,分割成两个部分,因此有两个前向mvd;第二个子宏块分割方式为8x8,即不分割,因此只有一个前向mvd;第三个子宏块为直接预测,没有mvd;第四个子宏块分割方式为8x4,分割成两个部分,因此有两个前向mvd
- 最后是后向mvd,例子中只有第一个子宏块,也就是采用Bi预测的会有后向mvd,由于第一个子宏块被分割成两部分,因此有两个后向mvd
residual,residual_luma
**像素残差进行变换、量化后的系数的语法结构。
residual( startIdx, endIdx ) { C Descriptor
if( !entropy_coding_mode_flag )
residual_block = residual_block_cavlc
else
residual_block = residual_block_cabac
residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
startIdx, endIdx ) 3 | 4
Intra16x16DCLevel = i16x16DClevel
Intra16x16ACLevel = i16x16AClevel
LumaLevel4x4 = level4x4
LumaLevel8x8 = level8x8
if( ChromaArrayType = = 1 | | ChromaArrayType = = 2 ) {
NumC8x8 = 4 / ( SubWidthC * SubHeightC )
for( iCbCr = 0; iCbCr < 2; iCbCr++ )
if( ( CodedBlockPatternChroma & 3 ) && startIdx = = 0 )
/* chroma DC residual present */
residual_block( ChromaDCLevel[ iCbCr ], 0, 4 * NumC8x8 − 1,
4 * NumC8x8 ) 3 | 4
else
for( i = 0; i < 4 * NumC8x8; i++ )
ChromaDCLevel[ iCbCr ][ i ] = 0
for( iCbCr = 0; iCbCr < 2; iCbCr++ )
for( i8x8 = 0; i8x8 < NumC8x8; i8x8++ )
for( i4x4 = 0; i4x4 < 4; i4x4++ )
if( CodedBlockPatternChroma & 2 )
/* chroma AC residual present */
residual_block( ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ],
Max( 0, startIdx − 1 ), endIdx − 1, 15) 3 | 4
else
for( i = 0; i < 15; i++ )
ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ][ i ] = 0
} else if( ChromaArrayType = = 3 ) {
residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
startIdx, endIdx ) 3 | 4
CbIntra16x16DCLevel = i16x16DClevel
CbIntra16x16ACLevel = i16x16AClevel
CbLevel4x4 = level4x4
CbLevel8x8 = level8x8
residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
startIdx, endIdx ) 3 | 4
CrIntra16x16DCLevel = i16x16DClevel
CrIntra16x16ACLevel = i16x16AClevel
CrLevel4x4 = level4x4
CrLevel8x8 = level8x8
}
}
residual内部首先会根据entropy_coding_mode_flag来选择CAVLC或者CABAC的熵编码方式,然后在下面进行level的处理。level处理部分先包含了residual_luma,也就是先进行luma level的处理,然后用residual_block对chroma level进行处理。
chroma level一般采用的yuv格式都是4:2:0,也就是ChromaArrayType=1。
residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
startIdx, endIdx ) { C Descriptor
if( startIdx = = 0 && MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
residual_block( i16x16DClevel, 0, 15, 16 ) 3
for( i8x8 = 0; i8x8 < 4; i8x8++ )
if( !transform_size_8x8_flag | | !entropy_coding_mode_flag )
for( i4x4 = 0; i4x4 < 4; i4x4++ ) {
if( CodedBlockPatternLuma & ( 1 << i8x8 ) )
if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
residual_block( i16x16AClevel[i8x8*4+ i4x4],
Max( 0, startIdx − 1 ), endIdx − 1, 15) 3
else
residual_block( level4x4[ i8x8 * 4 + i4x4 ],
startIdx, endIdx, 16) 3 | 4
else if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
for( i = 0; i < 15; i++ )
i16x16AClevel[ i8x8 * 4 + i4x4 ][ i ] = 0
else
for( i = 0; i < 16; i++ )
level4x4[ i8x8 * 4 + i4x4 ][ i ] = 0
if( !entropy_coding_mode_flag && transform_size_8x8_flag )
for( i = 0; i < 16; i++ )
level8x8[ i8x8 ][ 4 * i + i4x4 ] = level4x4[ i8x8 * 4 + i4x4 ][ i ]
}
else if( CodedBlockPatternLuma & ( 1 << i8x8 ) )
residual_block( level8x8[ i8x8 ], 4 * startIdx, 4 * endIdx + 3, 64 ) 3 | 4
else
for( i = 0; i < 64; i++ )
level8x8[ i8x8 ][ i ] = 0
}
语法元素
- residual_luma 亮度残差变换量化后的语法结构,各参数说明如下
- i16x16DClevel 如果是intra16x16的宏块模式,会分开AC与DC单独编码,DC level会从residual_block内取出,并存在i16x16DClevel数组内
- i16x16AClevel 如果是intra16x16的宏块模式,会分开AC与DC单独编码,AC level会从residual_block内取出,并存在i16x16AClevel数组内
- level4x4 不是intra16x16的情况下,如果DCT变换采用的是4x4的方式,宏块的level会从residual_block内取出,并存在level4x4数组内
- level8x8 不是intra16x16的情况下,如果DCT变换采用的是8x8的方式,宏块的level会从residual_block内取出,并存在level8x8数组内
- startIdx 需要进行熵编码的数组起始位置的索引
- endIdx 需要进行熵编码的数组结束位置的索引
residual_block 这并不是完整的熵编码,而是熵编码参数的语法结构,以后分析熵编码再进行分析
luma level分为几种,如上面的数组i16x16DClevel, i16x16AClevel, level4x4, level8x8,在解码过程中,这些数组后续会被用于逆量化、逆变换,residual_luma的目的是从residual_block内取出level,并填充到这些数组当中。
我们来完整分析一下residual_luma的工作过程如果当前宏块是Intra_16x16的预测模式,并且为亮度残差语法结构的开头,那么会从residual_block中获取16个DC level,存储到i16x16DClevel数组内
接下来会以子宏块为单位进行,一个宏块内有4个8x8大小的子宏块
- 如果当前宏块是以4x4块为单位进行DCT变换的(transform_size_8x8_flag=0),或者如果要求当前视频使用CAVLC进行熵编码(entropy_coding_mode_flag=0),这意味着熵编码是以4x4块为单位进行的。一个子宏块内包含4个4x4个子块。
- 在当前4x4块内level不全为0的情况下(CBP相应位为1),才需要从residual_block中获取level。
- 如果当前的宏块为Intra_16x16的预测方式,需要从residual_block中获取15个AC level,存储到i16x16AClevel中
- 否则,则会从residual_block中获取16个level,存储到level4x4中
- 否则(CBP相应位为0),如果当前的宏块为Intra_16x16的预测方式,它的AC level全为0,为i16x16AClevel中的15个元素赋值0
- 否则(CBP相应位为0),意味着该4x4块内的level全为0,就为level4x4中的16个元素赋值0
- 如果当前视频使用CAVLC进行熵编码(entropy_coding_mode_flag=0),但是当前宏块是以8x8块为单位进行DCT变换的(transform_size_8x8_flag=1),我们则需要把这些CAVLC解码得到的4x4块组合成8x8块,以便后续使用
- 在当前4x4块内level不全为0的情况下(CBP相应位为1),才需要从residual_block中获取level。
- 否则(要求当前视频使用CABAC进行熵编码(entropy_coding_mode_flag=1)并且当前宏块是以8x8为单位进行DCT变换(transform_size_8x8_flag=1),如果该8x8块内的level不全为0(CBP相应位为1),那么就从residual_block中获取64个level,并存储到level8x8中)
- 否则(要求当前视频使用CABAC进行熵编码(entropy_coding_mode_flag=1)并且当前宏块是以8x8为单位进行DCT变换(transform_size_8x8_flag=1),如果该8x8块内的level全为0(CBP相应位为0),就为level8x8中的64个元素赋值0.
如果没有宏块CBP上的bit全都不为0的话,它的residual就会是如下的样子- 如果当前宏块是以4x4块为单位进行DCT变换的(transform_size_8x8_flag=0),或者如果要求当前视频使用CAVLC进行熵编码(entropy_coding_mode_flag=0),这意味着熵编码是以4x4块为单位进行的。一个子宏块内包含4个4x4个子块。
宏块类型
I_slice宏块类型
I slice中的宏块类型只能是I宏块类型(I开头),下标列出了所有的I宏块类型
mb_type | name | transform_size_8x8_flag | MbPartPredMode (mb_type, 0) |
Intra16x16PredMode | CodedBlockPatternChroma | CodedBlockPatternLuma |
---|---|---|---|---|---|---|
0 | I_NxN | 0 | Intra_4x4 | na | ||
0 | I_NxN | 1 | Intra_8x8 | na | ||
1 | I_16x16_0_0_0 | na | Intra_16x16 | 0 | 0 | 0 |
2 | I_16x16_1_0_0 | na | Intra_16x16 | 1 | 0 | 0 |
3 | I_16x16_2_0_0 | na | Intra_16x16 | 2 | 0 | 0 |
4 | I_16x16_3_0_0 | na | Intra_16x16 | 3 | 0 | 0 |
5 | I_16x16_0_1_0 | na | Intra_16x16 | 0 | 1 | 0 |
6 | I_16x16_1_1_0 | na | Intra_16x16 | 1 | 1 | 0 |
7 | I_16x16_2_1_0 | na | Intra_16x16 | 2 | 1 | 0 |
8 | I_16x16_3_1_0 | na | Intra_16x16 | 3 | 1 | 0 |
9 | I_16x16_0_2_0 | na | Intra_16x16 | 0 | 2 | 0 |
10 | I_16x16_1_2_0 | na | Intra_16x16 | 1 | 2 | 0 |
11 | I_16x16_2_2_0 | na | Intra_16x16 | 2 | 2 | 0 |
12 | I_16x16_3_2_0 | na | Intra_16x16 | 3 | 2 | 0 |
13 | I_16x16_0_0_1 | na | Intra_16x16 | 0 | 0 | 15 |
14 | I_16x16_1_0_1 | na | Intra_16x16 | 1 | 0 | 15 |
15 | I_16x16_2_0_1 | na | Intra_16x16 | 2 | 0 | 15 |
16 | I_16x16_3_0_1 | na | Intra_16x16 | 3 | 0 | 15 |
17 | I_16x16_0_1_1 | na | Intra_16x16 | 0 | 1 | 15 |
18 | I_16x16_1_1_1 | na | Intra_16x16 | 1 | 1 | 15 |
19 | I_16x16_2_1_1 | na | Intra_16x16 | 2 | 1 | 15 |
20 | I_16x16_3_1_1 | na | Intra_16x16 | 3 | 1 | 15 |
21 | I_16x16_0_2_1 | na | Intra_16x16 | 0 | 2 | 15 |
22 | I_16x16_1_2_1 | na | Intra_16x16 | 1 | 2 | 15 |
23 | I_16x16_2_2_1 | na | Intra_16x16 | 2 | 2 | 15 |
24 | I_16x16_3_2_1 | na | Intra_16x16 | 3 | 2 | 15 |
25 | I_PCM | na | na | na | na | na |
mb_type | 宏块类型的数值,I slice共有26个数值 |
---|---|
name | 宏块类型的名称,其中 - 名称开头的I表示I宏块类型 - I_NxN表示的是I_8x8或者I_4x4 - I_16x16_a_b_c中的I_16x16代表以intra_16x16为预测方式 - I_16x16_a_b_c中的a代表intra_16x16当中的4种模式 - I_16x16_a_b_c中的b代表使用intra_16x16预测方式时的Chroma CBP - I_16x16_a_b_c中的c代表使用intra_16x16预测方式时的Luma CBP |
transform_size_8x8_flag | 1表示采用8x8的块进行熵编码 0表示采用4x4块进行熵编码 na的情况同0 |
MbPartPredMode | 表明当前宏块类型所采用的Intra预测方式 |
Intra16x16PredMode | 如果当前宏块类型采用的预测方式为Intra_16x16,那么该字段有效,它用0~3表示了Intra_16x16中的四种模式 |
CodedBlockPatternChroma | 如果当前宏块类型采用的预测方式为Intra_16x16,那么该字段有效,它用0~2表示了Chroma宏块中的CBP |
CodedBlockPatternLuma | 如果当前宏块类型采用的预测方式为Intra_16x16,那么该字段有效,它表示了Luma宏块中的CBP。从h.264语法结构分析的residual部分,我们知道当预测模式为Intra_16x16时,宏块需要分开AC level与DC level进行熵编码。0:表示宏块内的16个4x4块中的AC level全部都为0,15:宏块内的16个4x4块中至少有一个块的AC level不全为0 |
P_slice宏块类型
P slice中包含了I宏块类型(I开头)与P宏块类型(P开头)。I宏块类型mb_type数值为5~30,见上面I宏块类型的表格,其序号为mb_type-5。P宏块类型为0~4,见下表
mb_type | name | NumMbPart (mb_type) |
MbPartPredMode (mb_type,0) |
MbPartPredMode (mb_type,1) |
MbPartWidth (mb_type) |
MbPartHeight (mb_type) |
---|---|---|---|---|---|---|
0 | P_L0_16x16 | 1 | Pred_L0 | na | 16 | 16 |
1 | P_L0_L0_16x8 | 2 | Pred_L0 | Pred_L0 | 16 | 8 |
2 | P_L0_L0_8x16 | 3 | Pred_L0 | Pred_L0 | 8 | 16 |
3 | P_8x8 | 4 | na | na | 8 | 8 |
4 | P_8x8ref0 | 4 | na | na | 8 | 8 |
inferred | P_Skip | 1 | Pred_L0 | na | 16 | 16 |
mb_type | 宏块类型的数值,P slice共有31个数值 |
---|---|
name | 宏块类型的名称,其中 - 开头的P表示P宏块类型 - 末尾的mxn代表宏块的分割方式 - P_L0_16x16表示宏块的分割方式为16x16,也就是不进行分割,那么它只有一个前向参考图像L0 - P_L0_L0_16x8表示宏块的的分割方式为16x8,也就是宏块被分成俩个16x8的块,每个16x8的块都有一个前向参考图像L0,即两个L0,按顺序写成P_L0_L0_16x8 - P_L0_L0_16x8表示宏块的的分割方式为8x16,也就是宏块被分成俩个8x16的块,每个8x16的块都有一个前向参考图像L0,即两个L0,按顺序写成P_L0_L0_8x16 - P_8x8表示宏块分成4个8x8的子宏块,对每个子宏块会采用sub_mb_type来表明该子宏块的类型,下面一节会进行分析 - P_8x8ref0表示同上,不过该宏块的4个子宏块采用的参考图像都是ref0,在sub_mb_pred(请参考h.264语法结构分析中的sub_mb_pred部分)语法结构中不会包含他们的refIdx - P_Skip表示该宏块在码流中没有更多的数据了。请注意他的mb_type为inferred,不过它并不在mb_type中表示,而是在slice_data处就已经用skip_run或者skip_flag来表示,请参考h.264语法结构分析中的slice_data部分 |
NumMbPart(mb_type) | 宏块被分割成多少部分 |
MbPartPredMode(mb_type,0) | 宏块分割后的第一部分的预测模式为前向预测,还是后向预测,还是双向预测,由于是P slice,这里只能是前向预测Pred_L0 |
MbPartPredMode(mb_type,1) | 宏块分割后的第二部分的预测模式为前向预测,还是后向预测,还是双向预测,由于是P slice,这里只能是前向预测Pred_L0 |
MbPartWidth(mb_type) | 分割后的块的宽度 |
MbPartHeight(mb_type) | 分割后的块的高度 |
B_slice宏块类型
B slice中包含了I宏块类型(I开头)与B宏块类型(B开头)。I宏块类型mb_type数值为23~48,见上面I宏块类型的表格,其序号为mb_type-23。P宏块类型为0~22,见下表
mb_type | name | NumMbPart (mb_type) |
MbPartPredMode (mb_type,0) |
MbPartPredMode (mb_type,1) |
MbPartWidth (mb_type) |
MbPartHeight (mb_type) |
---|---|---|---|---|---|---|
0 | B_Direct_16x16 | na | Direct | na | 8 | 8 |
1 | B_L0_16x16 | 1 | Pred_L0 | na | 16 | 16 |
2 | B_L1_16x16 | 1 | Pred_L1 | na | 16 | 16 |
3 | B_Bi_16x16 | 1 | BiPred | na | 16 | 16 |
4 | B_L0_L0_16x8 | 2 | Pred_L0 | Pred_L0 | 16 | 8 |
5 | B_L0_L0_8x16 | 2 | Pred_L0 | Pred_L0 | 8 | 16 |
6 | B_L1_L1_16x8 | 2 | Pred_L1 | Pred_L1 | 16 | 8 |
7 | B_L1_L1_8x16 | 2 | Pred_L1 | Pred_L1 | 8 | 16 |
8 | B_L0_L1_16x8 | 2 | Pred_L0 | Pred_L1 | 16 | 8 |
9 | B_L0_L1_8x16 | 2 | Pred_L0 | Pred_L1 | 8 | 16 |
10 | B_L1_L0_16x8 | 2 | Pred_L1 | Pred_L0 | 16 | 8 |
11 | B_L1_L0_8x16 | 2 | Pred_L1 | Pred_L0 | 8 | 16 |
12 | B_L0_Bi_16x8 | 2 | Pred_L0 | BiPred | 16 | 8 |
13 | B_L0_Bi_8x16 | 2 | Pred_L0 | BiPred | 8 | 16 |
14 | B_L1_Bi_16x8 | 2 | Pred_L1 | BiPred | 16 | 8 |
15 | B_L1_Bi_8x16 | 2 | Pred_L1 | BiPred | 8 | 16 |
16 | B_Bi_L0_16x8 | 2 | BiPred | Pred_L0 | 16 | 8 |
17 | B_Bi_L0_8x16 | 2 | BiPred | Pred_L0 | 8 | 16 |
18 | B_Bi_L1_16x8 | 2 | BiPred | Pred_L1 | 16 | 8 |
19 | B_Bi_L1_8x16 | 2 | BiPred | Pred_L1 | 8 | 16 |
20 | B_Bi_Bi_16x8 | 2 | BiPred | BiPred | 16 | 8 |
21 | B_Bi_Bi_8x16 | 2 | BiPred | BiPred | 8 | 16 |
22 | B_8x8 | 4 | na | na | 8 | 8 |
inferred | B_Skip | na | na | 8 | 8 |
mb_type | 宏块类型的数值,P slice共有49个数值 |
---|---|
name | - 开头的B代表B宏块类型 - 末尾的mxn代表宏块的分割方式 - B_Direct_16x16 整个宏块都采用Direct的方式进行预测(请参考h.264直接预测),不需要编码mvd以及refIdx,在解码时重建宏块所用的mv与refIdx靠直接预测进行推导,只需要编码residual(请参考h.264语法结构分析) - B_X0_mxn 当宏块的分割方式为16x16时,意味着宏块不需要进行分割,因此只需要用一个字段(L0:前向预测;L1:后向预测;Bi:双向预测)来表示当前宏块的预测类型 - B_X0_X1_mxn 当前宏块的分割方式为16x8或者8x16时,意味着宏块会被分割成两部分,因此需要用两个字段来分别表示这两个部分的预测类型(如L0_Bi表示第一部分为前向预测,第二部分为双向预测) - B_8x8表示宏块分成4个8x8的子宏块,对每个子宏块会采用sub_mb_type来表明该子宏块的类型,下面一节会进行分析 - B_Skip表示该宏块在码流中没有更多的数据了。请注意他的mb_type为inferred,不过它并不在mb_type中表示,而是在slice_data处就已经用skip_run或者skip_flag来表示,请参考h.264语法结构分析中的slice_data部分 |
NumMbPart(mb_type) | 宏块被分割成多少部分 |
MbPartPredMode(mb_type,0) | 宏块分割后的第一部分的预测模式为前向预测,还是后向预测,还是双向预测 |
MbPartPredMode(mb_type,1) | 宏块分割后的第二部分的预测模式为前向预测,还是后向预测,还是双向预测 |
MbPartWidth(mb_type) | 分割后的块的宽度 |
MbPartHeight(mb_type) | 分割后的块的高度 |
子宏块类型 sub_mb_type
在P Slice和B Slice宏块中,当宏块被分为8x8四个块的时候,这四个子块就叫做子宏块。
子宏块即8x8块,一个16x16的宏块分为4个子宏块,每个子宏块类型表示都是一个8x8块的分割、预测方式,因此,采用子宏块预测的宏块其语法结构中会有4个子宏块类型。在h.264码流结构中,子宏块类型在sub_mb_pred中用sub_mb_type表示(请参考h.264语法结构分析中的sub_mb_pred)。sub_mb_type也是与slice类型相关的,在I slice中是没有子宏块类型的,因为I slice中只包含intra预测,而子宏块类型是inter预测中的部分。另外,数值相同的sub_mb_type在P slice与B slice中表示的是不同的类型。
P_Slice的子宏块类型
P slice的子宏块类型只包含了以下P子宏块类型(P开头),数值为0~3,见下表
sub_mb_type [mbPartIdx ] | name | NumSubMbPart ( sub_mb_type [mbPartIdx ] ) |
SubMbPredMode ( sub_mb_type [mbPartIdx ] ) |
SubMbPartWidth ( sub_mb_type [mbPartIdx ] ) |
SubMbPartHeight ( sub_mb_type [mbPartIdx ] ) |
---|---|---|---|---|---|
inferred | na | na | na | na | na |
0 | P_L0_8x8 | 1 | Pred_L0 | 8 | 8 |
1 | P_L0_8x4 | 2 | Pred_L0 | 8 | 4 |
2 | P_L0_4x8 | 2 | Pred_L0 | 4 | 8 |
3 | P_L0_4x4 | 4 | Pred_L0 | 4 | 4 |
sub_mb_type | 子宏块类型的值 |
---|---|
mbPartIdx | 由于一个宏块可以分割成4个子宏块,因此用这个符号来表示这四个子宏块 |
name | 子宏块类型的名称,其中 - P表示为P子宏块类型 - P_L0_mxn中的L0表示子宏块的预测方式 - P_L0_mxn中的mxn表示子宏块的分割方式 |
NumSubMbPart | 子宏块会被分割成多少部分 |
SubMbPredMode | 子宏块预测模式,由于是P slice,因此只能是前向预测Pred_L0 |
SubMbPartWidth | 分割后的块的宽度 |
SubMbPartHeight | 分割后的块的高度 |
B_Slice的子宏块类型
B slice的子宏块类型只包含了以下B子宏块类型(B开头),数值为0~12,见下表
sub_mb_type [mbPartIdx ] | name | NumSubMbPart ( sub_mb_type [mbPartIdx ] ) |
SubMbPredMode ( sub_mb_type [mbPartIdx ] ) |
SubMbPart Width |
SubMbPart Height |
---|---|---|---|---|---|
inferred | na | na | na | na | na |
0 | B_Direct_8x8 | 4 | Direct | 4 | 4 |
1 | B_L0_8x8 | 1 | Pred_L0 | 8 | 8 |
2 | B_L1_8x8 | 1 | Pred_L1 | 8 | 8 |
3 | B_Bi_8x8 | 1 | BiPred | 8 | 8 |
4 | B_L0_8x4 | 2 | Pred_L0 | 8 | 4 |
5 | B_L0_4x8 | 2 | Pred_L0 | 4 | 8 |
6 | B_L1_8x4 | 2 | Pred_L1 | 8 | 4 |
7 | B_L1_4x8 | 2 | Pred_L1 | 4 | 8 |
8 | B_Bi_8x4 | 2 | BiPred | 8 | 4 |
9 | B_Bi_4x8 | 2 | BiPred | 4 | 8 |
10 | B_L0_4x4 | 4 | Pred_L0 | 4 | 4 |
11 | B_L1_4x4 | 4 | Pred_L1 | 4 | 4 |
12 | B_Bi_4x4 | 4 | BiPred | 4 | 4 |
sub_mb_type | 子宏块类型的值 |
---|---|
mbPartIdx | 由于一个宏块可以分割成4个子宏块,因此用这个符号来表示这四个子宏块 |
name | 子宏块类型的名称,其中 - B表示为B子宏块类型 - B_X0_mxn中的X0表示子宏块的预测模式,由于参考图像索引refIdx是以8x8块为单位的,因此一个子宏块中的所有子块共用参考图像索引(请参考h.264语法结构分析中的sub_mb_pred部分),也就是说整个子宏块的预测模式只能是是前向预测Pred_L0、后向预测Pred_L1、双向预测BiPred中的某一种。 - B_X0_mxn中的mxn表示子宏块的分割方式 |
NumSubMbPart | 子宏块会被分割成多少部分 |
SubMbPredMode | 子宏块预测模式,由于是B slice,因此预测模式相比P slice的前向预测Pred_L0增加了后向预测Pred_L1以及双向预测BiPred中。 |
SubMbPartWidth | 分割后的块的宽度 |
SubMbPartHeight | 分割后的块的高度 |