1. 程式人生 > >H.264---SPS和PPS

H.264---SPS和PPS

轉自:https://zhuanlan.zhihu.com/p/27896239

  • 1 SPS和PPS從何處而來?
  • 2 SPS和PPS中的每個引數起什麼作用?
  • 3 如何解析SDP中包含的H.264的SPS和PPS串?

1 客戶端抓包

在做客戶端視訊解碼時,一般都會使用Wireshark抓包工具對接收的H264碼流進行分析,如下所示:

在這裡我們可以看到對解碼視訊起關鍵作用的SPS和PPS。

雙擊SPS內容如下:

雙擊PPS內容如下:

那麼從上面的sps中我們知道影象的寬,高

寬=(19+1 )*16=320

高=(14+1)*16=240

為什麼?參考下面

2 SPS PPS詳解

2.1 SPS語法元素及其含義

在H.264標準協議中規定了多種不同的NAL Unit型別,其中型別7表示該NAL Unit內儲存的資料為Sequence Paramater Set。在H.264的各種語法元素中,SPS中的資訊至關重要。如果其中的資料丟失或出現錯誤,那麼解碼過程很可能會失敗。SPS及後續將要講述的影象引數集PPS在某些平臺的視訊處理框架(比如iOS的VideoToolBox等)還通常作為解碼器例項的初始化資訊使用。

SPS即Sequence Paramater Set,又稱作序列引數集。SPS中儲存了一組編碼視訊序列(Coded video sequence)的全域性引數。所謂的編碼視訊序列即原始視訊的一幀一幀的畫素資料經過編碼之後的結構組成的序列。而每一幀的編碼後資料所依賴的引數保存於影象引數集中。一般情況SPS和PPS的NAL Unit通常位於整個碼流的起始位置。但在某些特殊情況下,在碼流中間也可能出現這兩種結構,主要原因可能為:

  • 解碼器需要在碼流中間開始解碼;
  • 編碼器在編碼的過程中改變了碼流的引數(如影象解析度等);

在做視訊播放器時,為了讓後續的解碼過程可以使用SPS中包含的引數,必須對其中的資料進行解析。其中H.264標準協議中規定的SPS格式位於文件的7.3.2.1.1部分,如下圖所示:

 

其中的每一個語法元素及其含義如下:

(1) profile_idc:

標識當前H.264碼流的profile。我們知道,H.264中定義了三種常用的檔次profile:

基準檔次:baseline profile;

主要檔次:main profile;

擴充套件檔次:extended profile;

在H.264的SPS中,第一個位元組表示profile_idc,根據profile_idc的值可以確定碼流符合哪一種檔次。判斷規律為:

profile_idc = 66 → baseline profile;

profile_idc = 77 → main profile;

profile_idc = 88 → extended profile;

在新版的標準中,還包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High 4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra等,每一種都由不同的profile_idc表示。

另外,constraint_set0_flag ~ constraint_set5_flag是在編碼的檔次方面對碼流增加的其他一些額外限制性條件。

在我們實驗碼流中,profile_idc = 0x42 = 66,因此碼流的檔次為baseline profile。

(2) level_idc

標識當前碼流的Level。編碼的Level定義了某種條件下的最大視訊解析度、最大視訊幀率等引數,碼流所遵從的level由level_idc指定。

當前碼流中,level_idc = 0x1e = 30,因此碼流的級別為3。

(3) seq_parameter_set_id

表示當前的序列引數集的id。通過該id值,影象引數集pps可以引用其代表的sps中的引數

(4) log2_max_frame_num_minus4

用於計算MaxFrameNum的值。計算公式為MaxFrameNum = 2^(log2_max_frame_num_minus4 + 4)。MaxFrameNum是frame_num的上限值,frame_num是影象序號的一種表示方法,在幀間編碼中常用作一種參考幀標記的手段。

(5) pic_order_cnt_type

表示解碼picture order count(POC)的方法。POC是另一種計量影象序號的方式,與frame_num有著不同的計算方法。該語法元素的取值為0、1或2。

(6) log2_max_pic_order_cnt_lsb_minus4

用於計算MaxPicOrderCntLsb的值,該值表示POC的上限。計算方法為MaxPicOrderCntLsb = 2^(log2_max_pic_order_cnt_lsb_minus4 + 4)。

(7) max_num_ref_frames

用於表示參考幀的最大數目

(8) gaps_in_frame_num_value_allowed_flag

標識位,說明frame_num中是否允許不連續的值

(9) pic_width_in_mbs_minus1

用於計算影象的寬度。單位為巨集塊個數,因此影象的實際寬度為:

frame_width = 16 × (pic\_width\_in\_mbs_minus1 + 1);

(10) pic_height_in_map_units_minus1

使用PicHeightInMapUnits來度量視訊中一幀影象的高度。PicHeightInMapUnits並非影象明確的以畫素或巨集塊為單位的高度,而需要考慮該巨集塊是幀編碼或場編碼。PicHeightInMapUnits的計算方式為:

PicHeightInMapUnits = pic\_height\_in\_map\_units\_minus1 + 1;

(11) frame_mbs_only_flag

標識位,說明巨集塊的編碼方式。當該標識位為0時,巨集塊可能為幀編碼場編碼;該標識位為1時,所有巨集塊都採用幀編碼。根據該標識位取值不同,PicHeightInMapUnits的含義也不同,為0時表示一場資料按巨集塊計算的高度,為1時表示一幀資料按巨集塊計算的高度。

按照巨集塊計算的影象實際高度FrameHeightInMbs的計算方法為:

FrameHeightInMbs = ( 2 − frame_mbs_only_flag ) * PicHeightInMapUnits

(12) mb_adaptive_frame_field_flag

標識位,說明是否採用了巨集塊級的幀場自適應編碼。當該標識位為0時,不存在幀編碼和場編碼之間的切換;當標識位為1時,巨集塊可能在幀編碼和場編碼模式之間進行選擇。

(13) direct_8x8_inference_flag

標識位,用於B_Skip、B_Direct模式運動向量的推導計算

(14) frame_cropping_flag

標識位,說明是否需要對輸出的影象幀進行裁剪

(15) vui_parameters_present_flag

標識位,說明SPS中是否存在VUI資訊

2.2 PPS語法元素及其含義

除了序列引數集SPS之外,H.264中另一重要的引數集合為影象引數集Picture Paramater Set(PPS)。通常情況下,PPS類似於SPS,在H.264的裸碼流中單獨儲存在一個NAL Unit中,只是PPS NAL Unit的nal_unit_type值為8;而在封裝格式中,PPS通常與SPS一起,儲存在視訊檔案的檔案頭中。

在H.264的協議文件中,PPS的結構定義在7.3.2.2節中,具體的結構如下表所示:

其中的每一個語法元素及其含義如下:

(1) pic_parameter_set_id

表示當前PPS的id。某個PPS在碼流中會被相應的slice引用,slice引用PPS的方式就是在Slice header中儲存PPS的id值。該值的取值範圍為[0,255]。

(2) seq_parameter_set_id

表示當前PPS所引用的啟用的SPS的id。通過這種方式,PPS中也可以取到對應SPS中的引數。該值的取值範圍為[0,31]。

(3) entropy_coding_mode_flag

熵編碼模式標識,該標識位表示碼流中熵編碼/解碼選擇的演算法。對於部分語法元素,在不同的編碼配置下,選擇的熵編碼方式不同。例如在一個巨集塊語法元素中,巨集塊型別mb_type的語法元素描述符為“ue(v) | ae(v)”,在baseline profile等設定下采用指數哥倫布編碼,在main profile等設定下采用CABAC編碼。

標識位entropy_coding_mode_flag的作用就是控制這種演算法選擇。當該值為0時,選擇左邊的演算法,通常為指數哥倫布編碼或者CAVLC;當該值為1時,選擇右邊的演算法,通常為CABAC。

(4) bottom_field_pic_order_in_frame_present_flag

標識位,用於表示另外條帶頭中的兩個語法元素delta_pic_order_cnt_bottom和delta_pic_order_cn是否存在的標識。這兩個語法元素表示了某一幀的底場的POC的計算方法

(5) num_slice_groups_minus1

表示某一幀中slice group的個數。當該值為0時,一幀中所有的slice都屬於一個slice group。slice group是一幀中巨集塊的組合方式,定義在協議文件的3.141部分。

(6) num_ref_idx_l0_default_active_minus1、num_ref_idx_l0_default_active_minus1

表示當Slice Header中的num_ref_idx_active_override_flag標識位為0時,P/SP/B slice的語法元素num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1的預設值。

(7) weighted_pred_flag

標識位,表示在P/SP slice中是否開啟加權預測

(8) weighted_bipred_idc

表示在B Slice中加權預測的方法,取值範圍為[0,2]。0表示預設加權預測,1表示顯式加權預測,2表示隱式加權預測。

(9) pic_init_qp_minus26和pic_init_qs_minus26

表示初始的量化引數。實際的量化引數由該引數、slice header中的slice_qp_delta/slice_qs_delta計算得到。

(10) chroma_qp_index_offset

用於計算色度分量的量化引數,取值範圍為[-12,12]。

(11) deblocking_filter_control_present_flag

標識位,用於表示Slice header中是否存在用於去塊濾波器控制的資訊。當該標誌位為1時,slice header中包含去塊濾波相應的資訊;當該標識位為0時,slice header中沒有相應的資訊。

(12) constrained_intra_pred_flag

若該標識為1,表示I巨集塊在進行幀內預測時只能使用來自I和SI型別巨集塊的資訊;若該標識位0,表示I巨集塊可以使用來自Inter型別巨集塊的資訊。

(13) redundant_pic_cnt_present_flag

標識位,用於表示Slice header中是否存在redundant_pic_cnt語法元素。當該標誌位為1時,slice header中包含redundant_pic_cnt;當該標識位為0時,slice header中沒有相應的資訊。

3 解析SDP中包含的H.264的SPS和PPS串

用RTP傳輸H264的時候,需要用到sdp協議描述,其中有兩項:Sequence Parameter Sets (SPS) 和Picture Parameter Set (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=

最終解析得到的結果為:

這裡需要特別提一下這兩個引數

pic_width_in_mbs_minus1 = 21

pic_height_in_mbs_minus1 = 17

分別表示影象的寬和高,以巨集塊(16x16)為單位的值減1

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

到這裡應該知道第一部分客戶端抓包計算影象寬高遺留下來的問題了吧。