RTP(載荷h264碼流)解包與封包

一、H264介紹

1.1 h264概述

h264是一種視頻壓縮標準。

經過壓縮後的幀分爲:I幀,P幀和B幀:

  • I幀:關鍵幀,採用幀內壓縮技術。(自身可以通過視頻解壓算法解壓成一張單獨的完整的圖片)
  • P幀:向前參考幀,在壓縮時,只參考前面已經處理的幀(只需要參考前面的I幀或P幀)。採用幀音壓縮技術。
  • B幀:雙向參考幀,在壓縮時,它即參考前而的幀,又參考它後面的幀(需要同時參考前面和後面的I幀或P幀)。採用幀間壓縮技術。

除了I/P/B幀外,還有圖像序列GOP。

  • GOP:兩個I幀之間是一個圖像序列,在一個圖像序列中只有一個I幀

(圖出自H.264視頻壓縮標準白皮書)

在H.264基準類中,僅使用I幀和P幀以實現低延時,因此是網絡攝像機和視頻編碼器的理想選擇。

 

1.2 h264原始碼流結構

H264功能分爲兩層,VCL(視頻編碼層)和 NAL(網絡提取層)

  • VCL:包括核心壓縮引擎和塊,宏塊和片的語法級別定義,設計目標是儘可能地獨立於網絡進行高效的編碼。
  • NAL:負責將VCL產生的比特字符串適配到各種各樣的網絡和多元環境中,覆蓋了所有片級以上的語法級別。

VCL數據傳輸或者存儲之前,會被映射到一個NALU中,H264數據包含一個個NALU。如下圖:


一個NALU = 一組對應於視頻編碼的NALU頭部信息 + 一個原始字節序列負荷(RBSP, Raw Byte Sequence Payload)


NAL單元格式如下圖:

一個原始的NALU單元結構包含:
[StartCode]+[NALU Header]+[NALU Payload] 三部分(StartCode,是一個NALU單元開始,必須是00 00 00 01 或者00 00 01)

       H.264 的編碼視頻序列包括一系列的NAL 單元,每個NAL 單元包含一個RBSP。編碼片(包括數據分割片IDR 片)和序列RBSP 結束符被定義爲VCL NAL 單元,其餘爲NAL 單元。典型的RBSP 單元序列如圖2 所示。每個單元都按獨立的NAL 單元傳送。單元的信息頭(一個字節)定義了RBSP 單元的類型,NAL 單元的其餘部分爲RBSP 數據。

1.3 NAL單元

       每個NAL單元是一個一定語法元素的可變長字節字符串,包括包含一個字節的頭信息(用來表示數據類型),以及若干整數字節的負荷數據。一個NAL單元可以攜帶一個編碼片、A/B/C型數據分割或一個序列或圖像參數集。

NALU頭由一個字節組成,它的語法如下:

NAL單元按RTP序列號按序傳送。其中,T爲負荷數據類型,佔5bit;R爲重要性指示位,佔2個bit;最後的F爲禁止位,佔1bit。具體如下:

  • NALU類型位:可以表示NALU的32種不同類型特徵,類型1~12是H.264定義的,類型24~31是用於H.264以外的,RTP負荷規範使用這其中的一些值來定義包聚合和分裂,其他值爲H.264保留。
  • 重要性指示位:用於在重構過程中標記一個NAL單元的重要性,值越大,越重要。值爲0表示這個NAL單元沒有用於預測,因此可被解碼器拋棄而不會有錯誤擴散;值高於0表示此NAL單元要用於無漂移重構,且值越高,對此NAL單元丟失的影響越大。
  • 禁止位:編碼中默認值爲0,當網絡識別此單元中存在比特錯誤時,可將其設爲1,以便接收方丟掉該單元,主要 用於適應不同種類的網絡環境(比如有線無線相結合的環境)。
     

264常見的幀頭數據爲:

00 00 00 01 67 (SPS)

00 00 00 01 68 (PPS)

00 00 00 01 65 ( IDR 幀)

00 00 00 01 61 (P幀)

上述的67,68,65,61,還有41等,都是該NALU的識別級別。

F:禁止爲,0表示正常,1表示錯誤,一般都是0

NRI:重要級別,11表示非常重要。

TYPE:表示該NALU的類型是什麼,

見下表,由此可知7爲序列參數集(SPS),8爲圖像參數集(PPS),5代表I幀。1代表非I幀。

由此可知,61和41其實都是P幀(type值爲1),只是重要級別不一樣(它們的NRI一個是11BIN,一個是10BIN)

NALU類型是我們判斷幀類型的利器,從官方文檔中得出如下圖:

1.4 h264幀判斷

最上面圖的碼流對應的數據來層層分析,以00 00 00 01分割之後的下一個字節就是NALU類型,將其轉爲二進制數據後,

解讀順序爲從左往右算,如下:

(1)第1位禁止位,值爲1表示語法出錯

(2)第2~3位爲參考級別

(3)第4~8爲是nal單元類型

例如上面00000001後有67,68以及65

其中0x67的二進制碼爲:

0110 0111

4-8爲00111,轉爲十進制7,參考第二幅圖:7對應序列參數集SPS

其中0x68的二進制碼爲:

0110 1000
4-8爲01000,轉爲十進制8,參考第二幅圖:8對應圖像參數集PPS

其中0x65的二進制碼爲:

011 00101

4-8位爲00101,轉爲十進制5,參考第二幅圖:5對應IDR圖像中的片(I幀)

所以判斷是否爲I幀的算法爲:

(NALU類型 & 0001 1111) = 5 即 (NALU類型 & 31) = 5
比如0x65 & 31 = 5

即:int value = buf[4] & 0x0f;   // 5是I幀, 7是sps 8是pps

 

二、RTP打包發送H264之封包詳解

RFC3984是H.264的baseline碼流在RTP方式下傳輸的規範,這裏只討論FU-A分包方式,

H264的碼流結構

1、單個NAL包單元

12字節的RTP頭後面的就是音視頻數據,比較簡單。一個封裝單個NAL單元包到RTP的NAL單元流的RTP序號必須符合NAL單元的解碼順序。
對於 NALU 的長度小於 MTU 大小的包, 一般採用單一 NAL 單元模式.
對於一個原始的 H.264 NALU 單元常由[Start Code] [NALU Header] [NALU Payload]三部分組成, 其中 Start Code 用於標示這是一個
NALU 單元的開始, 必須是 “00 00 00 01” 或 “00 00 01”, NALU 頭僅一個字節, 其後都是 NALU 單元內容.

打包時去除 “00 00 01” 或 “00 00 00 01” 的開始碼, 把其他數據封包的 RTP包即可

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |F|NRI|  type   |                                               |
  +-+-+-+-+-+-+-+-+                                               |
  |                                                               |
  |               Bytes 2..n of a Single NAL unit                 |
  |                                                               |
  |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                               :...OPTIONAL RTP padding        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

如有一個 H.264 的 NALU 是這樣的:

[00 00 00 01 67 42 A0 1E 23 56 0E 2F … ]

這是一個序列參數集 NAL 單元. [00 00 00 01] 是四個字節的開始碼, 67 是 NALU 頭, 42 開始的數據是 NALU 內容.

封裝成 RTP 包將如下:

[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]

即只要去掉 4 個字節的開始碼就可以了

2. 組合封包模式

當 NALU 的長度特別小時, 可以把幾個 NALU 單元封在一個 RTP 包中

3、FU-A的分片格式

數據比較大的H264視頻包,被RTP分片發送。12字節的RTP頭後面跟隨的就是FU-A分片:
而當 NALU 的長度超過 MTU 時, 就必須對 NALU 單元進行分片封包. 也稱爲 Fragmentation Units (FUs)

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | FU indicator  |   FU header  |                               |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
  |                                                               |
  |                         FU payload                            |
  |                                                               |
  |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                               :...OPTIONAL RTP padding        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  Figure 14.  RTP payload format for FU-A

 

1) FU indicator有以下格式:

  +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |F|NRI|  Type   |
  +---------------+

FU指示字節的類型域的28,29表示FU-A和FU-B。NRI域的值必須根據分片NAL單元的NRI域的值設置。(此處Type就是rtp分片類型) 見下表

  .Type   Packet      Type name                       
  ---------------------------------------------------------
  0      undefined                                    -
  1-23   NAL unit    Single NAL unit packet per H.264  
  24     STAP-A     Single-time aggregation packet    
  25     STAP-B     Single-time aggregation packet    
  26     MTAP16    Multi-time aggregation packet     
  27     MTAP24    Multi-time aggregation packet     
  28     FU-A      Fragmentation unit                
  29     FU-B      Fragmentation unit                 
  30-31  undefined  

2 ) FU header的格式如下:

  +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |S|E|R|  Type   |
  +---------------+

S: 1 bit 當設置成1,開始位指示分片NAL單元的開始。當跟隨的FU荷載不是分片NAL單元荷載的開始,開始位設爲0。

E: 1 bit 當設置成1, 結束位指示分片NAL單元的結束,即, 荷載的最後字節也是分片NAL單元的最後一個字節。
當跟隨的FU荷載不是分片NAL單元的最後分片,結束位設置爲0。

R: 1 bit
保留位必須設置爲0,接收者必須忽略該位。

Type: 5 bits
此處的Type就是NALU頭中的Type,取1-23的那個值,表示 NAL單元荷載類型定義,見圖4
 

4、拆包和解包


拆包:當編碼器在編碼時需要將原有一個NAL按照FU-A進行分片,原有的NAL的單元頭與分片後的FU-A的單元頭有如下關係:

原始的NAL頭的前三位爲FU indicator的前三位,原始的NAL頭的後五位爲FU header的後五位,

FU indicator與FU header的剩餘位數根據實際情況決定。

解包:當接收端收到FU-A的分片數據,需要將所有的分片包組合還原成原始的NAl包時,FU-A的單元頭與還原後的NAL的關係如下:

還原後的NAL頭的八位是由FU indicator的前三位加FU header的後五位組成,即:

nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f)

參考:

https://blog.csdn.net/machh/article/details/52165292

https://www.jianshu.com/p/8edb448cf22e

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