1. 程式人生 > >H264的解碼總結(概念性的總結)

H264的解碼總結(概念性的總結)

概念簡介:

1:nal的作用:簡稱網路抽象層,負責H264的格式化資料並且提供頭資訊,以保證資料適合各種信和儲存介質上的傳輸。

nal的結構是:

NAL頭+RBSP(所謂的RBSP是原始編碼資料後面加了結尾位元)

RBSP為資料塊:資料塊分為

A:SODB最原始的編碼資料,

B:RBSP原始編碼資料後面添加了結尾位元。

    資料內部可以分為:

    1:引數集:PS:序列的全域性資訊:影象尺寸,視訊格式等

    2:增強資訊:SEI:視訊序列解碼的增強資訊

    3:影象界定符:PD:視訊影象的邊界

    4:編碼片:SLICE:編碼片的頭資訊和資料

    5:資料分割:DP片層的資料,用於恢復錯誤解碼

    6:序列結束符:表明一個序列的結束,下一個影象為IDR影象

    7:流結束符:表明流中沒有影象。

C:EBSP加了防校驗字元。:

H264分為兩層:視訊編碼層(VCL)和網路提取層(NAL)VCL書記為被壓縮編碼後的視訊資料序列,在VCL資料封裝到NAL單元之後,才可以用來傳輸

始碼StartCodePrefix,如果該NALU對應的slice為一幀的開始則用4位位元組表示,ox00000001,否則用3位位元組表示ox000001(是一幀的一部分)。另外,為了使NALU主體中不包括與開始碼相沖突的,在編碼時,每遇到兩個位元組連續為0,就插入一個位元組的0x03。解碼時將0x03去掉。也稱為脫殼操作。

1個nal資料結構的基本單元:

**********************************************
NALU單元的開始 00 00 00 01 +nal_header頭
(RBSP結構)SPS+SEI+PPS+I片+影象界定符(PD視訊影象邊界)
SEI新增自定義資訊  (視訊序列解碼增強資訊)00 00 00 01 06

編碼片SLICE  (編碼片的頭資訊和資料)

一些主要資料結構的結構頭例程

SPS 00 00 00 01 67(序列引數集)
PPS 00 00 00 01 68(影象引數集)
IDR 00 00 00 01 65(IDR影象的片)

***************************************


       資料分割:組成片的編碼資料存放在 3 個獨立的 DP(資料分割,A、B、C)中,各自包含一個編碼片的子集。

       分割A包含片頭和片中每個巨集塊頭資料。

       分割B包含幀內和 SI 片巨集塊的編碼殘差資料。

       分割 C包含幀間巨集塊的編碼殘差資料。

       每個分割可放在獨立的 NAL 單元並獨立傳輸。

(一般是P幀,B幀的分割傳輸)


編碼器將每個NAL各自獨立、完整地放入一個分組,因為分組都有頭部,解碼器可以方便地檢測出NAL的分界,並依次取出NAL進行解碼。

每個NAL前有一個起始碼 0x00 00 01(或者0x00 00 00 01),解碼器檢測每個起始碼,作為一個NAL的起始標識,當檢測到下一個起始碼時,當前NAL結束。同時H.264規定,當檢測到0x000000時,也可以表徵當前NAL的結束。那麼NAL中資料出現0x000001或0x000000時怎麼辦?H.264引入了防止競爭機制,如果編碼器檢測到NAL資料存在0x000001或0x000000時,編碼器會在最後個位元組前插入一個新的位元組0x03

注意:解碼器在解碼時,首先逐個位元組讀取NAL的資料,統計NAL的長度,然後再開始解碼。

2:   H264的分塊

場幀的概念:,視訊中的一場一幀可以產生一個編碼圖片    。

一幀是有一片或者多個片組成,一片由1個或者多個巨集塊組成

一個編碼影象通常劃分為若干個巨集塊組成,一個巨集塊由一個16*16的亮度畫素和附加的一個8*8Cb和一個8*8的cr彩色畫素塊組成

16*16的YUV資料組成,巨集塊作為H264編碼的基本單位

片可以分為I片(只包含I巨集塊)。B片(B和I巨集塊),P片(P和I巨集塊)和其他一些片

I巨集塊利用當前片中已解碼的畫素作為參考進行幀內預測。

P巨集塊:利用前面已編碼的影象作為參考進行幀內預測

B巨集塊:利用雙向的參考影象進行幀內預測。

3:nal頭結構

1個位元組:forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)

1.forbidden_bit:                             禁止位,初始為0,當網路發現NAL單元有位元錯誤時可設定該位元為1,以便接收方糾錯或丟掉該單元。

2.nal_reference_bit:                   nal重要性指示,標誌該NAL單元的重要性,值越大,越重要,解碼器在解碼處理不過來的時候,可以丟掉重要性為0的NALU。

nal_unit_type 值為 5 的NALU 為IDR幀

nal_unit_type中的1(非IDR影象的編碼條帶)、2(編碼條帶資料分割塊A)、3(編碼條帶資料分割塊B)、4(編碼條帶資料分割塊C)、5(IDR影象的編碼條帶)種類型

0:未規定 
1:非IDR影象中不採用資料劃分的片段 
2:非IDR影象中A類資料劃分片段 
3:非IDR影象中B類資料劃分片段 
4:非IDR影象中C類資料劃分片段 
5:IDR影象的片段 
6:補充增強資訊 (SEI) 
7:序列引數集 
8:影象引數集 
9:分割符 
10:序列結束符 
11:流結束符 
12:填充資料 
13 – 23:保留 
24 – 31:未規定

所謂參考幀,就是在其他幀解碼時需要參照的幀。比如一個I幀可能被一個或多個B幀參考,一個B幀可能被某個P幀參考。

       從這個表我們也可以看出來,DIR的I幀是非常重要的,他一丟,那麼這個序列的所有幀都沒辦法解碼了;

       序列引數集和影象引數集也很重要,沒有序列引數集,這個序列的幀就沒法解;

       沒有影象引數集,那用到這個影象引數集的幀都沒法解。

4:比較重要的兩個序列引數集:SPS,PPS(需要解析這兩個引數用來解碼資料流)

H264中將原本的序列和影象頭部的大部分元素有力出來形成序列引數集和影象引數集,其餘的部分則放入片內。

引數集是一個獨立的資料單位,不依賴引數集外的其他句法元素,一個引數集不對應 某一個特定的影象或者序列,同一個序列引數集可以被多個影象引數集引用,用一個影象引數集可以被多個影象所引用,

*****************************************(比較重要)

只有在編碼器需要更新引數集的內容是,才會發出新的引數集

eg:I  P  P  P  P 是一個序列

在H264中影象以序列進行組織,一個序列的第一個影象叫做IDR影象(立即重新整理影象),IDR影象都是I幀影象,H264引入IDR是為了引入解碼的重同步,當解碼到IDR影象時,立即將參考幀佇列清空,將已解碼的資料全部輸出或者拋棄,重新查詢引數集,開始一個新的序列。

***************************************

1:序列引數集(SPS):

傳送以次引數集為參考的NAL單元之前傳送,nal單元有專門的表示,如果標識相同,則序列引數集相同

   SPS 包含的是針對一連續編碼視訊序列的引數,如識別符號 seq_parameter_set_id、幀數及 POC 的約束、參考幀數目、解碼影象尺寸和幀場編碼模式選擇標識等等。

2:影象引數集(PPS):

   其引數如識別符號 pic_parameter_set_id、可選的 seq_parameter_set_id、熵編碼模式選擇標識、片組數目、初始量化引數和去方塊濾波係數調整標識等等。

注意關於序列引數及和影象引數集:

1.序列引數集NAL單元       

必須在傳送所有以此引數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的序列引數集NAL單元。

所謂重複的詳細解釋為:序列引數集NAL單元都有其專門的標識,如果兩個序列引數集NAL單元的標識相同,就可以認為後一個只不過是前一個的拷貝,而非新的序列引數集。
      

2.影象引數集NAL單元      

必須在所有以此引數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的影象引數集NAL單元,這一點與上述的序列引數集NAL單元是相同的。

3.不同基本編碼影象中的片段(slice)單元和資料劃分片段(data partition)單元在順序上不可以相互交叉,即不允許屬於某一基本編碼影象的一系列片段(slice)單元和資料劃分片段(data partition)單元中忽然出現另一個基本編碼影象的片段(slice)單元片段和資料劃分片段(data partition)單元。


4.參考影象的影響:如果一幅影象以另一幅影象為參考,則屬於前者的所有片段(slice)單元和資料劃分片段(data partition)單元必須在屬於後者的片段和資料劃分片段之後,無論是基本編碼影象還是冗餘編碼影象都必須遵守這個規則。


5.基本編碼影象的所有片段(slice)單元和資料劃分片段(data partition)單元必須在屬於相應冗餘編碼影象的片段(slice)單元和資料劃分片段(data partition)單元之前。

       

6.如果資料流中出現了連續的無參考基本編碼影象,則影象序號小的在前面。

        

7.如果arbitrary_slice_order_allowed_flag置為1,一個基本編碼影象中的片段(slice)單元和資料劃分片段(data partition)單元的順序是任意的,如果arbitrary_slice_order_allowed_flag置為零,則要按照片段中第一個巨集塊的位置來確定片段的順序,若使用資料劃分,則A類資料劃分片段在B類資料劃分片段之前,B類資料劃分片段在C類資料劃分片段之前,而且對應不同片段的資料劃分片段不能相互交叉,也不能與沒有資料劃分的片段相互交叉。


8.如果存在SEI(補充增強資訊)單元的話,它必須在它所對應的基本編碼影象的片段(slice)單元和資料劃分片段(data partition)單元之前,並同時必須緊接在上一個基本編碼影象的所有片段(slice)單元和資料劃分片段(data partition)單元后邊。假如SEI屬於多個基本編碼影象,其順序僅以第一個基本編碼影象為參照。


9.如果存在影象分割符的話,它必須在所有SEI 單元、基本編碼影象的所有片段slice)單元和資料劃分片段(data partition)單元之前,並且緊接著上一個基本編碼影象那些NAL單元。

        

10.如果存在序列結束符,且序列結束符後還有影象,則該影象必須是IDR(即時解碼器重新整理)影象。序列結束符的位置應當在屬於這個IDR影象的分割符、SEI 單元等資料之前,且緊接著前面那些影象的NAL單元。如果序列結束符後沒有影象了,那麼它的就在位元流中所有影象資料之後。

      

11.流結束符在位元流中的最後。

*********************************************************************

在寫程式是用到的兩個序列集SPS序列引數集 PPS 影象引數集(引數集的獲取和解析)

從H264碼流中獲取.在H264碼流中,都是以"0x00 0x00 0x01"或者"0x00 0x00 0x00 0x01"為開始碼的,找到開始碼之後,使用開始碼之後的第一個位元組的低5位判斷是否為7(sps)或者8(pps), 及data[4] & 0x1f == 7 || data[4] & 0x1f == 8.然後對獲取的nal去掉開始碼之後進行base64編碼,得到的資訊就可以用於sdp.sps和pps需要用逗號分隔開來

SDP中的H.264的SPS和PPS串,包含了初始化H.264解碼器所需要的資訊引數,包括編碼所用的profile,level,影象的寬和高,deblock濾波器等。
由於SDP中的SPS和PPS都是BASE64編碼形式的,不容易理解,附件有一個工具軟體可以對SDP中的SPS和PPS進行解析。
用法是在命令列中輸入:
spsparser sps.txt pps.txt output.txt

例如sps.txt中的內容為:
Z0LgFNoFglE=
pps.txt中的內容為:
aM4wpIA=

最終解析的到的結果為:

Start dumping SPS:
  profile_idc = 66
  constrained_set0_flag = 1
  constrained_set1_flag = 1
  constrained_set2_flag = 1
  constrained_set3_flag = 0
  level_idc = 20
  seq_parameter_set_id = 0
  chroma_format_idc = 1
  bit_depth_luma_minus8 = 0
  bit_depth_chroma_minus8 = 0
  seq_scaling_matrix_present_flag = 0
  log2_max_frame_num_minus4 = 0
  pic_order_cnt_type = 2
  log2_max_pic_order_cnt_lsb_minus4 = 0
  delta_pic_order_always_zero_flag = 0
  offset_for_non_ref_pic = 0
  offset_for_top_to_bottom_field = 0
  num_ref_frames_in_pic_order_cnt_cycle = 0
  num_ref_frames = 1
  gaps_in_frame_num_value_allowed_flag = 0
  pic_width_in_mbs_minus1 = 21
  pic_height_in_mbs_minus1 = 17
  frame_mbs_only_flag = 1
  mb_adaptive_frame_field_flag = 0
  direct_8x8_interence_flag = 0
  frame_cropping_flag = 0
  frame_cropping_rect_left_offset = 0
  frame_cropping_rect_right_offset = 0
  frame_cropping_rect_top_offset = 0
  frame_cropping_rect_bottom_offset = 0
  vui_parameters_present_flag = 0

Start dumping PPS:
  pic_parameter_set_id = 0
  seq_parameter_set_id = 0
  entropy_coding_mode_flag = 0
  pic_order_present_flag = 0
  num_slice_groups_minus1 = 0
  slice_group_map_type = 0
  num_ref_idx_l0_active_minus1 = 0
  num_ref_idx_l1_active_minus1 = 0
  weighted_pref_flag = 0
  weighted_bipred_idc = 0
  pic_init_qp_minus26 = 0
  pic_init_qs_minus26 = 0
  chroma_qp_index_offset = 10
  deblocking_filter_control_present_flag = 1
  constrained_intra_pred_flag = 0
  redundant_pic_cnt_present_flag = 0
  transform_8x8_mode_flag = 0
  pic_scaling_matrix_present_flag = 0
  second_chroma_qp_index_offset = 10

/////////////////////////////////////////////////////////////////////////////////////////////////
這裡需要特別提一下這兩個引數
pic_width_in_mbs_minus1 = 21
  pic_height_in_mbs_minus1 = 17
分別表示影象的寬和高,以巨集塊(16x16)為單位的值減1

因此,實際的寬為 (21+1)*16 = 352