最近入門(mén)音視頻技術(shù),一直在學(xué)習H264編解碼標準,了解了不少關(guān)于H264的相關(guān)知識,對于網(wǎng)上各種類(lèi)型的資料,始終沒(méi)有找到一篇適合的知識梳理資料??赡苁遣檎曳绞讲粚?,所以花費了比較多的時(shí)間。經(jīng)過(guò)一段時(shí)間的熟悉后結合網(wǎng)上各類(lèi)大神的指導資料和自己的理解決定自己整理一下關(guān)于H264編解碼標準的知識。以后方便自己查閱,也讓更多剛入門(mén)的人提供一個(gè)參考資料,由于是初次涉及,所以有些地方可能有理解不全面或有誤,望各位前輩大神多多指導,不勝感激;
主要的參考資料在已貼在后面,大家可以詳細了解;
0、序言
首先要弄明白編碼的目的,有目的的學(xué)習效率會(huì )更好。編碼是為了將數據進(jìn)行壓縮,這樣在傳輸的過(guò)程中就不會(huì )使資源被浪費,用一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明編碼的必要性:
當你此刻顯示器正在播放一個(gè)視頻,分辨率是1280*720,幀率是25,那么一秒所產(chǎn)生正常的數據大小為:
1280*720(位像素)*25(張) / 8(1字節8位)(結果:B) / 1024(結果:KB) / 1024 (結果:MB) = 2.75MB
顯然一秒這么大的數據你是無(wú)法接受的,所以如果不將數據進(jìn)行壓縮,那么只能一首涼涼表達此刻的感受了;
1、前言
H264里面很多文章梳理,具體標準定義就不多贅述了,但是有些概念一定要弄明白,這樣在后面我就是因為有些概念模糊所以在理解某些知識點(diǎn)十分吃力,例如序列、GOP等等,我會(huì )逐一在后面描述到。
H264在視頻采集到輸出中屬于編解碼層次的數據,如圖1所示,是在采集數據后做編碼壓縮時(shí)通過(guò)編碼標準編碼后所呈現的數據。
圖1
2、H264相關(guān)概念
2.1序列
H264編碼標準中所遵循的理論依據個(gè)人理解成:參照一段時(shí)間內相鄰的圖像中,像素、亮度與色溫的差別很小。所以當面對一段時(shí)間內圖像我們沒(méi)必要去對每一幅圖像進(jìn)行完整一幀的編碼,而是可以選取這段時(shí)間的第一幀圖像作為完整編碼,而下一幅圖像可以記錄與第一幀完整編碼圖像像素、亮度與色溫等的差別即可,以此類(lèi)推循環(huán)下去。
什么叫序列呢?上述的這段時(shí)間內圖像變化不大的圖像集我們就可以稱(chēng)之為一個(gè)序列。序列可以理解為有相同特點(diǎn)的一段數據。但是如果某個(gè)圖像與之前的圖像變換很大,很難參考之前的幀來(lái)生成新的幀,那么久結束刪一個(gè)序列,開(kāi)始下一段序列。重復上一序列的做法,生成新的一段序列。
2.2、幀類(lèi)型
H264結構中,一個(gè)視頻圖像編碼后的數據叫做一幀,一幀由一個(gè)片(slice)或多個(gè)片組成,一個(gè)片由一個(gè)或多個(gè)宏塊(MB)組成,一個(gè)宏塊由16x16的yuv數據組成。宏塊作為H264編碼的基本單位。
在H264協(xié)議內定義了三種幀,分別是I幀、B幀與P幀。I幀就是之前所說(shuō)的一個(gè)完整的圖像幀,而B(niǎo)、幀與P幀所對應的就是之前說(shuō)的不編碼全部圖像的幀。P幀與B幀的差別就是P幀是參考之前的I幀而生成的,而B(niǎo)幀是參考前后圖像幀編碼生成的。
2.3、GOP(畫(huà)面組)
GOP我個(gè)人也理解為跟序列差不多意思,就是一段時(shí)間內變化不大的圖像集。GOP結構一般有兩個(gè)數字,如M=3,N=12。M指定I幀和P幀之間的距離,N指定兩個(gè)I幀之間的距離。上面的M=3,N=12,GOP結構為:IBBPBBPBBPBBI。在一個(gè)GOP內I frame解碼不依賴(lài)任何的其它幀,p frame解碼則依賴(lài)前面的I frame或P frame,B frame解碼依賴(lài)前最近的一個(gè)I frame或P frame 及其后最近的一個(gè)P frame。
2.4、IDR幀(關(guān)鍵幀)
在編碼解碼中為了方便,將GOP中首個(gè)I幀要和其他I幀區別開(kāi),把第一個(gè)I幀叫IDR,這樣方便控制編碼和解碼流程,所以IDR幀一定是I幀,但I幀不一定是IDR幀;IDR幀的作用是立刻刷新,使錯誤不致傳播,從IDR幀開(kāi)始算新的序列開(kāi)始編碼。I幀有被跨幀參考的可能,IDR不會(huì )。
I幀不用參考任何幀,但是之后的P幀和B幀是有可能參考這個(gè)I幀之前的幀的。IDR就不允許這樣,例如:
IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15 這里的B8可以跨過(guò)I10去參考P7
------------------------------------------------------------------------
IDR1 P4 B2 B3 P7 B5 B6 IDR8 P11 B9 B10 P14 B11 B12 這里的B9就只能參照IDR8和P11,不可以參考IDR8前面的幀
作用:
H.264引入 IDR 圖像是為了解碼的重同步,當解碼器解碼到 IDR圖像時(shí),立即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數集,開(kāi)始一個(gè)新的序列。這樣,如果前一個(gè)序列出現重大錯誤,在這里可以獲得重新同步的機會(huì )。IDR圖像之后的圖像永遠不會(huì )使用IDR之前的圖像的數據來(lái)解碼。
3、H264壓縮方式
H264采用的核心算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。
幀內(Intraframe)壓縮也稱(chēng)為空間壓縮(Spatialcompression)。當壓縮一幀圖像時(shí),僅考慮本幀的數據而不考慮相鄰幀之間的冗余信息,這實(shí)際上與靜態(tài)圖像壓縮類(lèi)似。幀內一般采用有損壓縮算法,由于幀內壓縮是編碼一個(gè)完整的圖像,所以可以獨立的解碼、顯示。幀內壓縮一般達不到很高的壓縮,跟編碼jpeg差不多。
幀間(Interframe)壓縮的原理是:相鄰幾幀的數據有很大的相關(guān)性,或者說(shuō)前后兩幀信息變化很小的特點(diǎn)。也即連續的視頻其相鄰幀之間具有冗余信息,根據這一特性,壓縮相鄰幀之間的冗余量就可以進(jìn)一步提高壓縮量,減小壓縮比。幀間壓縮也稱(chēng)為時(shí)間壓縮(Temporalcompression),它通過(guò)比較時(shí)間軸上不同幀之間的數據進(jìn)行壓縮。幀間壓縮一般是無(wú)損的。幀差值(Framedifferencing)算法是一種典型的時(shí)間壓縮法,它通過(guò)比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少數據量。
3.1、壓縮方式說(shuō)明
Step1:分組,也就是將一系列變換不大的圖像歸為一個(gè)組,也就是一個(gè)序列,也可以叫GOP(畫(huà)面組);
Step2:定義幀,將每組的圖像幀歸分為I幀、P幀和B幀三種類(lèi)型;
Step3:預測幀, 以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀;
Step4:數據傳輸, 最后將I幀數據與預測的差值信息進(jìn)行存儲和傳輸。
4、H264分層結構
H264的主要目標是為了有高的視頻壓縮比和良好的網(wǎng)絡(luò )親和性,為了達成這兩個(gè)目標,H264的解決方案是將系統框架分為兩個(gè)層面,分別是視頻編碼層面(VCL)和網(wǎng)絡(luò )抽象層面(NAL),如圖2;
圖2
VLC層是對核心算法引擎、塊、宏塊及片的語(yǔ)法級別的定義,負責有效表示視頻數據的內容,最終輸出編碼完的數據SODB;
NAL層定義了片級以上的語(yǔ)法級別(如序列參數集參數集和圖像參數集,針對網(wǎng)絡(luò )傳輸,后面會(huì )描述到),負責以網(wǎng)絡(luò )所要求的恰當方式去格式化數據并提供頭信息,以保證數據適合各種信道和存儲介質(zhì)上的傳輸。NAL層將SODB打包成RBSP然后加上NAL頭組成一個(gè)NALU單元,具體NAL單元的組成也會(huì )在后面詳細描述。
這里說(shuō)一下SODB與RBSP的關(guān)聯(lián),具體結構如圖3所示:
SODB: 數據比特串,是編碼后的原始數據;
RBSP: 原始字節序列載荷,是在原始編碼數據后面添加了結尾比特,一個(gè)bit“1”和若干個(gè)比特“0”,用于字節對齊。
圖3
5.H264碼流結構
我認為在具體講述NAL單元前,十分有必要先了解一下H264的碼流結構;在經(jīng)過(guò)編碼后的H264的碼流如圖4所示,從圖中我們需要得到一個(gè)概念,H264碼流是由一個(gè)個(gè)的NAL單元組成,其中SPS、PPS、IDR和SLICE是NAL單元某一類(lèi)型的數據。
圖4
6、H264的NAL單元
6.1、H264的NAL結構
在實(shí)際的網(wǎng)絡(luò )數據傳輸過(guò)程中H264的數據結構是以NALU(NAL單元)進(jìn)行傳輸的,傳輸數據結構組成為[NALU Header]+[RBSP],如圖5所示:
圖5
從之前的分析我們可以知道,VCL層編碼后的視頻幀數據,幀有可能是I/B/P幀,這些幀也可能是屬于不同的序列之中;同一序列也還有相應的序列參數集與圖片參數集;綜上所述,想要完成準確無(wú)誤視頻的解碼,除了需要VCL層編碼出來(lái)的視頻幀數據,同時(shí)還需要傳輸序列參數集和圖像參數集等等,所以RBSP不單純只保存I/B/P幀的數據編碼信息,還有其他信息也可能出現在里面。
上面知道NAL單元是作為實(shí)際視頻數據傳輸的基本單元,NALU頭是用來(lái)標識后面RBSP是什么類(lèi)型的數據,同時(shí)記錄RBSP數據是否會(huì )被其他幀參考以及網(wǎng)絡(luò )傳輸是否有錯誤,所以針對NAL頭和RBSP的作用以及結構與所承載的數據需要做個(gè)簡(jiǎn)單的了解;
6.2、NAL頭
一、 NAL頭的組成
NAL單元的頭部是由forbidden_bit(1bit),nal_reference_bit(2bits)(優(yōu)先級),nal_unit_type(5bits)(類(lèi)型)三個(gè)部分組成的,組成如圖6所示:
1、F(forbiden):禁止位,占用NAL頭的第一個(gè)位,當禁止位值為1時(shí)表示語(yǔ)法錯誤;
2、NRI:參考級別,占用NAL頭的第二到第三個(gè)位;值越大,該NAL越重要。
3、Type:Nal單元數據類(lèi)型,也就是標識該NAL單元的數據類(lèi)型是哪種,占用NAL頭的第四到第8個(gè)位;
圖6
二、 NAL單元數據類(lèi)型
NAL類(lèi)型主要就是下面圖7中這些類(lèi)型每個(gè)類(lèi)型都有特殊的作用;
圖 7
圖 8
在具體介紹NAL數據類(lèi)型前,有必要知道NAL分為VCL和非VCL的NAL單元。在圖8中有介紹(圖表中DIR應該為IDR),其中SPS、SEI、PPS等非VCL的NAL參數對解碼和顯示視頻都是很有用的。
而另外一個(gè)需要了解的概念就是參數集(Parameter sets),參數集是攜帶解碼參數的NAL單元,參數集對于正確解碼是非常重要的,在一個(gè)有損耗的傳輸場(chǎng)景中,傳輸過(guò)程中比特列或包可能丟失或損壞,在這種網(wǎng)絡(luò )環(huán)境下,參數集可以通過(guò)高質(zhì)量的服務(wù)來(lái)發(fā)送,比如向前糾錯機制或優(yōu)先級機制。Parameter sets與其之外的句法元素之間的關(guān)系如圖9所示:
圖 9
每種類(lèi)型都有代表一種數據類(lèi)型,比較重要的以下幾種做個(gè)簡(jiǎn)單的介紹:
1、非VCL的NAL數據類(lèi)型:
1)、SPS(序列參數集):SPS對如標識符、幀數以及參考幀數目、解碼圖像尺寸和幀場(chǎng)模式等解碼參數進(jìn)行標識記錄。
2)、PPS(圖像參數集):PPS對如熵編碼類(lèi)型、有效參考圖像的數目和初始化等解碼參數進(jìn)行標志記錄。
3)、SEI(補充增強信息):這部分參數可作為H264的比特流數據而被傳輸,每一個(gè)SEI信息被封裝成一個(gè)NAL單元。SEI對于解碼器來(lái)說(shuō)可能是有用的,但是對于基本的解碼過(guò)程來(lái)說(shuō),并不是必須的。
@:先標記一下,SPS、PPS內容是編碼器給的。(出處的話(huà),慢慢研究)
2、VCL的NAL數據類(lèi)型
1)、 頭信息塊,包括宏塊類(lèi)型,量化參數,運動(dòng)矢量。這些信息是最重要的,因為離開(kāi)他們,被的數據塊種的碼元都無(wú)法使用。該數據分塊稱(chēng)為A類(lèi)數據分塊。
2)、 幀內編碼信息數據塊,稱(chēng)為B類(lèi)數據分塊。它包含幀內編碼宏塊類(lèi)型,幀內編碼系數。對應的slice來(lái)說(shuō),B類(lèi)數據分塊的可用性依賴(lài)于A(yíng)類(lèi)數據分塊。和幀間編碼信息數據塊不通的是,幀內編碼信息能防止進(jìn)一步的偏差,因此比幀間編碼信息更重要。
3)、 幀間編碼信息數據塊,稱(chēng)為C類(lèi)數據分塊。它包含幀間編碼宏塊類(lèi)型,幀間編碼系數。它通常是slice種最大的一部分。幀間編碼信息數據塊是不重要的一部分。它所包含的信息并不提供編解碼器之間的同步。C類(lèi)數據分塊的可用性也依賴(lài)于A(yíng)類(lèi)數據分塊,但于B類(lèi)數據分塊無(wú)關(guān)。
以上三種數據塊每種分割被單獨的存放在一個(gè)NAL單元中,因此可以被單獨傳輸。
6.3、H264的NAL單元與片,宏之間的聯(lián)系
其實(shí)到這里可能就比較難理解了,為什么數據NAL單元中有這么多數據類(lèi)型,這個(gè)SLICE又是什么東西,為什么不直接是編碼后出來(lái)的原始字節序列載荷,所以我覺(jué)得在這里再講述幀所細分的一些片和宏的概念應該是比較合適的,也是能夠參照上下文更能理解這些概念的位置,又能給這些困惑做一個(gè)合理一點(diǎn)的解釋?zhuān)栽诖俗鲆粋€(gè)描述:
1幀(一幅圖像) = 1~N個(gè)片(slice) //也可以說(shuō)1到多個(gè)片為一個(gè)片組
1個(gè)片 = 1~N個(gè)宏塊(Marcroblock)
1個(gè)宏塊 = 16X16的YUV數據(原始視頻采集數據)
從數據層次角度來(lái)說(shuō),一幅原始的圖片可以算作廣義上的一幀,幀包含片組和片,片組由片來(lái)組成,片由宏塊來(lái)組成,每個(gè)宏塊可以是4*4、8*8、16*16像素規模的大小,它們之間的聯(lián)系如圖10所示。每個(gè)片都是一個(gè)獨立的編碼單位。
圖10
從容納數據角度來(lái)說(shuō),NAL單元除了容納Slice編碼的碼流外,還可以容納其他數據,這也就是為什么有SPS、PPS等這些數據出現的原因,并且這些數據在傳輸H264碼流的過(guò)程中起到不可或缺的作用,具體作用上面也是有講到的。
那么也就可以對下面這些概念做一個(gè)大小的排序了:
序列>圖像>片>宏>像素(當然還有片組、亞宏塊等等這些概念,初步了解就不了解這么深了,后面再慢慢研究)
同時(shí)有幾點(diǎn)需要說(shuō)明一下,這樣能便于理解NAL單元:
(1)、如果不采用 FMO(靈活宏塊排序) 機制,則一幅圖像只有一個(gè)片組;
(2)、如果不使用多個(gè)片,則一個(gè)片組只有一個(gè)片;
(3)、如果不采用 DP(數據分割)機制,則一個(gè)片就是一個(gè) NALU,一個(gè) NALU 也就是一個(gè)片。
否則,一個(gè)片的組成需要由 三個(gè) NALU 組成,也就是上面說(shuō)到的A、B、C類(lèi)數據塊。
這時(shí)候在看下面這幅碼流數據分層圖11就比較能理解整體的碼流結構組成了;
圖11
如我們所見(jiàn),每個(gè)分片也包含著(zhù)頭和數據兩部分,分片頭中包含著(zhù)分片類(lèi)型、分片中的宏塊類(lèi)型、分片幀的數量以及對應的幀的設置和參數等信息,而分片數據中則是宏塊,這里就是我們要找的存儲像素數據的地方;宏塊是視頻信息的主要承載者,因為它包含著(zhù)每一個(gè)像素的亮度和色度信息。視頻解碼最主要的工作則是提供高效的方式從碼流中獲得宏塊中的像素陣列。宏塊數據的組成如下圖12所示:
圖12
從上圖中,可以看到,宏塊中包含了宏塊類(lèi)型、預測類(lèi)型、Coded Block Pattern、Quantization Parameter、像素的亮度和色度數據集等等信息。
至此,我們對 H.264 的碼流數據結構應該有了一個(gè)大致的了解。
需要注意的幾點(diǎn):
H.264/AVC標準對送到解碼器的NAL單元順序是有嚴格要求的,如果NAL單元的順序是混亂的,必須將其重新依照規范組織后送入解碼器,否則解碼器不能夠正確解碼。
1.序列參數集NAL單元 必須在傳送所有以此參數集為參考的其他NAL單元之前傳送,不過(guò)允許這些NAL單元中間出現重復的序列參數集NAL單元。所謂重復的詳細解釋為:序列參數集NAL單元都有其專(zhuān)門(mén)的標識,如果兩個(gè)序列參數集NAL單元的標識相同,就可以認為后一個(gè)只不過(guò)是前一個(gè)的拷貝,而非新的序列參數集。
2.圖像參數集NAL單元 必須在所有以此參數集為參考的其他NAL單元之前傳送,不過(guò)允許這些NAL單元中間出現重復的圖像參數集NAL單元,這一點(diǎn)與上述的序列參數集NAL單元是相同
總結:
暫時(shí)就整理這么多,可能有些描述不準確、不合理或有誤的地方,希望各位前輩多多指正,我會(huì )盡量完善,至于有些不夠詳盡的地方會(huì )在后面找時(shí)間填補上去;本篇只是結合我個(gè)人的理解習慣做的歸納整理,下面這些學(xué)習資料也在一些概念上更加詳盡,想要更加深入的了解以上某些具體概念的,不妨點(diǎn)進(jìn)去學(xué)習;
參考資料: