一、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)
參考: