slice_header( ) {
// first_mb_in_slice 片中的第一个宏块的地址, 片通过这个句法元素来标定它自己的地址。 要注意的是在帧场自适应模式下,宏块都是成对出现,这时本句法元素表示的是第几个宏块对,对应的第一个宏块的真实地址应该是2 * first_mb_in_slice
first_mb_in_slice
/* slice_type 指明片的类型
slice_type Name of slice_type
0 P (P slice)
1 B (B slice)
2 I (I slice)
3 SP (SP slice)
4 SI (SI slice)
5 P (P slice)
6 B (B slice)
7 I (I slice)
8 SP (SP slice)
9 SI (SI slice) */
slice_type
// pic_parameter_set_id 图像参数集的索引号. 范围 0 到 255。
pic_parameter_set_id
// frame_num 每个参考帧都有一个依次连续的 frame_num 作为它们的标识,这指明了各图像的解码顺序。但事实上我们可以看到,frame_num 的出现没有 if 语句限定条件,这表明非参考帧的片头也会出现 frame_num。只是当该个图像是参考帧时,它所携带的这个句法元素在解码时才有意义。
H.264 对 frame_num的值作了如下规定:当参数集中的句法元素gaps_in_frame_num_value_allowed_flag 不为1 时,每个图像的 frame_num 值是它前一个参考帧的frame_num 值增加 1。这句话包含有两层意思:
1) 当 gaps_in_frame_num_value_allowed_flag 不为 1,即 frame_num 连续的情况下,每个图像的frame_num 由前一个参考帧图像对应的值加 1,着重点是“前一个参考帧”。
前面我们曾经提到,对于非参考帧来说,它的 frame_num 值在解码过程中是没有意义的,因为frame_num 值是参考帧特有的,它的主要作用是在该图像被其他图像引用作运动补偿的参考时提供一个标识。但 H.264 并没有在非参考帧图像中取消这一句法元素,原因是在 POC 的第二种和第三种解码方法中可以通过非参考帧的 frame_num 值计算出他们的 POC 值。
2) 当 gaps_in_frame_num_value_allowed_flag 等于 1,前文已经提到,这时若网络阻塞,编码器可以将编码后的若干图像丢弃,而不用另行通知解码器。在这种情况下,解码器必须有机制将缺失的frame_num 及所对应的图像填补,否则后续图像若将运动矢量指向缺失的图像将会产生解码错误。
frame_num
if( !frame_mbs_only_flag ) {
// field_pic_flag 这是在片层标识图像编码模式的唯一一个句法元素。所谓的编码模式是指的帧编码、场编码、帧场自适应编码。当这个句法元素取值为 1 时 属于场编码; 0 时为非场编码。
field_pic_flag
if( field_pic_flag )
// bottom_field_flag 等于 1 时表示当前图像是属于底场;等于 0 时表示当前图像是属于顶场。
bottom_field_flag
}
if( nal_unit_type == 5 )
// idr_pic_id IDR 图像的标识。不同的 IDR 图像有不同的 idr_pic_id 值。值得注意的是,IDR 图像有不等价于 I 图像,只有在作为 IDR 图像的 I 帧才有这个句法元素,在场模式下,IDR 帧的两个场有相同的 idr_pic_id 值。idr_pic_id 的取值范围是 [0,65535],和 frame_num 类似,当它的值超出这个范围时,它会以循环的方式重新开始计数。
idr_pic_id
if( pic_order_cnt_type == 0 ) {
// pic_order_cnt_lsb 在 POC 的第一种算法中本句法元素来计算 POC 值,在 POC 的第一种算法中是显式地传递 POC 的值,而其他两种算法是通过 frame_num 来映射 POC 的值。
pic_order_cnt_lsb
if( pic_order_present_flag && !field_pic_flag )
// delta_pic_order_cnt_bottom 如果是在场模式下,场对中的两个场都各自被构造为一个图像,它们有各自的 POC 算法来分别计算两个场的 POC 值,也就是一个场对拥有一对 POC 值;而在是帧模式或是帧场自适应模式下,一个图像只能根据片头的句法元素计算出一个 POC 值。根据 H.264 的规定,在序列中有可能出现场的情况,即 frame_mbs_only_flag 不为 1 时,每个帧或帧场自适应的图像在解码完后必须分解为两个场,以供后续图像中的场作为参考图像。所以当 frame_mb_only_flag 不为 1时,帧或帧场自适应中包含的两个场也必须有各自的 POC 值。通过本句法元素,可以在已经解开的帧或帧场自适应图像的 POC 基础上新映射一个 POC 值,并把它赋给底场。当然,象句法表指出的那样,这个句法元素只用在 POC 的第一个算法中。
delta_pic_order_cnt_bottom
}
if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
// delta_pic_order_cnt[0], delta_pic_order_cnt[1]:POC 的第二和第三种算法是从 frame_num 映射得来,这两个句法元素用于映射算法。delta_pic_order_cnt[0]用于帧编码方式下的底场和场编码方式的场,delta_pic_order_cnt[1] 用于帧编码方式下的顶场。
delta_pic_order_cnt[ 0 ]
if( pic_order_present_flag && !field_pic_flag )
delta_pic_order_cnt[ 1 ]
}
if( redundant_pic_cnt_present_flag )
// redundant_pic_cnt 冗余片的 id 号。
redundant_pic_cnt
if( slice_type == B )
// direct_spatial_mv_pred_flag 指出在B图像的直接预测的模式下,用时间预测还是用空间预测。1:空间预测;0:时间预测。
direct_spatial_mv_pred_flag
if( slice_type = = P | | slice_type = = SP | | slice_type = = B ) {
// num_ref_idx_active_override_flag 在图像参数集中我们看到已经出现句法元素num_ref_idx_l0_active_minus1 和num_ref_idx_l1_active_minus1 指定当前参考帧队列中实际可用的参考帧的数目。在片头可以重载这对句法元素,以给某特定图像更大的灵活度。这个句法元素就是指明片头是否会重载,如果该句法元素等于 1,下面会出现新的 num_ref_idx_l0_active_minus1 和num_ref_idx_l1_active_minus1 值。
num_ref_idx_active_override_flag
if( num_ref_idx_active_override_flag ) {
num_ref_idx_l0_active_minus1
if( slice_type == B )
num_ref_idx_l1_active_minus1
}
}
// 参考帧队列重排序(reordering)句法
ref_pic_list_reordering( )
if( ( weighted_pred_flag && ( slice_type == P | | slice_type == SP ) ) | |
( weighted_bipred_idc == 1 && slice_type == B ) )
// 加权预测句法
pred_weight_table( )
if( nal_ref_idc != 0 )
// 参考帧队列标记(marking)句法
dec_ref_pic_marking( )
if( entropy_coding_mode_flag && slice_type != I && slice_type != SI )
// cabac_init_idc 给出 cabac 初始化时表格的选择,范围 0 到 2。
cabac_init_idc
// slice_qp_delta 指出在用于当前片的所有宏块的量化参数的初始值。SliceQPY = 26+ pic_init_qp_minus26 + slice_qp_delta 范围是 0 to 51。 H.264 中量化参数是分图像参数集、片头、宏块头三层给出的,前两层各自给出一个偏移值,这个句法元素就是片层的偏移。
slice_qp_delta
if( slice_type = = SP | | slice_type = = SI ) {
if( slice_type = = SP )
// sp_for_switch_flag 指出SP 帧中的p 宏块的解码方式是否是switching 模式
sp_for_switch_flag
// slice_qs_delta 与 slice_qp_delta 的与语义相似,用在 SI 和 SP 中的
slice_qs_delta
}
if( deblocking_filter_control_present_flag ) {
// disable_deblocking_filter_idc H.264 指定了一套算法可以在解码器端独立地计算图像中各边界的滤波强度进行滤波。除了解码器独立计算之外,编码器也可以传递句法元素来干涉滤波强度,当这个句法元素指定了在块的边界是否要用滤波,同时指明那个块的边界不用块滤波
disable_deblocking_filter_idc
if( disable_deblocking_filter_idc != 1 ) {
// slice_alpha/beta_c0_offset_div2 给出用于增强 α/beta 和 t C0 的偏移值
slice_alpha_c0_offset_div2
slice_beta_offset_div2
}
}
if( num_slice_groups_minus1 > 0 &&
slice_group_map_type >= 3 && slice_group_map_type <= 5)
// slice_group_change_cycle 当片组的类型是 3, 4, 5,由句法元素可获得片组中 映射单元的数目:
slice_group_change_cycle
}
// first_mb_in_slice 片中的第一个宏块的地址, 片通过这个句法元素来标定它自己的地址。 要注意的是在帧场自适应模式下,宏块都是成对出现,这时本句法元素表示的是第几个宏块对,对应的第一个宏块的真实地址应该是2 * first_mb_in_slice
first_mb_in_slice
/* slice_type 指明片的类型
slice_type Name of slice_type
0 P (P slice)
1 B (B slice)
2 I (I slice)
3 SP (SP slice)
4 SI (SI slice)
5 P (P slice)
6 B (B slice)
7 I (I slice)
8 SP (SP slice)
9 SI (SI slice) */
slice_type
// pic_parameter_set_id 图像参数集的索引号. 范围 0 到 255。
pic_parameter_set_id
// frame_num 每个参考帧都有一个依次连续的 frame_num 作为它们的标识,这指明了各图像的解码顺序。但事实上我们可以看到,frame_num 的出现没有 if 语句限定条件,这表明非参考帧的片头也会出现 frame_num。只是当该个图像是参考帧时,它所携带的这个句法元素在解码时才有意义。
H.264 对 frame_num的值作了如下规定:当参数集中的句法元素gaps_in_frame_num_value_allowed_flag 不为1 时,每个图像的 frame_num 值是它前一个参考帧的frame_num 值增加 1。这句话包含有两层意思:
1) 当 gaps_in_frame_num_value_allowed_flag 不为 1,即 frame_num 连续的情况下,每个图像的frame_num 由前一个参考帧图像对应的值加 1,着重点是“前一个参考帧”。
前面我们曾经提到,对于非参考帧来说,它的 frame_num 值在解码过程中是没有意义的,因为frame_num 值是参考帧特有的,它的主要作用是在该图像被其他图像引用作运动补偿的参考时提供一个标识。但 H.264 并没有在非参考帧图像中取消这一句法元素,原因是在 POC 的第二种和第三种解码方法中可以通过非参考帧的 frame_num 值计算出他们的 POC 值。
2) 当 gaps_in_frame_num_value_allowed_flag 等于 1,前文已经提到,这时若网络阻塞,编码器可以将编码后的若干图像丢弃,而不用另行通知解码器。在这种情况下,解码器必须有机制将缺失的frame_num 及所对应的图像填补,否则后续图像若将运动矢量指向缺失的图像将会产生解码错误。
frame_num
if( !frame_mbs_only_flag ) {
// field_pic_flag 这是在片层标识图像编码模式的唯一一个句法元素。所谓的编码模式是指的帧编码、场编码、帧场自适应编码。当这个句法元素取值为 1 时 属于场编码; 0 时为非场编码。
field_pic_flag
if( field_pic_flag )
// bottom_field_flag 等于 1 时表示当前图像是属于底场;等于 0 时表示当前图像是属于顶场。
bottom_field_flag
}
if( nal_unit_type == 5 )
// idr_pic_id IDR 图像的标识。不同的 IDR 图像有不同的 idr_pic_id 值。值得注意的是,IDR 图像有不等价于 I 图像,只有在作为 IDR 图像的 I 帧才有这个句法元素,在场模式下,IDR 帧的两个场有相同的 idr_pic_id 值。idr_pic_id 的取值范围是 [0,65535],和 frame_num 类似,当它的值超出这个范围时,它会以循环的方式重新开始计数。
idr_pic_id
if( pic_order_cnt_type == 0 ) {
// pic_order_cnt_lsb 在 POC 的第一种算法中本句法元素来计算 POC 值,在 POC 的第一种算法中是显式地传递 POC 的值,而其他两种算法是通过 frame_num 来映射 POC 的值。
pic_order_cnt_lsb
if( pic_order_present_flag && !field_pic_flag )
// delta_pic_order_cnt_bottom 如果是在场模式下,场对中的两个场都各自被构造为一个图像,它们有各自的 POC 算法来分别计算两个场的 POC 值,也就是一个场对拥有一对 POC 值;而在是帧模式或是帧场自适应模式下,一个图像只能根据片头的句法元素计算出一个 POC 值。根据 H.264 的规定,在序列中有可能出现场的情况,即 frame_mbs_only_flag 不为 1 时,每个帧或帧场自适应的图像在解码完后必须分解为两个场,以供后续图像中的场作为参考图像。所以当 frame_mb_only_flag 不为 1时,帧或帧场自适应中包含的两个场也必须有各自的 POC 值。通过本句法元素,可以在已经解开的帧或帧场自适应图像的 POC 基础上新映射一个 POC 值,并把它赋给底场。当然,象句法表指出的那样,这个句法元素只用在 POC 的第一个算法中。
delta_pic_order_cnt_bottom
}
if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
// delta_pic_order_cnt[0], delta_pic_order_cnt[1]:POC 的第二和第三种算法是从 frame_num 映射得来,这两个句法元素用于映射算法。delta_pic_order_cnt[0]用于帧编码方式下的底场和场编码方式的场,delta_pic_order_cnt[1] 用于帧编码方式下的顶场。
delta_pic_order_cnt[ 0 ]
if( pic_order_present_flag && !field_pic_flag )
delta_pic_order_cnt[ 1 ]
}
if( redundant_pic_cnt_present_flag )
// redundant_pic_cnt 冗余片的 id 号。
redundant_pic_cnt
if( slice_type == B )
// direct_spatial_mv_pred_flag 指出在B图像的直接预测的模式下,用时间预测还是用空间预测。1:空间预测;0:时间预测。
direct_spatial_mv_pred_flag
if( slice_type = = P | | slice_type = = SP | | slice_type = = B ) {
// num_ref_idx_active_override_flag 在图像参数集中我们看到已经出现句法元素num_ref_idx_l0_active_minus1 和num_ref_idx_l1_active_minus1 指定当前参考帧队列中实际可用的参考帧的数目。在片头可以重载这对句法元素,以给某特定图像更大的灵活度。这个句法元素就是指明片头是否会重载,如果该句法元素等于 1,下面会出现新的 num_ref_idx_l0_active_minus1 和num_ref_idx_l1_active_minus1 值。
num_ref_idx_active_override_flag
if( num_ref_idx_active_override_flag ) {
num_ref_idx_l0_active_minus1
if( slice_type == B )
num_ref_idx_l1_active_minus1
}
}
// 参考帧队列重排序(reordering)句法
ref_pic_list_reordering( )
if( ( weighted_pred_flag && ( slice_type == P | | slice_type == SP ) ) | |
( weighted_bipred_idc == 1 && slice_type == B ) )
// 加权预测句法
pred_weight_table( )
if( nal_ref_idc != 0 )
// 参考帧队列标记(marking)句法
dec_ref_pic_marking( )
if( entropy_coding_mode_flag && slice_type != I && slice_type != SI )
// cabac_init_idc 给出 cabac 初始化时表格的选择,范围 0 到 2。
cabac_init_idc
// slice_qp_delta 指出在用于当前片的所有宏块的量化参数的初始值。SliceQPY = 26+ pic_init_qp_minus26 + slice_qp_delta 范围是 0 to 51。 H.264 中量化参数是分图像参数集、片头、宏块头三层给出的,前两层各自给出一个偏移值,这个句法元素就是片层的偏移。
slice_qp_delta
if( slice_type = = SP | | slice_type = = SI ) {
if( slice_type = = SP )
// sp_for_switch_flag 指出SP 帧中的p 宏块的解码方式是否是switching 模式
sp_for_switch_flag
// slice_qs_delta 与 slice_qp_delta 的与语义相似,用在 SI 和 SP 中的
slice_qs_delta
}
if( deblocking_filter_control_present_flag ) {
// disable_deblocking_filter_idc H.264 指定了一套算法可以在解码器端独立地计算图像中各边界的滤波强度进行滤波。除了解码器独立计算之外,编码器也可以传递句法元素来干涉滤波强度,当这个句法元素指定了在块的边界是否要用滤波,同时指明那个块的边界不用块滤波
disable_deblocking_filter_idc
if( disable_deblocking_filter_idc != 1 ) {
// slice_alpha/beta_c0_offset_div2 给出用于增强 α/beta 和 t C0 的偏移值
slice_alpha_c0_offset_div2
slice_beta_offset_div2
}
}
if( num_slice_groups_minus1 > 0 &&
slice_group_map_type >= 3 && slice_group_map_type <= 5)
// slice_group_change_cycle 当片组的类型是 3, 4, 5,由句法元素可获得片组中 映射单元的数目:
slice_group_change_cycle
}