參考:http://blog.csdn.net/ivy_reny/article/details/47144121
http://blog.sina.com.cn/s/blog_4ad7c2540101me90.html
從這一篇開始,我們詳細的講解H.264/AVC 比特碼流的句法和語義,可以說,能夠學習并掌握H.264的句法和語義,是能徹底掌握并應用H.264的關鍵。在前面幾篇文章中,我們隻是從理論層面,介紹了H.264、片、宏塊、幀内預測、幀間預測。而在句法和語義中,我們就可以拿到H.264編碼之後的裸流,用理論加實踐的方式,一步步探索H.264的編解碼實作過程。
而在這篇文章裡呢,我們就先從宏觀的角度,來看看使用H.264編碼之後,得到的裸流的分層結構是什麼樣的。
1. H264句法元素層次結構
(1)以往标準句法元素的分層結構
在H.264以往标準句法中,句法元素被組織成六個層次:序列(sequence)層、圖像組(gop)層、圖像(frame/field-picture)層、片/條帶(slice)層、宏塊(macroblock)層、子塊(sub-block)層。

上圖就是在之前标準中的分層結構,可以看到句法元素同畫面的劃分一樣,被組織成了有層次的結構,這種結構有助于更高效的節省碼流。但是這樣的結構,有幾個很大的缺點:
(1) 在這樣的結構中,每一層的頭部和它的資料部分形成管理與被管理的強依賴關系,頭部的句法元素是該層資料的核心,而一旦頭部丢失,資料部分的資訊幾乎不可能再被正确解碼出來,尤其在序列層及圖像層。
(2)在序列層及圖像層,因為資料量過大,不可能将所有的句法元素一次傳輸,這時假如頭部所在的分組丢失,那麼該層其他的資料,即使能正确接收也無法解碼。
(3)圖像層内的各個片之間,經常會攜帶相同的資料,造成碼流的浪費。
是以在此基礎上,H.264取消了圖像層和序列層,取而代之的,将原本屬于圖像層和序列層的大部分句法元素,抽取出來形成圖像參數集和序列參數集,其餘的部分,則放入片層。
(2)H.264的句法元素的分層結構
下圖為H.264圖像參數集和序列參數集,與片中句法元素的關系:
H.264參數集與片中句法元素的關系
從圖中可以看到,同一個序列參數集可以被多個序列中的圖像參數集引用,同一個圖像參數集也可以被多個圖像引用。是以我們在打開H.264碼流檔案時會看到,序列參數集和圖像參數集位于碼流的最前面。如果編碼器認為需要更新參數集時,會發送新的參數集。
在這種引用關系中,被引用方在時間上必須先被發送,是以在H.264建議中,參數集和參數集外部的句法元素,分别處于不同的信道中傳輸。
除了在參數上的改進,H.264在片層以下的句法元素上的結構,和之前的标準類似。而且因為取消了圖像層,片成為攜帶圖像像素資料的,最上層的資料機關。并且每個片必須攜帶所屬的圖像的編号、大小等資訊,這些資訊在同一個圖像的每個片中必須是一緻的。
以上就是H.264的句法元素的分層結構,它分别為序列、圖像、片、宏塊、子宏塊這5個層次。
- Sequence:序列起始碼後的序列頭中包含了圖像尺寸,寬高比,圖像速率等資訊。
- GOP:序列層下是圖像組層,一個圖像組由互相間有預測和生成關系的一組I、P、B圖像構成,但頭一幀圖像總是I幀。
- frame:圖像組層下是圖像,分為I、P、B三類。PIC頭中包含了圖像編碼的類型和時間參考資訊。
- slice:多個宏塊的組合。
- MB:宏塊大小為1616 , 對于亮度宏塊可以分為4個88的亮度色塊。
圖像組結構示意圖:
H264一個圖像序列的組成:SPS+PPS+SEI+一個I幀+若幹個P幀。SPS、PPS、SEI、一個I幀、一個P幀都 可以稱為一個NALU。
2. H.264資料元素
2.1. 序列(Sequence)
序列起始碼後的序列頭中包含了圖像尺寸,寬高比,圖像速率等資訊。
2.2. 圖像組(Gop)
參照一段時間内圖像的統計結果表明,在相鄰幾幅圖像畫面中,一般有差别的像素隻有10%以内的點,亮度內插補點變化不超過2%,而色度內插補點的變化隻有1%以内。
是以對于一段變化不大圖像畫面,我們可以先編碼出一個完整的圖像幀A,随後的B幀就不編碼全部圖像,隻寫入與A幀的差别,這樣B幀的大小就隻有完整幀的1/10或更小!B幀之後的C幀如果變化不大,我們可以繼續以參考B的方式編碼C幀,這樣循環下去。這段圖像我們稱為一個圖像組Gop(圖像組就是有相同特點的一段資料),當某個圖像與之前的圖像變化很大,無法參考前面的幀來生成,那我們就結束上一個Gop,開始下一個Gop,也就是對這個圖像生成一個完整幀A1,随後的圖像就參考A1生成,隻寫入與A1的差别内容。
在H264協定裡定義了三種幀,完整編碼的幀叫I幀,參考之前的I幀生成的隻包含差異部分編碼的幀叫P幀,還有一種參考前後的幀編碼的幀叫B幀。
H264采用的核心算法是幀内壓縮和幀間壓縮,幀内壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。
在H264中圖像以Gop為機關進行組織,一個Gop是一段圖像編碼後的資料流, 以I幀開始,到下一個I幀結束。
一個Gop的第一個圖像叫做 IDR 圖像(立即重新整理圖像),IDR 圖像都是 I 幀圖像。H.264 引入 IDR 圖像是為了解碼的重同步,當解碼器解碼到 IDR 圖像時,立即将參考幀隊列清空,将已解碼的資料全部輸出或抛棄,重新查找參數集,開始一個新的Gop。這樣,如果前一個Gop出現重大錯誤,在這裡可以獲得重新同步的機會。IDR圖像之後的圖像永遠不會使用IDR之前的圖像的資料來解碼。
一個Gop就是一段内容差異不太大的圖像編碼後生成的一串資料流。當運動變化比較少時,一個Gop可以很長,因為運動變化少就代表圖像畫面的内容變動很小,是以就可以編一個I幀,然後一直P幀、B幀了。當運動變化多時,可能一個Gop就比較短了,比如就包含一個I幀和3、4個P幀。
2.3. 圖像(picture)、幀(frame)、場(field)
視訊壓縮中,一幅圖像(picture)可以分成一幀(frame)或兩場(field)。
(1)幀内編碼幀/I 幀/關鍵幀
Intra codedframes/ Intra coded slices/ I-frames/ Key frames
在I幀中,所有宏塊都采用幀内預測的方式,是以解碼時僅用I幀的資料就可重構完整圖像,不需要參考其他畫面而生成。I幀可以用來快進快退,作為随機通路的參考點。I幀用來作為P幀和B幀的參考幀,其品質直接影響到其後各幀的品質。I幀描述了圖像背景和運動主體的詳情,可以幫助場景切換時重置畫面品質。當場景進行切換時,可以切換I幀進而更加高效地壓縮P幀和B幀(當然這要求編碼器要有場景切換檢測功能)。由于I幀僅進行幀内預測,沒有進行運動估計等幀間預測,是以I幀的碼率比較高。
在H.264中規定了兩種類型的I幀:普通I幀(normal Iframes)和IDR幀(InstantaneousDecoding Refresh, 即時解碼重新整理)。
IDR幀實質也是I幀,使用幀内預測。IDR幀的作用是立即重新整理,會導緻DPB(Decoded Picture Buffer參考幀清單)清空,而I幀不會。是以IDR幀承擔了随機通路功能,一個新的IDR幀開始,可以重新算一個新的Gop開始編碼,播放器永遠可以從一個IDR幀播放,因為在它之後沒有任何幀引用之前的幀。如果一個視訊中沒有IDR幀,這個視訊是不能随機通路的。所有位于IDR幀後的B幀和P幀都不能參考IDR幀以前的幀,而普通I幀後的B幀和P幀仍然可以參考I幀之前的其他幀。IDR幀阻斷了誤差的積累,而I幀并沒有阻斷誤差的積累。
I幀特點:
1)它是一個全幀壓縮編碼幀。它将全幀圖像資訊進行JPEG壓縮編碼及傳輸;
2)解碼時僅用I幀的資料就可重構完整圖像;
3)I幀描述了圖像背景和運動主體的詳情;
4)I幀不需要參考其他畫面而生成;
5)I幀是P幀和B幀的參考幀(其品質直接影響到同組中以後各幀的品質);
6)I幀是幀組GOP的基礎幀(第一幀),在一組中隻有一個I幀;
7)I幀不需要考慮運動矢量;
8)I幀所占資料的資訊量比較大。
I 幀編碼的基本流程:
① 進行幀内預測,決定所采用的幀内預測模式。
② 像素值減去預測值,得到殘差。
③ 對殘差進行變換和量化。
④ 變長編碼和算術編碼。
⑤ 重構圖像并濾波,得到的圖像作為其它幀的參考幀。
(2)預測幀/P幀/前向預測編碼幀
P幀屬于前向預測的幀間編碼,僅參考前面的最靠近它的I幀或者P幀進行幀間預測。編碼端在參考幀中找到P幀某點的預測值以及運動矢量,相減擷取預測內插補點,将預測內插補點和運動矢量進行編碼後傳輸。解碼端根據運動矢量從I幀中找出P幀某點的預測值并與預測內插補點相加得到P幀某點樣值,進而可得到完整的P幀。在以前的标準中(如MPEG-2),P幀解碼時隻使用前一幀作為參考,在H.264中,解碼時可以使用多幀已解碼的圖像作為參考。P幀可以是其後面P幀的參考幀,也可以是其前後的B幀的參考幀。由于P幀是參考幀,它可能造成解碼錯誤的擴散。由于是內插補點傳送,P幀的壓縮比較高。在H.264基本配置中僅僅存在I幀和P幀,不存在B幀。
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幀的壓縮比較高。
(3)B幀/雙向預測編碼幀
B幀以前面的I或P幀和後面的P幀為參考幀進行預測,編碼找出B幀某點的預測值和兩個運動矢量,并取預測內插補點和運動矢量傳送。解碼端根據運動矢量在兩個參考幀中找出預測值并與內插補點求和,得到B幀某點樣值,進而可得到完整的B幀。在以前的标準中(如MPEG-2),B幀不作為參考幀,可以使用較低的碼率、降低圖像品質而不會影響後續圖像,解碼時使用兩幀圖像作為參考。在H.264中,B幀可以作為參考幀,解碼時可以使用一幅、兩幅或多幅圖像作為參考,圖像幀的傳輸順序和顯示順序是不同的。B幀壓縮比最高,因為它隻反映兩參考幀間運動主體的變化情況,預測比較準确,但是解碼時CPU會比較累。
B幀特點:
1)B幀是由前面的I或P幀和後面的P幀來進行預測的;
2)B幀傳送的是它與前面的I或P幀和後面的P幀之間的預測誤差及運動矢量;
3)B幀是雙向預測編碼幀;
4)B幀壓縮比最高,因為它隻反映丙參考幀間運動主體的變化情況,預測比較準确;
5)B幀不是參考幀,不會造成解碼錯誤的擴散。
P 幀和 B 幀編碼的基本流程:
① 進行運動估計,計算采用幀間編碼模式的率失真函數(節)值。P幀隻參考前面的幀,B幀可參考後面的幀。
② 進行幀内預測,選取率失真函數值最小的幀内模式與幀間模式比較,确定采用哪種編碼模式。
③ 計算實際值和預測值的內插補點。
④ 對殘差進行變換和量化。
⑤ 熵編碼,如果是幀間編碼模式,編碼運動矢量。
注:I、B、P各幀是根據壓縮算法的需要,是人為定義的,它們都是實實在在的實體幀。一般來說,I幀的壓縮率是7(跟JPG差不多),P幀是20,B幀可以達到50。可見使用B幀能節省大量空間,節省出來的空間可以用來儲存多一些I幀,這樣在相同碼率下,可以提供更好的畫質。
2.4. 片/條帶(slice)
在H.264中,一幅圖像可以編碼為一個或多個片(slice),每個slice由宏塊組成,一個宏塊由一個16×16亮度像素和附加的一個8×8 Cb和一個8×8 Cr彩色像素塊組成。宏塊是H.264編碼的基本機關,可以采用不同的編碼類型。slice共有5種類型。**
slice的目的是為了限制誤碼的擴散和傳輸,使編碼片互相間保持獨立。
**一個slice編碼之後被打包進一個NALU,NALU除了容納slice還可以容納其它資料,如序列參數集SPS、PPS、SEI等。
類型 | 描述 | 支援的架構 |
---|---|---|
I frames/slices (Intra) | I宏塊 | 全部 |
P frames/slices (Predicted) | I宏塊、P宏塊 | 全部 |
B frames/slices (Bi-Predicted) | I宏塊、B宏塊 | 擴充和主 |
SI-frames/slices (switching I) | SI宏塊(一種特殊的幀内編碼宏塊),用于不同編碼流之間的切換 | 擴充 |
SP-frames/slices (switching P) | I宏塊、P宏塊,用于不同編碼流之間的切換 | 擴充 |
以下是片的句法結構:片頭規定了片的類型、屬于哪個圖像、有關的參考圖像等;片的資料包含了一系列宏塊和不編碼資料。
2.5. 宏塊(MB)
宏塊(Macro Block)是H.264編碼的基本機關,一個編碼圖像首先要劃分成多個塊(4x4 像素)才能進行處理,顯然宏塊應該是整數個塊組成,通常宏塊大小為16x16個像素。
宏塊分為I、P、B宏塊:
- I宏塊(幀内預測宏塊)隻能利用目前片中已解碼的像素作為參考進行幀内預測;
- P宏塊(幀間預測宏塊)可以利用前面已解碼的圖像作為參考圖像進行幀内預測;
- B宏塊(幀間雙向預測宏塊)則是利用前後向的參考圖形進行幀内預測
關于H.264的協定文檔:http://www.itu.int/rec/T-REC-H.264-200503-S/en