H.264 的句法和語義

H.264 的句法和語義


在編碼器輸出的碼流中,數據的基本單位是句法元素,每個句法元素由若干比特組成,它表示某個特定的物理意義,例如:宏塊類型、量化參數等。句 法表徵句法元素的組織結構,語義闡述句法元素的具體含義 。所有的視頻編碼標準都是通過定義句法和語義來規範編解碼器的工作流程。

 

句法元素的分層結構

編碼器 輸出的比特碼流中,每個比特都隸屬某個句法元素,也就是說,碼流是由一個個句法元素依次銜接組成的,碼流中除了句法元素並不存在專門用於控制或同步的內 容 。在H.264 定義的碼流中,句法元素被組織成有層次的結構,分別描述各個層次的信息。

句法元素的分層結構有助於更有效地節省碼流。例如,在一個圖像中,經常會在各個片 之間有相同的數據,如果每個片都同時攜帶這些數據,勢必會造成碼流的浪費。更爲有效的做法是將該圖像的公共信息抽取出來,形成圖像一級的句法元素,而在片 級只攜帶該片自身獨有的句法元素。

在H.264 中,句法元素共被組織成 序列、圖像、片、宏塊、子宏塊五個層次。H.264 的分層結構是經過精心設計的,與以往的視頻編碼標準相比有很大的改進,這些改進主要針對傳輸中的錯誤掩藏,在有誤碼發生時可以提高圖像重建的性能。

在以往 的標準中,分層的組織結構如圖7.2,它們如同TCP/IP 協議的結構,每一層都有頭部,然後在每層的數據部分包含該層的數據。在這樣的結構中,每一層的頭部和它的數據部分形成管理與被管理的強依賴關係,頭部的句 法元素是該層數據的核心,而一旦頭部丟失,數據部分的信息幾乎不可能再被正確解碼出來。尤其在序列層及圖像層,由於網絡 中 MTU(最大傳輸單元)大小的限制,不可能將整個層的句法元素全部放入同一個分組中,這個時候如果頭部所在的分組丟失,該層其他分組即使能被正確接收也無 法解碼,造成資源浪費。(應該可以這樣理解,也就是說,一個圖像層作爲一個單元被交付給IP層,但是被分割成了幾個分組,如果圖像層的頭部信息的分組丟 失,那麼這個圖像層的所有片都無法被解碼)          
                                        

H.264 中,分層結構最大的不同是取消了序列層和圖像層 ,並將原本屬於序列和圖像頭部的
大部分句法元素遊離出來形成序列和圖像兩級參數集,其餘的部分則放入片層。參數集是一個獨立的數據單位,不依賴於參數集外的其他句法元素 。圖7.3 描述了參數集與參數集外句法元素的關係,在圖中我們可以看到,參數集只是在片層句法元素需要的時候被引用,而且,一個參數集並不對應某個特定的圖像或序 列,同一個序列參數集可以被多個序列中的圖像參數集引用,同理,同一個圖像參數集也可以被多個圖像引用。只在編碼器認爲需要更新參數集的內容時,纔會發送 出新的參數集。在這種機制下,由於參數集是獨立的,可以被多次重發或者採用特殊技術加以保護

在圖7.3 的描述中,參數集與參數集外部的句法元素處於不同信道中,這是H.264 的一個建議 ,我們可以使用更安全但成本更昂貴的通道來傳輸參數集,而使用成本低但不夠可靠的信道傳輸其他句法元素,只需要保證片層中的某個句法元素需要引 用某個參數集時,那個參數集已經到達解碼器,也就是參數集在時間上必須先被傳送。當然,在條件不允許的情況下,我們也可以採用妥協的辦法:在同一個物理信 道中傳輸所有的句法元素,但專門爲參數集採用安全可靠的通信協議,如TCP 。當然,H.264 也允許我們爲包括參數集在內的所有句法元素指定同樣的通信協議,但這時所有參數集必須被多次重發 ,以保證解碼器最終至少能接收到一個。

在參數集和片使用同 個物理信道的情況下,圖7.3 中的信道1 和信道2 應該被理解爲邏輯上的信道,因爲從邏輯上看,參數集與其它句法元素還是處於各自彼此獨立的信道中。

H.264 在片層增加了新的句法元素指明所引用的參數集的編號,同時因爲取消了圖像層,片成爲了信道2 中最上層的獨立的數據單位,每個片必須自己攜帶關於所屬圖像的編號、大小等基本信息,這些信息在同一圖像的每個片中都必須是一致的在編碼時,H.264 的規範要求將參數集、片這些獨立的數據單位儘可能各自完整地放入一個分組中被傳送

從表面上看來,H.264 關於參數集和片層的結構增加了編碼後數據的冗餘度(比如參數集必須多次重發,又如每個片都必須攜帶一部分相同的關於整個圖像的信息,而這些數據完全是重複 的),降低了編碼效率,但這些技術的採用使得通信的魯棒性大大增強,當數據傳輸中出現丟包,能夠將使錯誤限制在最小範圍,防止錯誤的擴散,解碼後對錯誤的 掩藏和恢復也能起到很好的作用。 一個片的丟失將不會影響其它片的解碼,還可以通過該片前後的片來恢復該片的數據。H.264 片層以下的句法元素的結構大體上和以往標準類似,但在相當多的細節上有所改進,所有的改進的目的不外乎兩個:在錯誤發生時防止錯誤擴散、減少冗餘信息提高 編碼效率。 這兩者往往是矛盾的,H.264 在這兩者上的取捨顯得頗具匠心。

圖7.3 所示的碼流的結構是一種簡化的模型,這個模型已經能夠正確工作,但還不夠完善,不適合複雜的場合。在複雜的通信環境中,除了片和參數集外還需要其他的數據 單位來提供額外的信息。圖7.4 描述了在複雜通信中的碼流中可能出現的數據單位。如前文所述,參數集可以被抽取出來使用其它信道。

(更正:看來一個片不適合作爲一個分組,可能太小了。應該是以這裏的數據單元爲一個分組的?)在圖7.4 中我們看到,一個序列的第一個圖像叫做IDR 圖像(立即刷新圖像),IDR 圖像都是I圖像。H.264 引入IDR 圖像是爲了解碼的重同步,當解碼器解碼到IDR 圖像時,立即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數集,開始一個新的序列。這樣,如果在前一個序列的傳輸中發生重大錯誤,如嚴重 的丟包,或其他原因引起數據錯位,在這裏可以獲得重新同步。IDR 圖像之後的圖像永遠不會引用IDR 圖像之前的圖像的數據來解碼。

要注意IDR 圖像和I 圖像的區別,IDR 圖像一定是I 圖像,但I 圖像不一定是IDR 圖像。一個序列中可以有很多的I 圖像,I 圖像之後的圖像可以引用I 圖像之間的圖像做運動參考。在圖7.4 中,除了參數集與片外還有其它的數據單位,這些數據單位可以提供額外的數據或同步信息,這些數據單位也是一系列句法元素的集合。它們在解碼過程中不是必需 的,但卻可以適當提高同步性能或定義圖像的複雜特徵。插入:關於I,P片的概念

 

宏塊、片

一個編碼圖像通常劃分成若干宏塊組成,一個宏塊由一個16×16 亮度像素和附加的一個8×8 Cb和一個8×8 Cr 彩色像素塊組成。每個圖象中,若干宏塊被排列成片的形式。
I 片只包含I 宏塊,P 片可包含P 和I 宏塊,而B 片可包含B 和I 宏塊。
I 宏塊利用從當前片中已解碼的像素作爲參考進行幀內預測(不能取其它片中的已解碼像素作爲參考進行幀內預測)
P 宏塊利用前面已編碼圖象作爲參考圖象進行幀內預測,一個幀內編碼的宏塊可進一步作宏塊的分割:即16×16、16×8、8×16 或8×8 亮度像素塊(以及附帶的彩色像素);如果選了8×8 的子宏塊,則可再分成各種子宏塊的分割,其尺寸爲8×8、8×4、4×8 或4×4 亮度像素塊(以及附帶的彩色像素)。
B 宏塊則利用雙向的參考圖象(當前和未來的已編碼圖象幀)進行幀內預測。
檔次和級
H.264 規定了三種檔次,每個檔次支持一組特定的編碼功能 ,並支持一類特定的應用。
1)基本檔次:利用I 片和P 片支持幀內和幀間編碼,支持利用基於上下文的自適應的變長編碼
進行的熵編碼(CAVLC)。主要用於可視電話、會議電視、無線通信等實時視頻通信;
2)主要檔次:支持隔行視頻,採用B 片的幀間編碼和採用加權預測的幀內編碼;支持利用基於
上下文的自適應的算術編碼(CABAC)。主要用於數字廣播電視與數字視頻存儲;
3)擴展檔次:支持碼流之間有效的切換(SP 和SI 片)、改進誤碼性能(數據分割),但不支持
隔行視頻和CABAC。


以下是解碼時的知識,現在先不用考慮。
句法的表示方法句法元素與變量編碼器將數據編碼爲句法元素然後依次發送。在解碼器端,通常要將句法元素作求值計算,得出一些中間數據,這些中間數據就是H.264 定義的變量。如圖7.5:

圖7.5 從句法元素解出變量圖中,

pic_width_in_mbs_minus1 是解碼器直接從碼流中提取的句法元素,這個句法元素表徵圖像的寬度,以宏塊爲單位。我們看到,爲了提高編碼效率,H.264 將圖像實際的寬度減去1 後再傳送。PicWidthInMbs = pic_width_in_mbs_minus1 + 1

PicWidthInSamplesL = PicWidthInMbs * 16

PicWidthInSamplesC = PicWidthInMbs * 8

以上變量PicWidthInMbs 表示圖像以宏塊爲單位的寬, 變量PicWidthInSamplesL 、PicWidthInSamplesC 分別表示圖像的亮度、色度分量以像素爲單位的寬。H.264 定義這些變量是因爲在後續句法元素的提取算法或圖像的重建中需要用到它們的值。在H.264 中,句法元素的名稱是由小寫字母和一系列的下劃線組成,而變量名稱是大小寫字母組成,中間沒有下劃線。(根據句法元素可以求解句法變量)

 

句法...

語 義 

NAL層語義

在網絡傳輸的環境下,編碼器將每個NAL 各自獨立、完整地放入一個分組,由於分組都有頭部,解碼器可以很方便地檢測出NAL 的分界,依次取出NAL 進行解碼。爲了節省碼流,H.264 沒有另外在NAL 的頭部設立表示起始的句法元素,我們從表7.1 可以看到這點。但是如果編碼數據是儲存在介質(如DVD 光盤)上,由於NAL 是依次緊密排列,解碼器將無法在數據流中分辨每個NAL的起始和終止,所以必須要有另外的機制來解決 這 個問題。

針對這個問題,H.264 草案的附錄B 中指明瞭一種簡單又高效的方案。當數據流是存儲在介質上時,在每個NAL 前添加起始碼:0x000001

在某些類型的介質上,爲了尋址的方便,要求數據流在長度上對齊,或必須是某個常數的倍數。考慮到這種情況,H.264 建議在起始碼前添加若干字節的0 來填充,直到該NAL 的長度符合要求。在這樣的機制下,解碼器在碼流中檢測起始碼,作爲一個NAL 的起始標識,當檢測到下一個起始碼時當前NAL 結束。H.264 規定當檢測到0x000000 時也可以表徵當前NAL 的結束,這是因爲連着的三個字節的0 中的任何一個字節的0 要麼屬於起始碼要麼是起始碼前面添加的0。添加起始碼是一個解決問題的很好的方法,但上面關於起始碼的介紹還不完整,因爲忽略了一個重要的問題:如果在 NAL 內部出現了0x000001 或是0x000000 的序列怎麼辦?毫無疑問這種情況是致命的,解碼器將把這些本來不是起始碼的字節序列當作起始碼,而錯誤地認爲這裏往後是一個新的NAL 的開始,進而造成解碼數據的錯位!而我們做的大量實驗證明,NAL 內部經常會出現這樣的字節序列。

於是H.264 提出了另外一種機制,叫做“防止競爭”,在編碼器編碼完一個NAL 時,應該檢測是否出現圖7.6 左側 中的四個字節序列,以防止它們和起始碼競爭。如果檢測到這些序列存在,編碼器將在最後一個字節前插入一個新的字節:0x03,從而使它們變成圖7.6 右測的樣子。當解碼器在NAL 內部檢測到有0x000003 的序列時,將把0x03 拋棄,恢復原始數據。

圖7.6 NAL 內部爲防止與起始碼競爭插入 0x03

圖7.6 中的前兩個序列我們前文中已經提到,第三個0x000002 是作保留用,而第四個0x000003是爲了保證解碼器能正常工作,因爲我們剛纔提到,解碼器恢復原始數據的方法是檢測到0x000003就拋棄其中的 0x03,這樣當出現原始數據爲0x000003 時會破壞數據,所以必須也應該給這個序列插入0x03。

我們可以從句法表7.1 中看到解碼器在NAL 層的處理步驟,其中變量NumBytesInNALunit 是解碼器計算出來的,解碼器在逐個字節地讀一個NAL 時並不同時對它解碼,而是要通過起始碼機制將整個NAL 讀進、計算出長度後再開始解碼。

 

接下來的部分就非常重要了,句法元素的語義。想要理解一個編碼器輸出的碼流,就需要熟悉這些。 forbidden_zero_bit 等於0

nal_ref_idc 指示當前NAL 的優先級。取值範圍爲0-3, ,值越高,表示當前NAL 越重要,需要優                先受到保護。H.264 規定如果當前NAL 是屬於參考幀的片,或是序列參數集,或是圖像        參數集這些重要的數據單位時,本句法元素必須大於0。但在大於0 時具體該取何值,卻沒        有進一步規定,通信雙方可以靈活地制定策略。

nal_unit_type 指明當前NAL unit 的類型,具體類型的定義如表7.20:

表7.20 nal_uint_type 語義

nal_unit_type NAL類型 C

0 未使用

1 不分區 、 非IDR 圖像的片 2, 3, 4

2 片分區A 2

3 片分區B 3

4 片分區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 未使用

nal_unit_type=5 時,表示當前NAL 是IDR 圖像的一個片,在這種情況下,IDR 圖像中的每個片的nal_unit_type 都應該等於5。

注意IDR 圖像不能使用片分區。

rbsp_byte RBSP 的第i 個字節。RBSP 指原始字節載荷,它是NAL 單元的數據部分的封裝格式,封裝的數據來自SODB(原始數據比特流)。SODB 是編碼後的原始數據,SODB 經封裝爲RBSP 後放入NAL 的數據部分。下面介紹一個RBSP 的生成順序。從SODB 到RBSP 的生成過程:- 如果SODB 內容是空的,生成的RBSP 也是空的- 否則,RBSP 由如下的方式生成:1) RBSP 的第一個字節直接取自SODB 的第1 到8 個比特,(RBSP 字節內的比特按照從左到右對應爲從高到低的順序排列,most significant),以此類推,RBSP 其餘的每個字節都直接取自SODB 的相應比特。RBSP 的最後一個字節包含SODB 的最後幾個比特,及如下的rbsp_trailing_bits()2) rbsp_trailing_bits()的第一個比特是1,接下來填充0,直到字節對齊。(填充0 的目的也是爲了字節對齊)3) 最後添加若干個cabac_zero_word(其值等於0x0000)emulation_prevention_three_byte NAL 內部爲防止與起始碼競爭而引入的填充字節 ,值爲0x03。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章