首页 > 代码库 > H264学习

H264学习

最近在给MP4文件做CENC加密时需要解析H264的slice头部,才发现对于H264的一些基本概念没有搞清楚。小小的记录一下:

 

1. 如何判断一个H264的帧类型。帧类型包括IDR/I/P/B.

   看一下标准的描述:

nal_unit( NumBytesInNALunit )
{
  forbidden_zero_bit All                              f(1)
  nal_ref_idc                                    u(2)
  nal_unit_type                                   u(5)
  NumBytesInRBSP = 0
  for( i = 1; i < NumBytesInNALunit; i++ )
  {
    if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 )
    {
      rbsp_byte[ NumBytesInRBSP++ ]                      b(8)
      rbsp_byte[ NumBytesInRBSP++ ]                      b(8)
      i += 2
      emulation_prevention_three_byte /* equal to 0x03 */         f(8)
    }
    else
      rbsp_byte[ NumBytesInRBSP++ ]                      b(8)
  }
}

 第一个byte中的后5位 NAL_UNIT_TYPE 标志了帧类型。标准里帧类型的描述为:

技术分享

   在这张表里,区分了IDR帧和非IDR帧类型。NAL_UNIT_TYPE=5即IDR帧, 但是非IDR帧中I/P/B帧的类型并没有明显的区分。查看了一些H264文件,发现I/P/B帧的NAL_UNIT_TYPE通常为1,也就是“Coded slice of a non-IDR picture”。看来通过NAL_UNIT_TYPE是无法彻底区分帧类型了,事实上也是。对于H264帧类型,必须解析到Slice层。

   NAL_UNIT_TYPE等于1,2,5时是存在 slice_header头的。NAL_UNIT_TYPE为2,3,4的区别见下一节。

   slice头的结构如下:

slice_header( ){         first_mb_in_slice                     ue(v)          slice_type                            ue(v)          pic_parameter_set_id                  ue(v)          frame_num                             u(v)          if( !frame_mbs_only_flag )         {                     field_pic_flag                u(1)                   if( field_pic_flag )                          bottom_field_flag         u(1)          }            if( nal_unit_type  = =  5 )             idr_pic_id                        ue(v)          ....}

     Slice的类型,见下图:

技术分享

  1、I宏块是指每个块或宏块是通过其所在的Slice中的之前的已经编码过的数据进行预测的;
  2、P宏块是指宏快或宏块分割是通过List0中的一个参考图像来进行预测的;
  3、B宏块是指宏快或宏块分割是通过List0和/或List1中的参考图像来进行预测的;
  4、SI和SP:即Switch I和Switch P,是一种特殊的编解码条带,可以保证在视频流之间进行有效的切换,并且解码器可以任意的访问。比如,同一个视频源被编码成各种码率的码流,在传输的过程中可以根据网络环境进行实时的切换;
  5、SI宏块是一种特殊类型的内部编码宏块,按Intra_4x4预测宏块编码。

      Slice类型对应的slice_type值,见下图:  

技术分享

     在上图中,I/SI条带的值包括2,4,7,9。 P条带包括0,3,5,8。B条带包括1,6。

     可能会感觉有些奇怪,0到4与5到9居然是重复的。答案是这样的,slice_type的值在5到9的范围内表示,除了当前条带的编码类型,所有当前编码图像的其他条带的slice_type的值应与当前条带的slice_type的值一样,或者等于当前条带的slice_type的值减5。

     回到问题的原点,如何判断I/P/B。

     首先判断NALU类型是否是5,如果是,那么以后连续出现的NALU类型为5的NALU就属于 IDR 帧(一种特殊的 I 帧);

     如果NALU不是5,则要进一步判断 slice_type 是否是 7, 如果是, 那么连续出现的 slice_type=7的slice 就属于 I 帧; 如果 slice_type=2,那么就要判断与当前 slice 同属一帧的 slice 是否都是 I slice, 如果都是, 那么这些 slice 就属于一个 I 帧。

     码流中一般不会出现复杂的情况,粗略的判断标准就是 slice_type  是否等于2或7。

 

2. 编码条带数据分割块A/B/C区别

    我查了几个h264文件,没有在实际中发现这几种类型。

     以下直接引用自

   H264标准句法表中C的含义理解

     编码条带数据分割块A slice_data_partition_a_layer_rbsp( )
  编码条带数据分割块B slice_data_partition_b_layer_rbsp( )
  编码条带数据分割块C slice_data_partition_c_layer_rbsp( )
  这是3种对于片数据的处理方式,其中2类型时,只传递片中最重要的信息,如片头,片中宏块的预测模式等,3类型是只传输残差,而4时则只可以传输残差中的AC系数。

  对照句法表可以看到通过C中指定的数字值,限定了在各个句法元素在特定NAL类型中的使用,以达到在特定NAL中使用不同的句法元素,如不在4中传输残差的DC值,见毕书---表7.17中DC系数语法后面为3,而AC系数后面为3|4,这就达到了在 编码条带数据分割块B 中可以传输所有残差,而在编码条带数据分割块C中仅可以传输AC残差。

  据此可以得到下面的结论:

    C是语法元素可以出现在哪种NAL中的指示,NAL的类型由nal_type_unit指定

  

 参考:

 1. h264 NAL头解析

 2. h264 图像、帧、片、NALU

 3. 从Slice_Header学习H.264(一)--片头语法元素介绍

 

H264学习