1. 程式人生 > >RTP協議解析和H264碼流提取

RTP協議解析和H264碼流提取

一、 h264基礎概念

SODB: 資料位元串-->最原始的編碼資料

RBSP: 原始位元組序列載荷-->在SODB的後面填加了結尾位元(RBSP trailing bits 一個bit“1”)若干位元“0”,以便位元組對齊。

EBSP: 擴充套件位元組序列載荷– >在RBSP基礎上填加了仿校驗位元組(0X03)它的原因是: 在NALU加到Annexb上時,需要填加每組NALU之前的開始碼 StartCodePrefix,如果該NALU對應的slice為一幀的開始則用4位位元組表示,ox00000001,否則用3位位元組表示 ox000001.
為了使NALU主體中不包括與開始碼相沖突的,在編碼時,每遇到兩個位元組連續為0,就插入一個位元組的0x03。解碼時將0x03去掉。 也稱為脫殼操作。

H.264的功能分為兩層,視訊編碼層(VCL)和網路提取層(NAL)
VCL資料即被壓縮編碼後的視訊資料序列。把VCL資料要封裝到NAL單元中之後,才可以用來傳輸或儲存。NAL單元格式如下圖:
這裡寫圖片描述

H.264 的編碼視訊序列包括一系列的NAL 單元,每個NAL 單元包含一個RBSP,見表1。編碼片(包括資料分割片IDR 片)和序列RBSP 結束符被定義為VCL NAL 單元,其餘為NAL 單元。典型的RBSP 單元序列如圖2 所示。每個單元都按獨立的NAL 單元傳送。單元的資訊頭(一個位元組)定義了RBSP 單元的型別,NAL 單元的其餘部分為RBSP 資料。

這裡寫圖片描述

NAL單元

每個NAL單元是一個一定語法元素的可變長位元組字串,包括包含一個位元組的頭資訊(用來表示資料型別),以及若干整數字節的負荷資料。一個NAL單元可以攜帶一個編碼片、A/B/C型資料分割或一個序列或影象引數集。

NALU 頭由一個位元組組成, 它的語法如下:
這裡寫圖片描述

  NAL單元按RTP序列號按序傳送。其中,T為負荷資料型別,佔5bit;R為重要性指示位,佔2個bit;最後的F為禁止位,佔1bit。具體如下:
  (1)NALU型別位
  可以表示NALU的32種不同型別特徵,型別1~12是H.264定義的,型別24~31是用於H.264以外的,RTP負荷規範使用這其中的一些值來定義包聚合和分裂,其他值為H.264保留。
  (2)重要性指示位
  用於在重構過程中標記一個NAL單元的重要性,值越大,越重要。值為0表示這個NAL單元沒有用於預測,因此可被解碼器拋棄而不會有錯誤擴散;值高於0表示此NAL單元要用於無漂移重構,且值越高,對此NAL單元丟失的影響越大。
  (3)禁止位
編碼中預設值為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型別是我們判斷幀型別的利器,從官方文件中得出如下圖:

這裡寫圖片描述

H264(NAL簡介與I幀判斷)

這裡寫圖片描述

我們還是接著看最上面圖的碼流對應的資料來層層分析,以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

二、RTP Header解析


1)        V:RTP協議的版本號,佔2位,當前協議版本號為2

2)        P:填充標誌,佔1位,如果P=1,則在該報文的尾部填充一個或多個額外的八位組,它們不是有效載荷的一部分。

3)        X:擴充套件標誌,佔1位,如果X=1,則在RTP報頭後跟有一個擴充套件報頭

4)        CC:CSRC計數器,佔4位,指示CSRC 識別符號的個數

5)        M: 標記,佔1位,不同的有效載荷有不同的含義,對於視訊,標記一幀的結束;對於音訊,標記會話的開始。

6)        PT: 有效荷載型別,佔7位,用於說明RTP報文中有效載荷的型別,如GSM音訊、JPEM影象等,在流媒體中大部分是用來區分音訊流和視訊流的,這樣便於客戶端進行解析。

7)        序列號:佔16位,用於標識傳送者所傳送的RTP報文的序列號,每傳送一個報文,序列號增1。這個欄位當下層的承載協議用UDP的時候,網路狀況不好的時候可以用來檢查丟包。同時出現網路抖動的情況可以用來對資料進行重新排序,序列號的初始值是隨機的,同時音訊包和視訊包的sequence是分別記數的。

8)        時戳(Timestamp):佔32位,必須使用90 kHz 時鐘頻率。時戳反映了該RTP報文的第一個八位組的取樣時刻。接收者使用時戳來計算延遲和延遲抖動,並進行同步控制。

9)        同步信源(SSRC)識別符號:佔32位,用於標識同步信源。該識別符號是隨機選擇的,參加同一視訊會議的兩個同步信源不能有相同的SSRC。

10)    特約信源(CSRC)識別符號:每個CSRC識別符號佔32位,可以有0~15個。每個CSRC標識了包含在該RTP報文有效載荷中的所有特約信源。

注:基本的RTP說明並不定義任何頭擴充套件本身,如果遇到X=1,需要特殊處理

使用Wireshark抓一段碼流進行分析


其中,80                    是V_P_X_CC60                    是M_PT00 01              是SequenceNum00 00  00 00  是Timestamp
39 6e d3 46  是SSRC
把前兩位元組換成二進位制如下1000 0000 0110 0000按順序解釋如下:10               是V;0                 是P;0                 是X;0000          是CC;0                 是M;110 0000   是PT;

三、RTP荷載H264碼流

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

荷載格式定義三個不同的基本荷載結構,接收者可以通過RTP荷載的第一個位元組後5位(如上圖參考RFC3984)識別荷載結構。

1)   單個NAL單元包:荷載中只包含一個NAL單元。NAL頭型別域等於原始 NAL單元型別,即在範圍1到23之間

2)   聚合包:本型別用於聚合多個NAL單元到單個RTP荷載中。本包有四種版本,單時間聚合包型別A (STAP-A),單時間聚合包型別B (STAP-B),多時間聚合包型別(MTAP)16位位移(MTAP16), 多時間聚合包型別(MTAP)24位位移(MTAP24)。賦予STAP-A, STAP-B, MTAP16, MTAP24的NAL單元型別號分別是 24,25, 26, 27

3)   分片單元:用於分片單個NAL單元到多個RTP包。現存兩個版本FU-A,FU-B,用NAL單元型別 28,29標識

常用的打包時的分包規則是:如果小於MTU採用單個NAL單元包,如果大於MTU就採用FUs分片方式。因為常用的打包方式就是單個NAL包和FU-A方式,所以我們只解析這兩種。

3.1 單個NAL單元包

 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        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

定義在此的NAL單元包必須只包含一個。這意味聚合包和分片單元不可以用在單個NAL 單元包中。並且RTP序號必須符合NAL單元的解碼順序。NAL單元的第一位元組和RTP荷載頭第一個位元組重合。(如上圖參考RFC3984)打包H264碼流時,只需在幀前面加上12位元組的RTP頭即可。

3.2 分片單元(FU-A)

  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        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                 圖 1

分片只定義於單個NAL單元不用於任何聚合包。NAL單元的一個分片由整數個連續NAL單元位元組組成。每個NAL單元位元組必須正好是該NAL單元一個分片的一部分。相同NAL單元的分片必須使用遞增的RTP序號連續順序傳送(第一和最後分片之間沒有其他的RTP包)。相似,NAL單元必須按照RTP順序號的順序裝配。

   當一個NAL單元被分片運送在分片單元(FUs)中時,被引用為分片NAL單元。STAPs,MTAPs不可以被分片。 FUs不可以巢狀。 即, 一個FU 不可以包含另一個FU。運送FU的RTP時戳被設定成分片NAL單元的NALU時刻。

圖 1表示FU-A的RTP荷載格式。FU-A由1位元組的分片單元指示(如圖2),1位元組的分片單元頭(如圖3),和分片單元荷載組成。

  +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |F|NRI|  Type   |
  +---------------+
        圖 2
FU指示位元組的型別域Type=28表示FU-A。。NRI域的值必須根據分片NAL單元的NRI域的值設定。
 +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |S|E|R|  Type   |
  +---------------+
         圖 3

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

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

R: 1 bit 保留位必須設定為0,接收者必須忽略該位

打包時,原始的NAL頭的前三位為FU indicator的前三位,原始的NAL頭的後五位為FU header的後五位。

使用Wireshark抓一段碼流進行分析


前12位元組是RTP Header
7c是FU indicator
85是FU Header
FU indicator(0x7C)和FU Header(0x85)換成二進位制如下
0111 1100 1000 0101
按順序解析如下:0                            是F11                          是NRI11100                    是FU Type,這裡是28,即FU-A1                            是S,Start,說明是分片的第一包0                            是E,End,如果是分片的最後一包,設定為1,這裡不是0                            是R,Remain,保留位,總是000101                   是NAl Type,這裡是5,說明是關鍵幀(不知道為什麼是關鍵幀請自行谷歌)打包時,FUindicator的F、NRI是NAL Header中的NRI,Type是28;FU Header的S、E、R分別按照分片起始位置設定,Type是NAL Header中的Type。解包時,取FU indicator的前三位和FU Header的後五位,即0110 0101(0x65)為NAL型別(即I幀)。Refrences:http://blog.csdn.net/machh/article/details/52165292