H264
H264結構中,一個視頻圖像編碼後的數據叫做一幀
,一幀由一個片(slice)
或多個片組成,一個片
由一個或多個宏塊(MB)
組成,一個宏塊由16x16的yuv
數據組成。宏塊作爲H264編碼的基本單位。
一個宏塊由一個16×16
亮度像素和附加的一個8×8 Cb
和一個8×8 Cr
彩色像素塊組成,我們常見的YUV
格式I420
。
SODB(數據比特串)
最原始的編碼數據,即VCL數據;
RBSP(原始字節序列載荷)
在SODB
的後面填加了結尾比特(RBSP trailing bits 一個bit“1”)若干比特“0”,以便字節對齊;
EBSP( 擴展字節序列載荷)
在RBSP
基礎上填加了仿校驗字節(0X03)它的原因是:在 NALU
加到Annexb
上時,需要添加每組NALU之前的開始碼StartCodePrefix(0x00000001 or 0x000001)
,如果該NALU對應的slice
爲一幀
的開始則用4位字節
表示,0x00000001
,否則用3位字節表示0x000001
(是一幀的一部分)。另外,爲了使NALU主體
中不包括與開始碼
相沖突的,在編碼時,每遇到兩個字節連續爲0,就插入一個字節的0x03
。解碼時將0x03去掉。也稱爲脫殼操作
。
注:
0x00
表示16
進制2位,則2進制的八位(8bit = 1byte
)。即一個字節。所以0x00 00 00 01
是4位字節,0x00 00 01
是3位字節。
數據構成 NAL
NAL (Network Abstract Layer)
, 即網絡抽象層。
一般來說編碼器編出的首幀數據爲PPS
與SPS
,接着爲I幀
,然後是P幀
編碼器將每個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
,這樣:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解碼器檢測到0x000003
時,把03
拋棄,恢復原始數據(脫殼操作)。解碼器在解碼時,首先逐個字節讀取NAL的數據,統計 NAL
的長度,然後再開始解碼。
網絡傳輸結構 NALU
H264
在網絡傳輸的是NALU
,NALU
的結構是:NAL頭+RBSP
,實際傳輸中的數據流如圖所示:
NALU
頭用來標識後面的RBSP
是什麼類型
的數據,是否會被其他幀參考
以及網絡傳輸
是否有錯誤。
NAL Header
NALU頭結構
長度:1byte(1字節) = 8位
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
forbidden_bit
:禁止位,初始爲0,當網絡發現NAL單元有比特錯誤時可設置該比特爲1,以便接收方糾錯或丟掉該單元。nal_reference_bit
:nal重要性指示,標誌該NAL單元的重要性,值越大,越重要,解碼器在解碼處理不過來的時候,可以丟掉重要性爲0的NALU
。
例如: 0x65
表示關鍵幀IDR
0 11 00101
禁止位 = 0
相關性 = 11
NAL類型 = 5
這裏以16進制顯示 2^4=16,即4位,即0110 = 6,0101 = 5
最後數據爲0x65
即一個起始碼0x00000001 + 0x65 + RBSP
0x000001的開頭一般是當一幀數據比較大時,需要拆分爲幾個子段,子段的開頭就是0x000001開頭的,一幀的開頭就是3個00的0x00000001 (即4位字節)。
nal_unit_type |
NAL類型 |
nal_reference_bit |
0 |
未使用 |
0 |
1 |
非IDR的片 |
此片屬於參考幀,則不等於0, 不屬於參考幀,則等與0 |
2 |
片數據A分區 |
同上 |
3 |
片數據B分區 |
同上 |
4 |
片數據C分區 |
同上 |
5 |
IDR圖像的片 |
6 |
6 |
補充增強信息單元(SEI) |
0 |
7 |
序列參數集 |
非0 |
8 |
圖像參數集 |
非0 |
9 |
分界符 |
0 |
10 |
序列結束 |
0 |
11 |
碼流結束 |
0 |
12 |
填充 |
0 |
13..23 |
保留 |
0 |
24..31 |
不保留 |
0 |
所謂參考幀,就是在其他幀解碼時需要參照的幀。比如一個I幀
可能被一個或多個B幀
參考,一個B幀
可能被某個P幀
參考。
IDR
的I幀
是非常重要的,他一丟,那麼這個序列的所有幀都沒辦法解碼了
序列參數集(SPS)
和圖像參數集(PPS)
也很重要,沒有序列參數集(SPS)
,這個序列的幀就沒法解;
沒有圖像參數集(PPS)
,那用到這個圖像參數集(PPS)
的幀都沒法解。
NALU順序
H.264/AVC標準
對送到解碼器的NAL
單元順序是有嚴格要求的,如果NAL單元
的順序是混亂的,必須將其重新依照規範組織
後送入解碼器,否則解碼器不能夠正確解碼。
-
序列參數集SPS
必須在傳送所有以此參數集爲參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的序列參數集NAL單元。
所謂重複的詳細解釋爲:序列參數集NAL單元都有其專門的標識,如果兩個序列參數集NAL單元的標識相同,就可以認爲後一個只不過是前一個的拷貝,而非新的序列參數集。
-
圖像參數集PPS
必須在所有以此參數集爲參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的圖像參數集NAL單元,這一點與上述的序列參數集NAL單元是相同的。
-
不同基本編碼圖像中的片段(slice)單元和數據劃分片段(data partition)單元在順序上不可以相互交叉,即不允許屬於某一基本編碼圖像的一系列片段(slice)單元和數據劃分片段(data partition)單元中忽然出現另一個基本編碼圖像的片段(slice)單元片段和數據劃分片段(data partition)單元。
-
參考圖像的影響
:如果一幅圖像P2
以另一幅圖像P1
爲參考,則屬於P2
的所有片段(slice)單元和數據劃分片段(data partition)單元必須在屬於P1
的片段和數據劃分片段之後,無論是基本編碼圖像還是冗餘編碼圖像都必須遵守這個規則。 -
基本編碼圖像的所有片段(
slice
)單元和數據劃分片段(data partition
)單元必須在屬於相應冗餘編碼圖像的片段(slice
)單元和數據劃分片段(data partition
)單元之前。 -
如果數據流中出現了連續的無參考基本編碼圖像,則圖像序號小的在前面。
-
如果
arbitrary_slice_order_allowed_flag
置爲1,一個基本編碼圖像中的片段(slice
)單元和數據劃分片段(data partition
)單元的順序是任意的,如果arbitrary_slice_order_allowed_flag
置爲零,則要按照片段中第一個宏塊的位置來確定片段的順序,若使用數據劃分,則A類
數據劃分片段在B類
數據劃分片段之前,B類
數據劃分片段在C類
數據劃分片段之前,而且對應不同片段的數據劃分片段不能相互交叉,也不能與沒有數據劃分的片段相互交叉。 -
如果存在
SEI(補充增強信息)
單元的話,它必須在它所對應的基本編碼圖像的片段(slice
)單元和數據劃分片段(data partition
)單元之前,並同時必須緊接在上一個基本編碼圖像的所有片段(slice
)單元和數據劃分片段(data partition
)單元后邊。假如SEI
屬於多個基本編碼圖像,其順序僅以第一個基本編碼圖像爲參照。 -
如果存在圖像分割符的話,它必須在所有
SEI 單元
、基本編碼圖像的所有片段slice
)單元和數據劃分片段(data partition
)單元之前,並且緊接着上一個基本編碼圖像那些NAL單元
。 -
如果存在序列結束符,且序列結束符後還有圖像,則該圖像必須是
IDR(即時解碼器刷新)圖像
。序列結束符的位置應當在屬於這個IDR
圖像的分割符、SEI 單元
等數據之前,且緊接着前面那些圖像的NAL
單元。如果序列結束符後沒有圖像了,那麼它的就在比特流中所有圖像數據之後。 -
流結束符在比特流中的最後。
參考自文章:
https://blog.csdn.net/yuanchunsi/article/details/73194569
https://blog.csdn.net/yangzhongxuan/article/details/8003494