最近在给MP4文件作CENC加密时须要解析H264的slice头部,才发现对于H264的一些基本概念没有搞清楚。小小的记录一下:html
1. 如何判断一个H264的帧类型。帧类型包括IDR/I/P/B.网络
看一下标准的描述:post
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的区别见下一节。url
slice头的结构以下:spa
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的类型,见下图:.net
一、I宏块是指每一个块或宏块是经过其所在的Slice中的以前的已经编码过的数据进行预测的;
二、P宏块是指宏快或宏块分割是经过List0中的一个参考图像来进行预测的;
三、B宏块是指宏快或宏块分割是经过List0和/或List1中的参考图像来进行预测的;
四、SI和SP:即Switch I和Switch P,是一种特殊的编解码条带,能够保证在视频流之间进行有效的切换,而且解码器能够任意的访问。好比,同一个视频源被编码成各类码率的码流,在传输的过程当中能够根据网络环境进行实时的切换;
五、SI宏块是一种特殊类型的内部编码宏块,按Intra_4x4预测宏块编码。3d
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头解析