H264学习 男娘i 2022-07-16 10:50 210阅读 0赞 最近在给MP4文件做CENC加密时需要解析H264的slice头部,才发现对于H264的一些基本概念没有搞清楚。小小的记录一下: 1. 如何判断一个H264的帧类型。帧类型包括IDR/I/P/B. 看一下标准的描述: ![复制代码][copycode.gif] 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) \} \} ![复制代码][copycode.gif] 第一个byte中的后5位 NAL\_UNIT\_TYPE 标志了帧类型。标准里帧类型的描述为: ![930948-20161015163849093-1705919560.jpg][] 在这张表里,区分了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头的结构如下: ![复制代码][copycode.gif] 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) .... } ![复制代码][copycode.gif] Slice的类型,见下图: ![930948-20161015213828984-439052576.jpg][] 1、I宏块是指每个块或宏块是通过其所在的Slice中的之前的已经编码过的数据进行预测的; 2、P宏块是指宏快或宏块分割是通过List0中的一个参考图像来进行预测的; 3、B宏块是指宏快或宏块分割是通过List0和/或List1中的参考图像来进行预测的; 4、SI和SP:即Switch I和Switch P,是一种特殊的编解码条带,可以保证在视频流之间进行有效的切换,并且解码器可以任意的访问。比如,同一个视频源被编码成各种码率的码流,在传输的过程中可以根据网络环境进行实时的切换; 5、SI宏块是一种特殊类型的内部编码宏块,按Intra\_4x4预测宏块编码。 Slice类型对应的slice\_type值,见下图: ![930948-20161015213907609-917923499.jpg][] 在上图中,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的含义理解][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头解析][h264 NAL] > > 2. [h264 图像、帧、片、NALU][h264 _NALU] > > 3. [从Slice\_Header学习H.264(一)--片头语法元素介绍][Slice_Header_H.264_--] [copycode.gif]: /images/20220716/f9ac2abd292a4e33a4c4efbe3600cfd1.png [930948-20161015163849093-1705919560.jpg]: /images/20220716/562c4a8f8e504f60a7700a890e8944c9.png [930948-20161015213828984-439052576.jpg]: /images/20220716/04ee9e5eb7e841f2950ef807a71417c4.png [930948-20161015213907609-917923499.jpg]: /images/20220716/a2ed111f30354f66be578ea4456bfef5.png [H264_C]: http://www.cnblogs.com/shakin/p/3714862.html [h264 NAL]: http://blog.csdn.net/occupy8/article/details/9042139 [h264 _NALU]: http://blog.csdn.net/tgdzsjh/article/details/18227241 [Slice_Header_H.264_--]: http://blog.csdn.net/newthinker_wei/article/details/8784685
还没有评论,来说两句吧...