1. 程式人生 > >H264語法結構及編碼原理

H264語法結構及編碼原理

----------------------

前言

-----------------------

       H264是新一代的編碼標準,以高壓縮高質量和支援多種網路的流媒體傳輸著稱,在編碼方面,我理解的他的理論依據是:參照一段時間內影象的統計結果表明,在相鄰幾幅影象畫面中,一般有差別的畫素只有10%以內的點,亮度差值變化不超過2%,而色度差值的變化只有1%以內。所以對於一段變化不大影象畫面,我們可以先編碼出一個完整的影象幀A,隨後的B幀就不編碼全部影象,只寫入與A幀的差別,這樣B幀的大小就只有完整幀的1/10或更小!B幀之後的C幀如果變化不大,我們可以繼續以參考B的方式編碼C幀,這樣迴圈下去。這段影象我們稱為一個序列(序列就是有相同特點的一段資料),當某個影象與之前的影象變化很大,無法參考前面的幀來生成,那我們就結束上一個序列,開始下一段序列,也就是對這個影象生成一個完整幀A1,隨後的影象就參考A1生成,只寫入與A1的差別內容。

        在H264協議裡定義了三種幀,完整編碼的幀叫I幀,參考之前的I幀生成的只包含差異部分編碼的幀叫P幀,還有一種參考前後的幀編碼的幀叫B幀。

        H264採用的核心演算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的演算法,幀間壓縮是生成B幀和P幀的演算法。

----------------------

序列的說明

----------------------

        在H264中影象以序列為單位進行組織,一個序列是一段影象編碼後的資料流,以I幀開始,到下一個I幀結束。

        一個序列的第一個影象叫做 IDR 影象(立即重新整理影象),IDR 影象都是 I 幀影象。

H.264 引入 IDR 影象是為了解碼的重同步,當解碼器解碼到 IDR 影象時,立即將參考幀佇列清空,將已解碼的資料全部輸出或拋棄,重新查詢引數集,開始一個新的序列。這樣,如果前一個序列出現重大錯誤,在這裡可以獲得重新同步的機會。IDR影象之後的影象永遠不會使用IDR之前的影象的資料來解碼。

        一個序列就是一段內容差異不太大的影象編碼後生成的一串資料流。當運動變化比較少時,一個序列可以很長,因為運動變化少就代表影象畫面的內容變動很小,所以就可以編一個I幀,然後一直P幀、B幀了。當運動變化多時,可能一個序列就比較短了,比如就包含一個I幀和3、4個P幀。

-----------------------

三種幀的說明

-----------------------

I幀:幀內編碼幀 ,I幀表示關鍵幀,你可以理解為這一幀畫面的完整保留;解碼時只需要本幀資料就可以完成(因為包含完整畫面)

I幀特點: 
1.它是一個全幀壓縮編碼幀。它將全幀影象資訊進行JPEG壓縮編碼及傳輸; 
2.解碼時僅用I幀的資料就可重構完整影象; 
3.I幀描述了影象背景和運動主體的詳情; 
4.I幀不需要參考其他畫面而生成; 
5.I幀是P幀和B幀的參考幀(其質量直接影響到同組中以後各幀的質量); 
6.I幀是幀組GOP的基礎幀(第一幀),在一組中只有一個I幀; 
7.I幀不需要考慮運動向量; 
8.I幀所佔資料的資訊量比較大。 


P幀:前向預測編碼幀。P幀表示的是這一幀跟之前的一個關鍵幀(或P幀)的差別,解碼時需要用之前快取的畫面疊加上本幀定義的差別,生成最終畫面。(也就是差別幀,P幀沒有完整畫面資料,只有與前一幀的畫面差別的資料)

 
P幀的預測與重構:P幀是以I幀為參考幀,在I幀中找出P幀“某點”的預測值和運動向量,取預測差值和運動向量一起傳送。在接收端根據運動向量從I幀中找出P幀“某點”的預測值並與差值相加以得到P幀“某點”樣值,從而可得到完整的P幀。
P幀特點: 
1.P幀是I幀後面相隔1~2幀的編碼幀; 
2.P幀採用運動補償的方法傳送它與前面的I或P幀的差值及運動向量(預測誤差); 
3.解碼時必須將I幀中的預測值與預測誤差求和後才能重構完整的P幀影象; 
4.P幀屬於前向預測的幀間編碼。它只參考前面最靠近它的I幀或P幀; 
5.P幀可以是其後面P幀的參考幀,也可以是其前後的B幀的參考幀; 
6.由於P幀是參考幀,它可能造成解碼錯誤的擴散; 
7.由於是差值傳送,P幀的壓縮比較高。 

B幀:雙向預測內插編碼幀。B幀是雙向差別幀,也就是B幀記錄的是本幀與前後幀的差別(具體比較複雜,有4種情況,但我這樣說簡單些),換言之,要解碼B幀,不僅要取得之前的快取畫面,還要解碼之後的畫面,通過前後畫面的與本幀資料的疊加取得最終的畫面。B幀壓縮率高,但是解碼時CPU會比較累。
 
B幀的預測與重構 
B幀以前面的I或P幀和後面的P幀為參考幀,“找出”B幀“某點”的預測值和兩個運動向量,並取預測差值和運動向量傳送。接收端根據運動向量在兩個參考幀中“找出(算出)”預測值並與差值求和,得到B幀“某點”樣值,從而可得到完整的B幀。
B幀特點 
1.B幀是由前面的I或P幀和後面的P幀來進行預測的; 
2.B幀傳送的是它與前面的I或P幀和後面的P幀之間的預測誤差及運動向量; 
3.B幀是雙向預測編碼幀; 
4.B幀壓縮比最高,因為它只反映丙參考幀間運動主體的變化情況,預測比較準確; 
5.B幀不是參考幀,不會造成解碼錯誤的擴散。 

注:I、B、P各幀是根據壓縮演算法的需要,是人為定義的,它們都是實實在在的物理幀。一般來說,I幀的壓縮率是7(跟JPG差不多),P幀是20,B幀可以達到50。可見使用B幀能節省大量空間,節省出來的空間可以用來儲存多一些I幀,這樣在相同位元速率下,可以提供更好的畫質。

--------------------------------

壓縮演算法的說明

--------------------------------

h264的壓縮方法:

1.分組:把幾幀影象分為一組(GOP,也就是一個序列),為防止運動變化,幀數不宜取多。 
2.定義幀:將每組內各幀影象定義為三種類型,即I幀、B幀和P幀; 
3.預測幀:以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀; 
4.資料傳輸:最後將I幀資料與預測的差值資訊進行儲存和傳輸。

        幀內(Intraframe)壓縮也稱為空間壓縮(Spatial compression)。當壓縮一幀影象時,僅考慮本幀的資料而不考慮相鄰幀之間的冗餘資訊,這實際上與靜態影象壓縮類似。幀內一般採用有失真壓縮演算法,由於幀內壓縮是編碼一個完整的影象,所以可以獨立的解碼、顯示。幀內壓縮一般達不到很高的壓縮,跟編碼jpeg差不多。  

        幀間(Interframe)壓縮的原理是:相鄰幾幀的資料有很大的相關性,或者說前後兩幀資訊變化很小的特點。也即連續的視訊其相鄰幀之間具有冗餘資訊,根據這一特性,壓縮相鄰幀之間的冗餘量就可以進一步提高壓縮量,減小壓縮比。幀間壓縮也稱為時間壓縮(Temporal compression),它通過比較時間軸上不同幀之間的資料進行壓縮。幀間壓縮一般是無損的。幀差值(Frame differencing)演算法是一種典型的時間壓縮法,它通過比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少資料量。

       順便說下有損(Lossy )壓縮和無損(Lossy less)壓縮。無失真壓縮也即壓縮前和解壓縮後的資料完全一致。多數的無失真壓縮都採用RLE行程編碼演算法。有失真壓縮意味著解壓縮後的資料與壓縮前的資料不一致。在壓縮的過程中要丟失一些人眼和人耳所不敏感的影象或音訊資訊,而且丟失的資訊不可恢復。幾乎所有高壓縮的演算法都採用有失真壓縮,這樣才能達到低資料率的目標。丟失的資料率與壓縮比有關,壓縮比越小,丟失的資料越多,解壓縮後的效果一般越差。此外,某些有失真壓縮演算法採用多次重複壓縮的方式,這樣還會引起額外的資料丟失。

============================================================================================

語法結構

名詞解釋

場和幀 :    視訊的一場或一幀可用來產生一個編碼影象。在電視中,為減少大面積閃爍現象,把一幀分成兩個隔行的場。

片:             每個圖象中,若干巨集塊被排列成片的形式。片分為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,實際傳輸中的資料流如圖所示:

264句法元素的分層結構

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->0x00000300
0x000001->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.流結束符在位元流中的最後。

附錄NAL型別定義:

nal_unit_type
NAL 單元和 RBSP 語法結構的內容 
0 未指定
1 一個非IDR影象的編碼條帶 
slice_layer_without_partitioning_rbsp( )
2 編碼條帶資料分割塊A 
slice_data_partition_a_layer_rbsp( )
3 編碼條帶資料分割塊B 
slice_data_partition_b_layer_rbsp( )
4 編碼條帶資料分割塊C 
slice_data_partition_c_layer_rbsp( )
5 IDR影象的編碼條帶 
slice_layer_without_partitioning_rbsp( )
6 輔助增強資訊 (SEI) 
sei_rbsp( )
7 序列引數集 
seq_parameter_set_rbsp( )
8 影象引數集 
pic_parameter_set_rbsp( )
9 訪問單元分隔符 
access_unit_delimiter_rbsp( )
10 序列結尾 
end_of_seq_rbsp( )
11 流結尾 
end_of_stream_rbsp( )
12 填充資料 
filler_data_rbsp( )
13 序列引數集擴充套件 
seq_parameter_set_extension_rbsp( )
14...18 保留
19 未分割的輔助編碼影象的編碼條帶 
slice_layer_without_partitioning_rbsp( )
20...23 保留
24...31 未指定

#define NALU_TYPE_SLICE     1
#define NALU_TYPE_DPA        2
#define NALU_TYPE_DPB        3
#define NALU_TYPE_DPC        4
#define NALU_TYPE_IDR         5
#define NALU_TYPE_SEI         6
#define NALU_TYPE_SPS       7
#define NALU_TYPE_PPS       8
#define NALU_TYPE_AUD       9
#define NALU_TYPE_EOSEQ     10
#define NALU_TYPE_EOSTREAM  11
#define NALU_TYPE_FILL          12

本文轉載自http://blog.csdn.net/yangzhongxuan/article/details/8003494 

                  http://blog.csdn.net/yangzhongxuan/article/details/8003504