1. 程式人生 > >深入淺出理解視訊編碼H264結構

深入淺出理解視訊編碼H264結構

編碼流程:
那麼 H.264 其編解碼流程是怎麼樣的呢?其實可以主要分為 5 部分: 幀間和幀內預測(Estimation)、變換(Transform)和反變換、量化(Quantization)和反量化、環路濾波(Loop Filter)、熵編碼(Entropy Coding)。
看起來很高深的樣子,實際上也是很高深的樣子,因為這裡麵包含著許許多多的演算法和專業知識,這裡我們就不做過多的講解,有興趣的同學可以上網翻翻,夠你看到睡覺的了。H.264詳細文件

原理簡介

H.264 原始碼流(又稱為裸流),是有一個接一個的 NALU 組成的,而它的功能分為兩層:視訊編碼層(VCL, Video Coding Layer)和網路提取層(NAL, Network Abstraction Layer)。
VCL 資料即編碼處理的輸出,它表示被壓縮編碼後的視訊資料 序列。在 VCL 資料傳輸或儲存之前,這些編碼的 VCL 資料,先被對映或封裝進 NAL 單元(以下簡稱 NALU,Nal Unit) 中。每個 NALU 包括一個原始位元組序列負荷(RBSP, Raw Byte Sequence Payload)、一組 對應於視訊編碼的 NALU 頭部資訊。RBSP 的基本結構是:在原始編碼資料的後面填加了結尾 位元。一個 bit“1”若干位元“0”,以便位元組對齊。


NAL 單元排列

上圖中的 NALU頭 + RBSP 就相當與一個 NALU (Nal Unit), 每個單元都按獨立的 NALU 傳送。 其實說白了,H.264 中的結構全部都是以 NALU 為主的,理解了 NALU,就理解 H.264 的結構了。

一幀圖片跟 NALU 的關聯 :

究竟 NALU 是怎麼由一幀圖片變化而來的呀,H.264究竟為什麼這麼神奇?

一幀圖片經過 H.264 編碼器之後,就被編碼為一個或多個片(slice),而裝載著這些片(slice)的載體,就是 NALU 了,我們可以來看看 NALU 跟片的關係(slice)。


圖片編碼後
NALU 結構

小夥伴們要明白,片(slice)的概念不同與幀(frame),幀(frame)是用作描述一張圖片的,一幀(frame)對應一張圖片,而片(slice),是 H.264 中提出的新概念,是通過編碼圖片後切分通過高效的方式整合出來的概念,一張圖片至少有一個或多個片(slice)。

上圖中可以看出,片(slice)都是又 NALU 裝載並進行網路傳輸的,但是這並不代表 NALU 內就一定是切片,這是充分不必要條件,因為 NALU 還有可能裝載著其他用作描述視訊的資訊。

什麼是切片(slice)?

片的主要作用是用作巨集塊(Macroblock)的載體(ps:下面會介紹到巨集塊的概念)。片之所以被創造出來,主要目的是為限制誤碼的擴散和傳輸。
如何限制誤碼的擴散和傳輸?
每個片(slice)都應該是互相獨立被傳輸的,某片的預測(片(slice)內預測和片(slice)間預測)不能以其它片中的巨集塊(Macroblock)為參考影象。

那麼片(slice)的具體結構,我們用一張圖來直觀說明吧:


我們可以理解為一 張/幀 圖片可以包含一個或多個分片(Slice),而每一個分片(Slice)包含整數個巨集塊(Macroblock),即每片(slice)至少一個 巨集塊(Macroblock),最多時每片包 整個影象的巨集塊。

上圖結構中,我們不難看出,每個分片也包含著頭和資料兩部分:
1、分片頭中包含著分片型別、分片中的巨集塊型別、分片幀的數量、分片屬於那個影象以及對應的幀的設定和引數等資訊。
2、分片資料中則是巨集塊,這裡就是我們要找的儲存畫素資料的地方。

什麼是巨集塊?

巨集塊是視訊資訊的主要承載者,因為它包含著每一個畫素的亮度和色度資訊。視訊解碼最主要的工作則是提供高效的方式從碼流中獲得巨集塊中的畫素陣列。
組成部分:一個巨集塊由一個16×16亮度畫素和附加的一個8×8 Cb和一個 8×8 Cr 彩色畫素塊組成。每個圖象中,若干巨集塊被排列成片的形式。

我們先來看看巨集塊的結構圖:


從上圖中,可以看到,巨集塊中包含了巨集塊型別、預測型別、Coded Block Pattern、Quantization Parameter、畫素的亮度和色度資料集等等資訊。

切片(slice)型別跟巨集塊型別的關係

對於切片(slice)來講,分為以下幾種型別:

0 P-slice. Consists of P-macroblocks (each macro block is predicted using one reference frame) and / or I-macroblocks.
1 B-slice. Consists of B-macroblocks (each macroblock is predicted using one or two reference frames) and / or I-macroblocks.
2 I-slice. Contains only I-macroblocks. Each macroblock is predicted from previously coded blocks of the same slice.
3 SP-slice. Consists of P and / or I-macroblocks and lets you switch between encoded streams.
4 SI-slice. It consists of a special type of SI-macroblocks and lets you switch between encoded streams.

I片:只包 I巨集塊,I 巨集塊利用從當前片中已解碼的畫素作為參考進行幀內預測(不能取其它片中的已解碼畫素作為參考進行幀內預測)。

P片:可包 P和I巨集塊,P 巨集塊利用前面已編碼圖象作為參考圖象進行幀內預測,一個幀內編碼的巨集塊可進一步作巨集塊的分割:即 16×16、16×8、8×16 或 8×8 亮度畫素塊(以及附帶的彩色畫素);如果選了 8×8 的子巨集塊,則可再分成各種子巨集塊的分割,其尺寸為 8×8、8×4、4×8 或 4×4 亮度畫素塊(以及附帶的彩色畫素)。

B片:可包 B和I巨集塊,B 巨集塊則利用雙向的參考圖象(當前和 來的已編碼圖象幀)進行幀內預測。

SP片(切換P):用於不同編碼流之間的切換,包含 P 和/或 I 巨集塊

SI片:擴充套件檔次中必須具有的切換,它包 了一種特殊型別的編碼巨集塊,叫做 SI 巨集塊,SI 也是擴充套件檔次中的必備功能。

整體結構
通過剖析了這麼多個小零件,是時候個大家一個世界地圖了,
那麼我們的 NALU 整體結構可以呼之欲出了,以下就引用 H.264 文件當中的一幅圖了


其實 H.264 的碼流結構並沒有大家想的那麼複雜,編碼後視訊的每一組影象(GOP,影象組)都給予了傳輸中的序列(PPS)和本身這個幀的影象引數(SPS),所以,我們的整體結構,應該如此:


GOP (影象組)主要用作形容一個 i 幀 到下一個 i 幀之間的間隔了多少個幀,增大圖片組能有效的減少編碼後的視訊體積,但是也會降低視訊質量,至於怎麼取捨,得看需求了。

主題外:(未完待續)

那麼,NALU 頭部中的型別確定著什麼資訊呢?
我們首先來看看 NALU 中究竟有哪幾種類型,我們來看看 H.264 中原始碼對 nal_unit_type_e 中的定義:

enum nal_unit_type_e
{
NAL_UNKNOWN = 0, // 未使用
NAL_SLICE = 1, // 不分割槽、非 IDR 影象的片(片的頭資訊和資料)
NAL_SLICE_DPA = 2, // 片分割槽 A
NAL_SLICE_DPB = 3, // 片分割槽 B
NAL_SLICE_DPC = 4, // 片分割槽 C
NAL_SLICE_IDR = 5, / ref_idc != 0 / // IDR 影象中的片
NAL_SEI = 6, / ref_idc == 0 / // 補充增強資訊單元
-
引數集是 H.264 標準的一個新概念,是一種通過改進視訊碼流結構增強錯誤恢復能力的方法。
NAL_SPS = 7, // 序列引數集 (包括一個影象序列的所有資訊,即兩個 IDR 影象間的所有影象資訊,如影象尺寸、視訊格式等)
NAL_PPS = 8, // 影象引數集 (包括一個影象的所有分片的所有相關資訊, 包括影象型別、序列號等,解碼時某些序列號的丟失可用來檢驗資訊包的丟失與否)
-
NAL_AUD = 9, // 分界符
NAL_FILLER = 12, // 填充(啞元資料,用於填充位元組)
/ ref_idc == 0 for 6,9, 10 (表明下一影象為 IDR 影象),11(表明該流中已沒有影象),12 /
};
ps: 以上括號()中的為型別描述

上面NALU型別當中,分片/切片(slice)的概念我們都已經很清楚了,但是用 NALU 作載體的還有 SEI、SPS、PPS 等等。

今天我們不一一聚述這些型別對整個流程的作用了,我們挑出兩個符合我們今天主題的型別來講,PPS 和 SPS。



那麼今天我們講的 H.264 的碼流結構相信大家都有個大概輪廓的瞭解了,總結的一句話就是:

H.264 中,句法元素共被組織成 序列、影象、片、巨集塊、子巨集塊五個層次。