1. 引言
前一篇搞完了最重要SPS,接下来就是PPS了。
PPS(picture parameter set)也是一份很重要的数据,会被很多slice参考到,里面的参数我们也需要熟悉和理解。
PPS的解析方式和SPS类似,没有什么很复杂的地方,分支也不多。基本上按照指数哥伦布的解析方式,按照白皮书依次挨个解析下去即可,所以我们主要要注意的就是里面每个参数的含义,以及他们的使用场合。
2. PPS
翻到白皮书《Rec. ITU-T H.264 (03/2010) 46面。 章节 “7.3.2.2 Picture parameter set RBSP syntax”,可以看出来这个表不是非常长,参数有限,依然先上一个原版全家福。
下面我们来挨个理解每个参数的作用。
3. 参数解析
-
pic_parameter_set_id: 当前PPS的id号,范围0~255,用于slice 参考时指定使用。
-
seq_parameter_set_id:指定当前PPS所参考的SPS。
-
entropy_coding_mode_flag:熵编码类型,0 是 CAVLC,1是CABAC。CAVLC编解码简单,但是效率没有CABAC高。
-
bottom_field_pic_order_in_frame_present_flag:标识位,用于表示片头中的两个语法元素delta_pic_order_cnt_bottom和delta_pic_order_cn是否存在,用于计算POC。
具体POC的计算流程图详见《新一代视频压缩编码标准》P185。 -
num_slice_groups_minus1:片组的数量,为0时表示没有使用片组模式。H264规定每张影像最多分成8个片组。
注意,这里并不是说slice片的数量,而是片组。一帧中可能会有多个片,但是他们属于同一个片组。 -
slice_group_map_type:片组的类型。片组这又是一个单独的概念了,主要用于恢复重建的作用。
-
run_length_minus1:
-
top_left
-
bottom_right
-
slice_group_change_direction_flag
-
slice_group_change_rate_minus1
-
pic_size_in_map_units_minus1
-
slice_group_id
以上这些都是针对不同的片组类型,用于计算MB与slice group的归属关系。
slice group还有一个名字就是 FMO(Flexibility Macroblock Order)。
这一块比较多,就不在这展开写了,以后单独开一章单独介绍。
-
num_ref_idx_l0_default_active_minus1:minus1是计算方式的后缀,num_ref_idx_l0_default_active是指明当前参考队列list0中有多少有效的参考帧。
-
num_ref_idx_l1_default_active_minus1:指明当前参考队列list1中有多少有效的参考帧。
-
weighted_pred_flag:是否允许P和SP的加权预测,0:不允许,1允许。
-
weighted_bipred_idc:指明B的加权预测模式,0:默认加权,1:显式加权,2:隐式加权
-
pic_init_qp_minus26:范围[-26,25],用于计算宏块的qp。
宏块的qp在PPS(pic_init_qp_minus26),slice header (slice_qp_delta)和 宏块层(qp_mb_delta)中都有涉及。
片中第一个MB的qp计算为QP y = 26 + pic_init_qp_minus26 + slice_qp_delta
后续的MB的qp都是基于上一个MB的qp计算得到
QP y = (QP y,prev + qp_mb_delta + 52 ) % 52
-
pic_init_qs_minus26:和pic_init_qp_minus26基本一致,主要用于SP和SI。
-
chroma_qp_index_offset:范围[-12,12],用于基于亮度分量的qp来计算色度分量的qp。详见《新一代视频压缩编码标准》P219,主要用于Cb
-
deblocking_filter_control_present_flag : 表明去块效应滤波器的语法元素是否在slice header中出现。1:出现,使用设定值;0:不出现,使用预测值。
-
constrained_intra_pred_flag:用于标志在intra的宏块预测中,是否可以使用相邻的inter块的数据。1的话表示不行,0的话表示可以,这个在后面intra宏块的时候会细说,会细分出来很多种可能。
-
redundant_pic_cnt_present_flag:指明slice header中是否会出现redundant_pic_cnt 元素。
-
transform_8x8_mode_flag:是否使用8x8的size。在baseline中,intra只有16x16和4x4的size,后面加入了8x8,这个增加了灵活度,但是在MB解算的时候多了一堆可能性。这个在后面intra宏块的时候会细说。
-
pic_scaling_matrix_present_flag:指明pic_scaling_matrix是否存在
-
pic_scaling_list_present_flag:指明 pic_scaling_list是否存在
关于 pic_scaling_matrix 和 pic_scaling_list的使用方法,我暂时没找到说明。后续会补充这里。 -
second_chroma_qp_index_offset:功能和chroma_qp_index_offset类似,主要用于Cr。
4. 伪代码解析
解析 pic_parameter_set_id
解析 seq_parameter_set_id
解析 entropy_coding_mode_flag
解析 bottom_field_pic_order_in_frame_present_flag
解析 num_slice_groups_minus1
|
if (num_slice_groups_minus1 > 0) 如果存在片组
解析 slice_group_map_type 获取片组的类型
if (slice_group_map_type == 0 ) 如果片组是类型0,根据run_length_minus1来分片组
for (iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++)
run_length_minus1[iGroup] 读取每个片组的run_length,取出run_length个map unit之后就切换成下一个片组
else if( slice_group_map_type == 2 ) 如果片组是类型2,
for( iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) {
top_left[iGroup] 读取top_left和bottom_right确定每个片组的矩形的位置。
bottom_right[iGroup] 片组重叠位置的map unit归于片组id小的那个
}
else if( slice_group_map_type = = 3 || 如果是类型3,4,5,都是只分2个group。
slice_group_map_type = = 4 ||
slice_group_map_type = = 5 ) {
slice_group_change_direction_flag 指明是顺时针还是逆时针
slice_group_change_rate_minus1
} else if( slice_group_map_type = = 6 ) { 如果是类型6, 这个规律性比较低,所以要传很多参数,完全是encoder自行决定 MapUnit和 slice group的对应关系
pic_size_in_map_units_minus1 指明图像中有多少个map unit,虽然从SPS中也能算出来,但是有时候,PPS比SPS先到。所以再传一次。
for( i = 0; i <= pic_size_in_map_units_minus1; i++ )
slice_group_id[i] 每一个map unit都要指明是属于哪个片组的
}
}
|
解析 num_ref_idx_l0_default_active_minus1
解析 num_ref_idx_l1_default_active_minus1
解析 weighted_pred_flag
解析 weighted_bipred_idc
解析 pic_init_qp_minus26
解析 pic_init_qs_minus26
解析 chroma_qp_index_offset
解析 deblocking_filter_control_present_flag
解析 constrained_intra_pred_flag
解析 redundant_pic_cnt_present_flag
|
if(more_rbsp_data()) { 如果这个NALU的数据还没读取完
解析 transform_8x8_mode_flag
解析 pic_scaling_matrix_present_flag
if( pic_scaling_matrix_present_flag ) 如果 pic_scaling_matrix 存在
for( i = 0; i < 6+((chroma_format_idc!= 3) ? 2 : 6 ) * transform_8x8_mode_flag; i++ ) {
解析 pic_scaling_list_present_flag[i]
if(pic_scaling_list_present_flag[i])
if( i < 6 )
scaling_list(ScalingList4x4[i], 16, UseDefaultScalingMatrix4x4Flag[i])
else
scaling_list(ScalingList8x8[i−6], 64, UseDefaultScalingMatrix8x8Flag[i−6])
}
解析second_chroma_qp_index_offset
}
5. 小总结
SPS里的参数都也是比较重要的,但是解析起来都比较好懂。
个人感觉片组和scalelist会相对冷僻一些,要再花点心思。