視訊格式封裝——H264
轉自:
http://blog.csdn.net/yangzhongxuan/article/details/8003494
http://blog.csdn.net/gl1987807/article/details/11946025
名詞解釋
場和幀 : 視訊的一場或一幀可用來產生一個編碼影象。在電視中,為減少大面積閃爍現象,把一幀分成兩個隔行的場。
片: 每個圖象中,若干巨集塊被排列成片的形式。片分為I片、B片、P片和其他一些片。
I片只包含I巨集塊,P片可包含P和I巨集塊,而B片可包含B和I巨集塊。
I巨集塊利用從當前片中已解碼的畫素作為參考進行幀內預測。
P巨集塊利用前面已編碼圖象作為參考圖象進行幀內預測。
B巨集塊則利用雙向的參考圖象(前一幀和後一幀)進行幀內預測。
片的目的是為了限制誤碼的擴散和傳輸,使編碼片相互間是獨立的。
某片的預測不能以其它片中的巨集塊為參考影象,這樣某一片中的預測誤差才不會傳播到其它片中去。
巨集塊 : 一個編碼影象通常劃分成若干巨集塊組成,一個巨集塊由一個16×16亮度畫素和附加的一個8×8 Cb和一個8×8 Cr彩色畫素塊組成。
資料之間的關係:
H264結構中,一個視訊影象編碼後的資料叫做一幀,一幀由一個片(slice)或多個片組成,一個片由一個或多個巨集塊(MB)組成,一個巨集塊由16x16的yuv資料組成。巨集塊作為H264編碼的基本單位。
H264編碼過程中的三種不同的資料形式:SODB 資料位元串 ---->最原始的編碼資料,即VCL資料;
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去掉。也稱為脫殼操作。
H264/AVC 的分層結構
H.264的主要目標是:1.高的視訊壓縮比;
2.良好的網路親和性;
為了完成這些目標H264的解決方案是:
1.VCL video coding layer 視訊編碼層;
2.NAL network abstraction layer 網路提取層;
其中,VCL層是對核心演算法引擎,塊,巨集塊及片的語法級別的定義,他最終輸出編碼完的資料 SODB;
NAL層定義片級以上的語法級別(如序列引數集和影象引數集,針對網路傳輸),
同時支援以下功能:獨立片解碼,起始碼唯一保證,SEI以及流格式編碼資料傳送,NAL層將SODB打包成RBSP然後加上NAL頭,組成一個NALU(NAL單元)。
H264網路傳輸的結構
H264在網路傳輸的是NALU,NALU的結構是:NAL頭+RBSP,實際傳輸中的資料流如圖所示:
NALU頭用來標識後面的RBSP是什麼型別的資料,他是否會被其他幀參考以及網路傳輸是否有錯誤。
NALU頭結構
長度:1byte
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
1.forbidden_bit: 禁止位,初始為0,當網路發現NAL單元有位元錯誤時可設定該位元為1,以便接收方糾錯或丟掉該單元。
2.nal_reference_bit: nal重要性指示,標誌該NAL單元的重要性,值越大,越重要,解碼器在解碼處理不過來的時候,可以丟掉重要性為0的NALU。
不同型別的NALU的重要性指示如下表所示。
nal_unit_type |
NAL型別 |
nal_reference_bit |
0 |
未使用 |
0 |
1 |
非IDR的片 |
此片屬於參考幀,則不等於0, 不屬於參考幀,則等與0 |
2 |
片資料A分割槽 |
同上 |
3 |
片資料B分割槽 |
同上 |
4 |
片資料C分割槽 |
同上 |
5 |
IDR影象的片 |
5 |
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幀參考。
從這個表我們也可以看出來,DIR的I幀是非常重要的,他一丟,那麼這個序列的所有幀都沒辦法解碼了;序列引數集和影象引數集也很重要,沒有序列引數集,這個序列的幀就沒法解;
沒有影象引數集,那用到這個影象引數集的幀都沒法解。
3.nal_unit_type:NALU型別取值如下表所示。
nal_unit_type |
NAL型別 |
C |
0 |
未使用 |
|
1 |
非IDR影象中不採用資料劃分的片段 |
2,3,4 |
2 |
非IDR影象中A類資料劃分片段 |
2 |
3 |
非IDR影象中B類資料劃分片段 |
3 |
4 |
非IDR影象中C類資料劃分片段 |
4 |
5 |
IDR影象的片 |
2,3 |
6 |
補充增強資訊單元(SEI) |
5 |
7 |
序列引數集 |
0 |
8 |
影象引數集 |
1 |
9 |
分界符 |
6 |
10 |
序列結束 |
7 |
11 |
碼流結束 |
8 |
12 |
填充 |
9 |
13..23 |
保留 |
|
24..31 |
不保留(RTP打包時會用到) |
RTP 打包時的擴充套件型別
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 |
RBSP
RBSP資料是下表中的一種
RBSP型別 | 所寫 | 描述 |
引數集 | PS | 序列的全域性資訊,如影象尺寸,視訊格式等 |
增強資訊 | SEI | 視訊序列解碼的增強資訊 |
影象界定符 | PD | 視訊影象的邊界 |
編碼片 | SLICE | 編碼片的頭資訊和資料 |
資料分割 | DP片層的資料,用於錯誤恢復解碼 | |
序列結束符 | 表明一個序列的結束,下一個影象為IDR影象 | |
流結束符 | 表明該流中已沒有影象 | |
填充資料 | 亞元資料,用於填充位元組 |
從前面的分析我們知道,VCL層出來的是編碼完的視訊幀資料,
這些幀可能是I、B、P幀,而且這些幀可能屬於不同的序列,再者同一個序列還有相對應的一套序列引數集和圖片引數集等等,
所以要完成視訊的解碼,不僅需要傳輸VCL層編碼出來的視訊幀資料,還需要傳輸序列引數集、影象引數集等資料。
引數集:包括序列引數集 SPS 和影象引數集 PPS
SPS 包含的是針對一連續編碼視訊序列的引數,如識別符號 seq_parameter_set_id、幀數及 POC 的約束、參考幀數目、解碼影象尺寸和幀場編碼模式選擇標識等等。
PPS對應的是一個序列中某一幅影象或者某幾幅影象,
其引數如識別符號 pic_parameter_set_id、可選的 seq_parameter_set_id、熵編碼模式選擇標識、片組數目、初始量化引數和去方塊濾波係數調整標識等等。
資料分割:組成片的編碼資料存放在 3 個獨立的 DP(資料分割,A、B、C)中,各自包含一個編碼片的子集。
分割A包含片頭和片中每個巨集塊頭資料。
分割B包含幀內和 SI 片巨集塊的編碼殘差資料。
分割 C包含幀間巨集塊的編碼殘差資料。
每個分割可放在獨立的 NAL 單元並獨立傳輸。
NAL的開始和結束編碼器將每個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->0x000003000x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解碼器檢測到0x000003時,把03拋棄,恢復原始資料(脫殼操作)。解碼器在解碼時,首先逐個位元組讀取NAL的資料,統計NAL的長度,然後再開始解碼。
NALU的順序要求
H.264/AVC標準對送到解碼器的NAL單元順序是有嚴格要求的,如果NAL單元的順序是混亂的,必須將其重新依照規範組織後送入解碼器,否則解碼器不能夠正確解碼。
1.序列引數集NAL單元
必須在傳送所有以此引數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的序列引數集NAL單元。
所謂重複的詳細解釋為:序列引數集NAL單元都有其專門的標識,如果兩個序列引數集NAL單元的標識相同,就可以認為後一個只不過是前一個的拷貝,而非新的序列引數集。
2.影象引數集NAL單元
必須在所有以此引數集為參考的其他NAL單元之前傳送,不過允許這些NAL單元中間出現重複的影象引數集NAL單元,這一點與上述的序列引數集NAL單元是相同的。
3.不同基本編碼影象中的片段(slice)單元和資料劃分片段(data partition)單元在順序上不可以相互交叉,即不允許屬於某一基本編碼影象的一系列片段(slice)單元和資料劃分片段(data partition)單元中忽然出現另一個基本編碼影象的片段(slice)單元片段和資料劃分片段(data partition)單元。
4.參考影象的影響:如果一幅影象以另一幅影象為參考,則屬於前者的所有片段(slice)單元和資料劃分片段(data partition)單元必須在屬於後者的片段和資料劃分片段之後,無論是基本編碼影象還是冗餘編碼影象都必須遵守這個規則。
5.基本編碼影象的所有片段(slice)單元和資料劃分片段(data partition)單元必須在屬於相應冗餘編碼影象的片段(slice)單元和資料劃分片段(data partition)單元之前。
6.如果資料流中出現了連續的無參考基本編碼影象,則影象序號小的在前面。
7.如果arbitrary_slice_order_allowed_flag置為1,一個基本編碼影象中的片段(slice)單元和資料劃分片段(data partition)單元的順序是任意的,如果arbitrary_slice_order_allowed_flag置為零,則要按照片段中第一個巨集塊的位置來確定片段的順序,若使用資料劃分,則A類資料劃分片段在B類資料劃分片段之前,B類資料劃分片段在C類資料劃分片段之前,而且對應不同片段的資料劃分片段不能相互交叉,也不能與沒有資料劃分的片段相互交叉。
8.如果存在SEI(補充增強資訊)單元的話,它必須在它所對應的基本編碼影象的片段(slice)單元和資料劃分片段(data partition)單元之前,並同時必須緊接在上一個基本編碼影象的所有片段(slice)單元和資料劃分片段(data partition)單元后邊。假如SEI屬於多個基本編碼影象,其順序僅以第一個基本編碼影象為參照。
9.如果存在影象分割符的話,它必須在所有SEI 單元、基本編碼影象的所有片段slice)單元和資料劃分片段(data partition)單元之前,並且緊接著上一個基本編碼影象那些NAL單元。
10.如果存在序列結束符,且序列結束符後還有影象,則該影象必須是IDR(即時解碼器重新整理)影象。序列結束符的位置應當在屬於這個IDR影象的分割符、SEI 單元等資料之前,且緊接著前面那些影象的NAL單元。如果序列結束符後沒有影象了,那麼它的就在位元流中所有影象資料之後。
11.流結束符在位元流中的最後。
/**********************************************************************************/
H264元素的分層結構
H.264編碼器輸出的Bit流中,每個Bit都隸屬於某個句法元素。句法元素被組織成有層次的結構,分別描述各個層次的資訊。
在H.264 中,句法元素共被組織成 序列、影象、片、巨集塊、子巨集塊五個層次。在這樣的結構中,每一層的頭部和它的資料部分形成管理與被管理的強依賴關係,頭部的句法元素是該層資料的核心,而一旦頭部丟失,資料部分的資訊幾乎不可能再被正確解碼出來,尤其在序列層及影象層。
在 H.264 中,分層結構最大的不同是取消了序列層和影象層,並將原本屬於序列和影象頭部的大部分句法元素遊離出來形成序列和影象兩級引數集,其餘的部分則放入片層。
引數集是一個獨立的資料單位,不依賴於引數集外的其他句法元素。一個引數集不對應某一個特定的影象或序列,同一序列引數集可以被多個影象引數集引用,同理,同一個影象引數集也可以被多個影象引用。只在編碼器認為需要更新引數集的內容時,才會發出新的引數集。
複雜通訊中的碼流中可能出現的資料單位:
IDR: 在H.264中,影象以序列為單位進行組織。一個序列的第一個影象叫做 IDR 影象(立即重新整理影象),IDR 影象都是 I 幀影象。H.264 引入 IDR 影象是為了解碼的重同步,當解碼器解碼到 IDR 影象時,立即將參考幀佇列清空,將已解碼的資料全部輸出或拋棄,重新查詢引數集,開始一個新的序列。這樣,如果前一個序列出現重大錯誤,在這裡可以獲得重新同步的機會。IDR影象之後的影象永遠不會使用IDR之前的影象的資料來解碼。 IDR 影象一定是 I 影象,但 I 影象不一定是 IDR 影象。I幀之後的影象有可能會使用I幀之前的影象做運動參考。
H264碼流結構 1. H264分層結構 H.263定義的碼流結構是分級結構,共四層。自上而下分別為:影象層(picturelayer)、塊組層(GOB layer)、巨集塊層(macroblock layer)和塊層(block layer)。而與H.263相比,H.264的碼流結構和H.263的有很大的區別,它採用的不再是嚴格的分級結構。 H.264的功能分為兩層,視訊編碼層(VCL)和網路提取層(NAL)VCL資料即被壓縮編碼後的視訊資料序列。在VCL資料要封裝到NAL單元中之後,才可以用來傳輸或儲存。 NAL單元格式[2] 表1所示:
表1 NAL單元格式 | |||
NAL頭 | RBSP | NAL頭 | RBSP |
RBSP 型別之一 PS:包括序列引數集 SPS 和 影象引數集 PPS
SPS包含的是針對一連續編碼視訊序列的引數,如識別符號 seq_parameter_set_id、幀數及 POC 的約束、參考幀數目、解碼影象尺寸和幀場編碼模式選擇標識等等。
PPS對應的是一個序列中某一幅影象或者某幾幅影象,其引數如識別符號 pic_parameter_set_id、可選的 seq_parameter_set_id、熵編碼模式選擇標識、片組數目、初始量化引數和去方塊濾波係數調整標識等等。 NALU型別
標識NAL單元中的RBSP資料型別,其中,nal_unit_type為1, 2, 3, 4, 5及12的NAL單元稱為VCL的NAL單元,其他型別的NAL單元為非VCL的NAL單元。
0:未規定
1:非IDR影象中不採用資料劃分的片段
2:非IDR影象中A類資料劃分片段
3:非IDR影象中B類資料劃分片段
4:非IDR影象中C類資料劃分片段
5:IDR影象的片段
6:補充增強資訊 (SEI)
7:序列引數集
8:影象引數集
9:分割符
10:序列結束符
11:流結束符
12:填充資料
13 – 23:保留
24 – 31:未規定
2. H.264碼流結構圖 通過相關知識的查閱,概括出H.264的碼流結構圖[2]如圖1所示:
圖1 H.264的碼流結構 |
3 H.264碼流分析的應用 在有些時候,需要從H.264碼流中直接取得相關資訊(如:影象的寬度和影象的高度等等資訊)。下面介紹下取得相關資訊的方法: 影象的相關資訊儲存在網路提取層(NAL)的RBSP結構中,要取得影象的相關資訊,既要獲得影象的相關位。需依據RBSP結構,獲得pic_width_in_mbs_minus1和pic_height_in_map_units_minus1兩個值,那麼寬度為(pic_width_in_mbs_minus1+1)*16,高度為(pic_height_in_map_units_minus1+1)*16,但是有些情況還得考慮nNum_Ref_Frames的值,一般為1。 3.1獲得試驗資料 裝置:SUNNIC(IP Cam) 名字:ST100factory Firmware版本:p8b8 視訊格式:H.264 (1)將裝置解析度設成176*144,使用Ethereal等抓包工具抓得一組資料,並去掉相應的RTP頭後,該資料為0x00,0x00,0x00,0x01,0x67,0x42,0x00,0x1E,0x99,0xA0,0xB1,0x31。 (2)將裝置解析度設成720*240,使用Ethereal等抓包工具抓得一組資料,並去掉相應的RTP頭後,該資料為0x00,0x00,0x00,0x01,0x67,0x42,0xE0,0x1E,0xDA,0x82,0xD1,0xF1。 (3)將裝置解析度設成720*480,使用Ethereal等抓包工具抓得一組資料,並去掉相應的RTP頭後,該資料為0x00,0x00,0x00,0x01,0x67,0x42,0xE0,0x1E,0xDB,0x82,0xD1,0xF1。