最近在給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頭解析