H.264分幀-2.結合看碼流結構

 因爲幀在H.264標準中沒有一個具體的概念,而從NALU、slice、宏塊等概念來看,它們之中的許多元素實際上是屬於把幀這個概念劃分細小化了。既然幀是相對屬於較大較上層的概念,所以我從NAL層開始往下學習解析。這裏學習主要參考了ITU-T H.264標準建議書,所用到的語法結構表,相關標準都取自該建議書。
 建議書鏈接, H.264標準中文版(不過,這個建議書和我當時看的有微小差別,應該影響不大)

1. NAL頭解析

 首先,查看了NAL單元的語法表,見下表,
NAL單元語法表
 該表描述的NAL單元,大致與前一篇文章所描述的一致。
 可看出NALU是由三個重要的語法元素forbidden_zero_bit, nal_ref_idc, nal_unit_type組成及rbsp字節流組成。其中前三個語法元素就組成了nal_unit_header,即nal頭。該頭由八位組成。
 第1位orbidden_zero_bit爲禁止位,值爲1表示語法錯誤,一般情況下應爲0,不作詳述。
 2~3位nal_ref_idc代表參考級別位,用於表示該NAL是否可以丟棄(有無被其後的NAL參考),“00”表示沒有參考作用,可丟棄,如B slice、SEI等,非零——包括“01”、“10”、“11”——表示該NAL不可丟棄,如SPS、PPS、I Slice、P Slice等。
 4~8位指nal_unit_type(nal單元類型),是指包含在NAL 單元中的RBSP 數據結構的類型。nal_unit_type對應的整數所表示的nalu類型如下表。
NAL單元類型碼
 其中nal_unit_type爲1~5的,稱爲VCL NAL單元。所有其他的NAL單元稱爲非VCL NAL單元。
 不難看出,VCL NAL單元都與slice相關,確切的來說,裏面裝載的RBSP數據就是圖像的數據。而非VCL NAL單元大多是用來描述圖像數據的。
 實際操作中,可通過將起始碼之後的第一個字節&0x1f,對應上表,判斷該nalu的類型。如下圖是一段碼流數據,嘗試用以上方法分析下面碼流,
碼流數據
 矩形標註的爲起始碼,圓形標註的爲該NALU的類型。可以得到第一個NALU類型是27,二進制表示爲0010 0111,&0x1f(二進制0001 1111),得到0000 0111,即十進制7,對比NALU類型表可知,該NALU爲SPS,是一個序列參數集。緊接着一個NALU類型爲8,是PPS,圖像參數集。第三個NALU類型爲IDR。
 目前爲止,根據前面已有知識與結論,結合標準7.4.1.2.3的介紹。可以獲得一種分幀的方法。

 標準7.4.1.2.3中介紹,在基本編碼圖像的最後一個VCL NAL 單元之後的第一個任何下列NAL 單元代表了一個新的訪問單元(即一幀圖像)的開始。
 — 訪問單元分隔NAL單元(存在時)
 — 序列參數集NAL單元(存在時)
 — 圖像參數集NAL單元(存在時)
 — SEI NAL單元(存在時)
 — nal_unit_type值在14-18之間(包括)的NAL單元
 — 基本編碼圖像的第一個VCL NAL單元(總是存在)
 從上述描述中,可以看出新一幀的第一個VCL NAL單元是一定存在的,所以只有找到新一幀的第一個VCL NAL單元時,才能確定的進行分幀。

 而幸運的,該節還介紹了對於基本編碼圖像的第一個VCL NAL單元的檢測的規定在7.4.1.2.4節給出。

 當前訪問單元的基本編碼圖像的任何編碼條帶NAL單元或編碼條帶數據分割塊A的NAL單元應與前一個訪問單元的基本編碼圖像的任何編碼條帶NAL單元或編碼條帶數據分割塊A的NAL單元以下列方式中的一種或多種進行區分:
 — frame_num的值不同。不管由於memory_management_control_operation等於5的情況出現而爲了在後續解碼過程中使用frame_num的值是否已經等於0,該值通常在測試條件中是在條帶頭的語法中出現的frame_num的值。
注1—上述情況的一個推理是一個包含frame_num值等於1的基本編碼圖像不能包含一個等於5的memory_management_control_operation,除非跟隨其後的下一個基本編碼圖像(如果有的話)能夠滿足下面列出的其它條件。
 — pic_parameter_set_id 值不同。
 — field_pic_flag 值不同。
 — bottom_field_flag 在兩個訪問單元中都出現而且值不同。
 — nal_ref_idc 值不同,而且其中一個的nal_ref_idc 值等於0。
 — 兩個訪問單元的pic_order_cnt_type 都等於0,並且兩個pic_order_cnt_lsb值不同或delta_pic_
order_cnt_bottom 值不同。
 — 兩個訪問單元的pic_order_cnt_type都等於1,並且兩個delta_pic_order_cnt[ 0 ] 值不同或者delta_pic_order_cnt[ 1 ] 值不同。
 — nal_unit_type 值不同,而且其中一個的nal_unit_type 值等於5。
 — 兩個訪問單元的nal_unit_type都等於5,並且idr_pic_id 值不同。

 追蹤至此,發現一下子出現了許多沒接觸過的參數,而這些參數需要多重判斷並且未在NALU層出現。於是,往下進入slice層的解析。

2. slice頭解析

 按照前文所述,數據由數據頭和數據體組成。數據頭即是對數據體的描述。所以進入slice層後,先跟蹤slice頭的信息,查看其語法結構,果然發現了frame_num這個語法元素。見下圖,因slice頭語法結構龐大,只給出部分語法結構表。
slice_header部分語法結構表
 進入建議書7.4.3節,查看條帶頭語義,

7.4.3 條帶頭語義
 如果存在,條帶頭語法元素pic_parameter_set_id、 frame_num、 field_pic_flag、bottom_field_flag、
idr_pic_id、 pic_order_cnt_lsb、 delta_pic_order_cnt_bottom、delta_pic_order_cnt[ 0 ]、delta_pic_order_cnt[ 1 ]、sp_for_switch_flag和 slice_group_change_cycle 的值在一個編碼圖像的所有條帶頭中都應一樣。
 first_mb_in_slice 表示在條帶中第一個宏塊的地址。當如附件A 中規定的那樣不允許任意的條帶順序時,本條帶的first_mb_in_slice 的值應不小於當前圖像的任何在該條帶之前(按解碼順序)的其他條帶的first_mb_in_slice 的值。

 從該小節該段落可知,可以通過段1中的語法元素(若存在)判斷是否相等,或者判斷段2中first_mb_in_slice是否單調遞增(在不允許任意條帶順序時)來進行分幀。
 結合1中nal頭的解析,可以分爲兩種方案,一是對slice頭中大量語法元素的判斷(存在時),二是對VCL NAL中總是存在的first_mb_in_slice是否單調遞增(在不允許任意條帶順序時)進行判斷。

小結

 在此,我選擇方法的時候,選擇了通過判斷slice頭中的first_mb_in_slice的來進行分幀。因爲參考建議書中的附件A並結合實際情況,編寫分幀工具,不一定支持ASO,所以我在此不需要對任意條帶順序進行判斷。通過進一步瞭解,可知該語法元素代表slice中首個宏塊在圖像中的地址。換言之,我無須判斷他的單調遞增性,只需要判斷其值是否爲0,就可以知道該NALU是否爲一幀新的圖像的開始,即分幀方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章