【H.264/AVC 句法和语义详解】(十一):Slice_Header的句法和语义

前面我们根据SPS和PPS的句法和语义,已经能够解析出h264码流文件中的前两个NALU。现在我们就开始解析第三个NALU,它同码流中的后面几个NALU一样,它们的nal_unit_type不是等于5,就是等于1。

因此它们不是IDR的slice,就是非IDR的slice,总之后面的几个NALU,都代表了片层的数据。而它们的句法元素,都包含在slice_layer_without_partitioning_rbsp()中,由表7-1可以查到。
【H.264/AVC 句法和语义详解】(十一):Slice_Header的句法和语义_第1张图片

1、slice_layer()

在h264协议文档7.3.2.8节,我们可以看到slice_layer()的结构如下:

/*
7.3.2.8 Slice layer without partitioning RBSP syntax
*/
slice_layer_without_partitioning_rbsp( ) {
     
   slice_header( )
   slice_data( ) /* all categories of slice_data( ) syntax */
   rbsp_slice_trailing_bits( )
}

它主要包含了slice_header()slice_data()两部分,slice_header()保存了这个slice解码所需的一些全局性信息,slice_data()则真正保存了这个slice的宏块数据。

在解析完slice_header()之后,我们很多解码准备性的工作就可以开展了。比如激活这个slice所指向的PPS和SPS,确定编码的帧场模式,计算视频宽高,计算POC等。

因此我们接下来几篇的重点,就围绕这几个话题来开展。一旦这些准备性工作完成,我们就可以从slice_data()中解析宏块数据,然后就可以嗨皮的去解码了。

2、slice_header()句法结构

slice_header()的结构定义在7.3.3 Slice header syntax一节中,其中的句法元素所表达的功能,主要分为以下几点:

slice_header() {
     
   // 1. 指定当前slice类型
   // 2. 当前slice所引用的pps_id
   // 3. 帧场编码模式
   // 4. 用于计算POC的几个句法元素
   // 5. B Slice和P Slice所需的参考图像列表和加权预测表
   // 6. 量化初始值
   // 7. SP和SI 条带相关的句法元素
   // 8. 去块效应滤波器相关句法元素
   // 9. FMO相关
}

联系上篇最简单的H264编解码器,可知其中5、7、9都是暂时不会涉及到的,剩下的几点功能中,1、2、3、4是马上就能用到的,而6、8要再后面一点才会涉及到。

因此在下面的语义解析中,我们要把精力先主要放在前四点中。为此我们先来浏览一下,这八点功能对应的句法元素的划分:

slice_header( ) {
     
   first_mb_in_slice
   // 1. 指定当前slice类型
   slice_type
   // 2. 当前slice所引用的pps_id
   pic_parameter_set_id
 
   if( separate_colour_plane_flag = = 1 )
       colour_plane_id

/*  —————————— 4. 用于计算POC的几个句法元素 Start  —————————— */
   frame_num

/*  —————————— 3. 帧场编码模式 Start  —————————— */
   if( !frame_mbs_only_flag ) {
     
       field_pic_flag
       if( field_pic_flag ) 
           bottom_field_flag
 }
/*  —————————— 3. 帧场编码模式 End  —————————— */

   if( IdrPicFlag )
       idr_pic_id
   if( pic_order_cnt_type = = 0 ) {
     
       pic_order_cnt_lsb
       if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag )
           delta_pic_order_cnt_bottom
   }

   if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
     
       delta_pic_order_cnt[ 0 ]
       if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag ) 
           delta_pic_order_cnt[ 1 ] 
 }
/*  —————————— 4. 用于计算POC的几个句法元素 End  —————————— */

   if( redundant_pic_cnt_present_flag ) 
       redundant_pic_cnt 
   if( slice_type = = B ) 
       direct_spatial_mv_pred_flag 

/*  ———— 5. B Slice和P Slice所需的参考图像列表和加权预测表 Start  ————— */
   if(slice_type == P || slice_type == SP || slice_type == B){
      
       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 
       } 
   } 

   if( nal_unit_type = = 20 | | nal_unit_type = = 21 )
       ref_pic_list_mvc_modification( ) /* specified in Annex H */ 
   else
       ref_pic_list_modification( ) 
 
   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 )
       dec_ref_pic_marking( ) 
/*  ———— 5. B Slice和P Slice所需的参考图像列表和加权预测表 End  ————— */

   if( entropy_coding_mode_flag && slice_type != I && slice_type != SI ) 
       cabac_init_idc

   // 6. 量化初始值  
   slice_qp_delta

   // 7. SP和SI 条带相关的句法元素  
   if(slice_type == SP || slice_type == SI){
      
       if( slice_type = = SP ) 
           sp_for_switch_flag
       slice_qs_delta 
   }
 
   // 8.  去块效应滤波器相关句法元素
   if( deblocking_filter_control_present_flag ) {
      
       disable_deblocking_filter_idc 
       if( disable_deblocking_filter_idc != 1 ) {
      
           slice_alpha_c0_offset_div2 
           slice_beta_offset_div2 
       }
   } 

   // 9. FMO相关
   if( num_slice_groups_minus1 > 0 &&
       slice_group_map_type >= 3 && slice_group_map_type <= 5) 
       slice_group_change_cycle 
}

其中第5点,B片、P片的参考图像列表和加权预测表,使用了额外的三个语法结构。

3、slice_header()语义

slice_header()的语义在h264协议文档的7.4.3节介绍,这里详细解释如下:

slice_header( ) {
     
   /*
   表示在Slice中第一个宏块的地址,在目前我们不使用FMO时,一幅图像只有一个Slice,因此这个值通常为0。
   要注意的是,first_mb_in_slice并不是直接表示为第一个宏块的 
   地址的,具体如下:
   当MbaffFrameFlag为0时,first_mb_in_slice就是该Slice中
   第一个宏块的地址,并且first_mb_in_slice的取值范围为[0, PicSizeInMbs-1]
   否则(MbaffFrameFlag为1时),first_mb_in_slice表示的是
   Slice中的第几个宏块对,因此第一个宏块的地址为first_mb_in_slice * 2,
   并且此时first_mb_in_slice的取值范围为[0, PicSizeInMbs/2-1]

   其中判断的依据MbaffFrameFlag,表示是否为宏块级的帧场自适应,它的值需要经另外两个句法元素推算得来。
   */
   first_mb_in_slice

   // 1. 指定当前slice类型
   /*
   表示当前Slice的编码类型,它的取值范围为[0, 9],可参见表7-6
   我们已经知道Slice的类型总共只有五种,因此slice_type值为0~4
   时,依次表示当前Slice为P、B、I、SP、SI, 而slice_type值为
   5~9时,依然顺序表示这五种slice类型。因此在解码器中,如果 
   slice_type大于4,则做减5处理。
   */
   slice_type

   // 2. 当前slice所引用的pps_id
   /*
   表示当前Slice引用的PPS的pps_id,同PPS语义一篇一样,它的取值范围为[0, 255]
   */
   pic_parameter_set_id
 
   if( separate_colour_plane_flag = = 1 )
       /*
       这一句法元素需要结合SPS中的separate_colour_plane_flag的语义来理解,  
       separate_colour_plane_flag等于1表示三个颜色分量Y、Cb、Cr分别进行编码,
       而colour_plane_id就表示当前Slice是对哪一个颜色分量进 
       行编码的。
       因此colour_plane_id的取值范围为[0, 2],0、1、2分别表示Y、Cb、Cr。
       */
       colour_plane_id

/*  —————————— 4. 用于计算POC的几个句法元素 Start  —————————— */
   /*
   表示一个图像的标识符,在后面我们会单开几篇,用于讲解POC的计 
   算,
   那时会对这个句法元素有非常深刻的理解。此时我们只需知道,
   它的最大值是由SPS中的log2_max_frame_num_minus4决定,
   并且frame_num的解析方式为u(v),其中的v即是指(log2_max_frame_num_minus4 + 4)
   */
   frame_num


/*  —————————— 3. 帧场编码模式 Start  —————————— */
   /*
   以下两个句法元素field_pic_flag和bottom_field_flag,
   和SPS中的frame_mbs_only_flag、 
   mb_adaptive_frame_field_flag一起,决定了当前Slice的编码模式。
   而所谓编码模式,即指当前Slice用的是帧编码、还是场编码,如果是场编码,
   那么当前Slice位于顶场,还是底场。关于这点,我们会单开一小篇来介绍。
   在编码模式确定后,视频的宽高、当前图像(当前图像可能是帧、可能是场)的宽高,就可以计算出来了。
   */
   if( !frame_mbs_only_flag ) {
     
       field_pic_flag
       if( field_pic_flag )
           bottom_field_flag
   }
/*  —————————— 3. 帧场编码模式 End  —————————— */

   if( IdrPicFlag )
       /*
       IDR图像的标识,意思是只有作为IDR图像的I帧才有这个句法元素,
       并且不同的IDR图像有不同的idr_pic_id值,如果编码模式为场模式,那么IDR帧的两个场的idr_pic_id值相同。
       idr_pic_id的取值范围为[0, 65535],如果它的值超出65535,它会以循环的方式重新开始计数。
       */
       idr_pic_id

   if( pic_order_cnt_type = = 0 ) {
     
       /*
       在poc_type为0时,用于计算poc。它的值为编码帧的顶场,或
       一个编码场的poc值对MaxPicOrderCntLsb取模得出,因此它直接传递了POC的值。
       pic_order_cnt_lsb的解析方式为u(v),其中的v即为SPS中的(log2_max_pic_order_cnt_lsb_minus4 + 4)
       而pic_order_cnt_lsb的取值范围为:[0, MaxPicOrderCntLsb-1]
       */
       pic_order_cnt_lsb

       if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag )
           /*
           表示一个编码帧的底场和顶场之间的poc的差值,它的取值范围为:
           如果当前图像包含一个等于5的memory_management_control_operation,
           那么它的取值范围为:[1 – MaxPicOrderCntLsb, 2^31 – 1]
           否则它的取值范围为:[-2^31, 2^31 - 1]
           当此句法元素不存在时,默认为0
           */
           delta_pic_order_cnt_bottom
   }
 
   if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
     
       /*
       表示POC值与经计算得出的,编码帧的顶场或编码场的,预期POC值之间的差值,
       它的取值范围为[-2^31, 2^31 - 1],当此句法元素不存在时,默认为0
       */
       delta_pic_order_cnt[ 0 ]
       if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag ) 
           /*      
           与delta_pic_order_cnt[ 0 ]类似,只不过delta_pic_order_cnt[ 1 ]表示的是POC值与
           经计算得出的,编码帧的底场的,预期POC值之间的差值,它的取值范围同样为[-2^31, 2^31 - 1],
           当此句法元素不存在时,默认为0
           */
           delta_pic_order_cnt[ 1 ] 
   }
/*  —————————— 4. 用于计算POC的几个句法元素 End  —————————— */

   if( redundant_pic_cnt_present_flag ) 
       // 冗余片的id号,取值范围为[0, 127]
       redundant_pic_cnt 
   if( slice_type = = B ) 
       // 表示在B片下,决定为得到帧间预测的运动矢量和参考序号而使用的方法,当然暂时也用不到
       direct_spatial_mv_pred_flag 


/*  ———— 5. B Slice和P Slice所需的参考图像列表和加权预测表 Start  ————— */
   if(slice_type == P || slice_type == SP || slice_type == B){
      
       /*
       此句法元素用于控制,num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1是否要覆盖在PPS中的默认值:
num_ref_idx_l0_default_active_minus1和num_ref_idx_l1_default_active_minus1。
       因此如果num_ref_idx_active_override_flag为1,则覆盖,
       否则则使用num_ref_idx_l0_default_active_minus1和num_ref_idx_l1_default_active_minus1
       在以下两种情况下,num_ref_idx_active_override_flag的值为1
       当当前Slice为P、SP或B Slice,且field_pic_flag等于0,
       且num_ref_idx_l0_default_active_minus1的值大于15时,num_ref_idx_active_override_flag的值将为1
       当当前Slice为B Slice,且field_pic_flag等于0,且
       num_ref_idx_l1_default_active_minus1的值大于15时,num_ref_idx_active_override_flag的值将为1
       */
       num_ref_idx_active_override_flag 
       if( num_ref_idx_active_override_flag ) {
      
           /*
           表示用于解码该Slice的参考图像列表 0 的最大参考序号。 
           它的取值范围为:如果field_pic_flag等于0,取值范围为:[0, 15],否则为[0, 31]
           */
           num_ref_idx_l0_active_minus1 
           if( slice_type = = B ) 
               // 与num_ref_idx_l0_active_minus1语义一致,只不过表示的是参考图像列表1
               num_ref_idx_l1_active_minus1 
       } 
   } 

   if( nal_unit_type = = 20 | | nal_unit_type = = 21 )
       // 一般用不到
       ref_pic_list_mvc_modification( ) /* specified in Annex H */ 
   else  
       /*
       用于在编解码端维护参考图像列表时,
       指定是否要对参考图像列表进行重排序操作,
       这需要我们使用到B Slice或P Slice时,
       用到帧间预测时才会出现,因此暂时可以忽略
       */
       ref_pic_list_modification( ) 

   if( ( weighted_pred_flag && ( slice_type = = P | | slice_type = = SP ) ) | | 
 ( weighted_bipred_idc = = 1 && slice_type = = B ) )
       /*
       加权预测表,当使用到加权预测,并且用到P、SP、B 条带时才会用到,暂时可以忽略
       */
       pred_weight_table( ) 

   if( nal_ref_idc != 0 )
       /*
       解码参考图像标记,同重排序操作一样,它同样用于维护参考图
       像列表,主要功能为将参考图像移入或移出参考图像列表
       */
       dec_ref_pic_marking( ) 
/*  ———— 5. B Slice和P Slice所需的参考图像列表和加权预测表 End  ————— */

   if( entropy_coding_mode_flag && slice_type != I && slice_type != SI ) 
       /*
       CABAC中用于决定关联变量的初始化过程中使用的初始化表格的序号,取值范围[0, 2]。
       当然暂时也用不到,由上篇【最简单的h264编解码器】可知,只有在main级别时才可能用到
       */
       cabac_init_idc

   // 6. 量化初始值
   /*
   这个值在不久的将来会用到,结合PPS中的
   pic_init_qp_minus26,表示该Slice内所有宏块的初始量化值:
   SliceQP(Y) = 26 + pic_init_qp_minus26 + slice_qp_delta
   要注意的是,在宏块层会进一步有句法元素mb_qp_delta,对这个初始值进行修改
   */
   slice_qp_delta

   // 7. SP和SI 条带相关的句法元素  
   if(slice_type == SP || slice_type == SI){
      
       if( slice_type = = SP ) 
           // 用于解码SP Slice中P宏块的解码过程,当然暂时用不到
           sp_for_switch_flag
       /*
       与slice_qp_delta类似,只不过用于SP和SI Slice,因此QS(Y)量化参数为:
       QS(Y) = 26 + pic_init_qs_minus26 + slice_qs_delta 
       QS(Y)取值范围为[0, 51]
       */
       slice_qs_delta 
   }
 
   // 8.  去块效应滤波器相关句法元素
   // 去块滤波器相关,暂时也不会用到
   if( deblocking_filter_control_present_flag ) {
      
       disable_deblocking_filter_idc 
       if( disable_deblocking_filter_idc != 1 ) {
      
           slice_alpha_c0_offset_div2 
           slice_beta_offset_div2 
       }
   } 

   // 9. FMO相关
   if( num_slice_groups_minus1 > 0 &&
       slice_group_map_type >= 3 && slice_group_map_type <= 5) 
       /*
       slice_group_map_type为3、4、5时才会用到,因此暂时可
       以忽略,不过它的解析方式有点特别,这点我们接下来会在【解
       析器】中解析Slice_Header()句法元素时看到
       */
       slice_group_change_cycle 
}

你可能感兴趣的:(视频编解码,H264,slice)