前言:
為什么需要編碼呢?比如當(dāng)前屏幕是1280*720.一秒24張圖片.那么我們一秒的視頻數(shù)據(jù)是
1280*720(位像素)*24(張) / 8(1字節(jié)8位)(結(jié)果:B) / 1024(結(jié)果:KB) / 1024 (結(jié)果:MB) = 2.64MB
一秒的數(shù)據(jù)有2.64MB數(shù)據(jù)量。1分鐘就會(huì)有100多MB祸穷。這對(duì)用戶來(lái)說(shuō)真心是災(zāi)難望蜡。所以現(xiàn)在我們需要一種壓縮方式減小數(shù)據(jù)的大小.在更低 比特率(bps)的情況下依然提供清晰的視頻尸疆。
H264: H264/AVC是廣泛采用的一種編碼方式蟀拷。我們這邊會(huì)帶大家了解制市。從大到小排序依次是 序列,圖像暖庄,NALU聊替,片,宏塊培廓,亞宏塊惹悄,塊,像素肩钠。
問(wèn)題背景:
前面在講封裝格式過(guò)程中泣港,都有一個(gè)章節(jié)講解如何將H.264的NALU單元如何打包到TS、FLV蔬将、RTP中爷速,解裝剛好相反,怎么從這些封裝格式里面解析出一個(gè)個(gè)NALU單元霞怀。NALU即是編碼器的輸出數(shù)據(jù)又是解碼器的輸入數(shù)據(jù)惫东,所以在封裝和傳輸時(shí),我們一般處理對(duì)象就是NALU毙石,至于NALU內(nèi)部到底是什么則很少關(guān)心廉沮。甚至我們?cè)诰幗獯a時(shí),我們只需要初始化好x264編碼庫(kù)徐矩,然后輸入YUV數(shù)據(jù)滞时,它就會(huì)給你經(jīng)過(guò)一系列壓縮算法后輸出NALU,或者將NALU輸入到x264解碼庫(kù)就會(huì)輸出YUV數(shù)據(jù)滤灯。
這篇文章就初步帶你看下NALU能傳輸那些數(shù)據(jù)坪稽,NALU的類(lèi)型和結(jié)構(gòu)以及H264碼流的層次,最后通過(guò)分析工具分析下裸碼流記性驗(yàn)證鳞骤,你可以選擇感興趣章節(jié)閱讀窒百。
NALU結(jié)構(gòu):
H.264的基本流(elementary stream),也叫裸流(沒(méi)有加格式封裝)豫尽,就是一系列NALU的集合篙梢,如下圖所示:
用Notepad十六進(jìn)制形式打開(kāi),以annexb格式存儲(chǔ)的h264裸流文件內(nèi)容:
字節(jié)流格式(Annex B)和RTP格式流淺析:
AnnexB(附錄B)格式:
NALU數(shù)據(jù)+開(kāi)始前綴(00 00 00 01或00 00 01)美旧,針對(duì)H.320電話會(huì)議渤滞。
RTP格式:
NALU數(shù)據(jù)+20個(gè)字節(jié)的類(lèi)似的并不符合RTP協(xié)議的RTP頭。針對(duì)IP網(wǎng)絡(luò)的RTP打包方式榴嗅。為原始的NAL打包格式妄呕,就是開(kāi)始的若干字節(jié)(1,2嗽测,4字節(jié))是NAL的長(zhǎng)度趴腋,而不是start_code,此時(shí)必須借助某個(gè)全局的數(shù)據(jù)來(lái)獲得編碼器的profile,level,PPS,SPS等信息才可以解碼。
tips:
H.264協(xié)議只規(guī)定了字節(jié)流格式,沒(méi)有規(guī)定 RTP 格式优炬“渚可能也是因?yàn)檫@個(gè)原因,RTP 格式?jīng)]有被用到任何場(chǎng)合場(chǎng)合中蠢护,成為了擺設(shè)雅宾。
NALU結(jié)構(gòu)分為兩層,包含了視頻編碼層(VCL)和網(wǎng)絡(luò)適配層(NAL):
視頻編碼層(VCL即Video Coding Layer):負(fù)責(zé)高效的視頻內(nèi)容表示,這是核心算法引擎葵硕,其中對(duì)宏塊眉抬、片的處理都包含在這個(gè)層級(jí)上,它輸出的數(shù)據(jù)是SODB;
網(wǎng)絡(luò)適配層(NAL即Network Abstraction Layer):以網(wǎng)絡(luò)所要求的恰當(dāng)方式對(duì)數(shù)據(jù)進(jìn)行打包和發(fā)送懈凹,比較簡(jiǎn)單蜀变,先報(bào)VCL吐出來(lái)的數(shù)據(jù)SODB進(jìn)行字節(jié)對(duì)齊,形成RBSP介评,最后再RBSP數(shù)據(jù)前面加上NAL頭則組成一個(gè)NALU單元库北。
分層目的:
這樣做的目的:VCL只負(fù)責(zé)視頻的信號(hào)處理,包含壓縮们陆,量化等處理寒瓦,NAL解決編碼后數(shù)據(jù)的網(wǎng)絡(luò)傳輸,這樣可以將VCL和NAL的處理放到不同平臺(tái)來(lái)處理坪仇,可以減少因?yàn)榫W(wǎng)絡(luò)環(huán)境不同對(duì)VCL的比特流進(jìn)行重構(gòu)和重編碼杂腰;
NLAU結(jié)構(gòu):
其實(shí)NALU的承載數(shù)據(jù)真實(shí)并不是RBSP(Raw Byte Sequence Playload)而是EBSP即(Extent Byte Sequence Payload),EBSP和RBSP的區(qū)別就是在 RBSP里面加入防偽起始碼字節(jié)(0x03)椅文,因?yàn)镠.264規(guī)范規(guī)定喂很,編碼器吐出來(lái)的數(shù)據(jù)需要在每個(gè)NALU添加起始碼:0x00 00 01或者0x00 00 00 01,用來(lái)指示一個(gè)NALU的起始和終止位置,那么RBSP數(shù)據(jù)內(nèi)部是有可能含有這種字節(jié)序列的皆刺,為了防止解析錯(cuò)誤少辣,所以在RBSP數(shù)據(jù)流里面碰到0x 00 00 00 01的0x01前面就會(huì)加上0x03,解碼時(shí)將NALU的EBSP中的0x03去掉成為RBSP芹橡,稱為脫殼操作。
原始字節(jié)序列負(fù)載RBSP即Raw Byte Sequence Playload望伦,因?yàn)閂CL輸出的原始數(shù)據(jù)比特流SODB即String Of Data Bits林说,其長(zhǎng)度不一定是8bit的整數(shù)倍,為了湊成整數(shù)個(gè)字節(jié)屯伞,往往需要對(duì)SODB最后一個(gè)字節(jié)進(jìn)行填充形成RBSP腿箩,所以從SODB到RBSP的示意圖如下:
填充方式就是對(duì)VCL的輸出數(shù)據(jù)進(jìn)行8bit進(jìn)行切分,最后一個(gè)不滿8bit的字節(jié)第一bit位置1劣摇,然后后面缺省的bit置0即可
具體填充語(yǔ)法見(jiàn)下文:
參考ISO IEC 14496-10文檔
7.3.2.11 RBSP trailing bits syntax
rbsp_trailing_bits( ) {
rbsp_stop_one_bit /* equal to 1 /
while( !byte_aligned( ) )
rbsp_alignment_zero_bit / equal to 0 */
}
原來(lái)文檔中的解釋:
An RBSP is specified as an ordered sequence of bytes as follows.The RBSP contains an SODB as follows:– If the SODB is empty (i.e., zero bits in length), the RBSP is also empty.– Otherwise, the RBSP contains the SODB as follows:1) The first byte of the RBSP contains the (most significant, left-most) eight bits of the SODB; the next byte ofthe RBSP contains the next eight bits of the SODB, etc., until fewer than eight bits of the SODB remain.2) rbsp_trailing_bits( ) are present after the SODB as follows:i) The first (most significant, left-most) bits of the final RBSP byte contains the remaining bits of the SODB(if any).ii) The next bit consists of a single rbsp_stop_one_bit equal to 1.
iii)
When the rbsp_stop_one_bit is not the last bit of a byte-aligned byte, one or morerbsp_alignment_zero_bit is present to result in byte alignment.3) One or more cabac_zero_word 16-bit syntax elements equal to 0x0000 may be present in some RBSPs after therbsp_trailing_bits( ) at the end of the RBSP.
主要的意思我的理解如下:
- 如果SODB恰好占到8的倍數(shù)個(gè)bits珠移,這時(shí)候還是需要RBSP
trailing bits,即二進(jìn)制的‘10000000’ - 如果SODB按8位字節(jié)占據(jù),多余7個(gè)bits钧惧,這時(shí)候的RBSP
trailing bits暇韧,即二進(jìn)制的‘1’,后面不用再添0了 - 普通的情況(多余1-6bits)是后面補(bǔ)齊二進(jìn)制1和(1-6個(gè))0
還有一種情況是浓瞪,有的RBSP最后還會(huì)出現(xiàn)1個(gè)或多個(gè)cabac_zero_word懈玻,即16位的0
其中H.264規(guī)范規(guī)定,編碼器吐出來(lái)的數(shù)據(jù)需要在每個(gè)NALU添加起始碼:0x00 00 01或者0x00 00 00 01,用來(lái)指示一個(gè)NALU的起始和終止位置乾颁。
所以H.264編碼器輸出的碼流中每個(gè)幀開(kāi)頭3-4字節(jié)的start code起始碼為0x00 00 01或者0x00 00 00 01涂乌。
上面我們分析了NALU的結(jié)構(gòu)以及每層輸出數(shù)據(jù)的處理方法,但是對(duì)于NALU的RBSP數(shù)據(jù)二進(jìn)制表示的什么含義并不清楚英岭,下面分析下NALU的類(lèi)型湾盒。
1. NALU Header
名詞解釋
IDR(Instantaneous Decoding Refresh): 即時(shí)解碼刷新。
I和IDR幀都是使用幀內(nèi)預(yù)測(cè)的诅妹。它們都是同一個(gè)東西, 在編碼和解碼中為了方便罚勾,要把首個(gè)I幀和其他I幀區(qū)別開(kāi),所以首個(gè)I幀叫IDR漾唉,以方便控制編碼和解碼流程荧库。IDR幀的作用是立刻刷新, 使錯(cuò)誤不致傳播。從IDR幀開(kāi)始, 重新算一個(gè)新的序列開(kāi)始編碼赵刑。而I幀不具有隨機(jī)訪問(wèn)的能力分衫,這個(gè)功能是由IDR承擔(dān)。
頭信息協(xié)議如上圖般此。
舉例說(shuō)明:
1. 00 00 00 01 06: SEI信息
2. 00 00 00 01 67: 0x67&0x1f = 0x07 :SPS蚪战,Nalu_Type占Nalu header后5bit,所以&0x1f
3. 00 00 00 01 68: 0x68&0x1f = 0x08 :PPS
4. 00 00 00 01 65: 0x65&0x1f = 0x05 :IDR Slice
5. 00 00 00 01 41: 0x41&0x1f = 0x01 :非IDR Slice(P Slice or B Slice)
這其中NALU的RBSP除了能承載真實(shí)的視頻壓縮數(shù)據(jù)铐懊,還能傳輸編碼器的配置信息邀桑,其中能傳輸視頻壓縮數(shù)據(jù)的為slice。
那么如果NLAU傳輸視頻壓縮數(shù)據(jù)時(shí)科乎,編碼器沒(méi)有開(kāi)啟DP(數(shù)據(jù)分割)機(jī)制壁畸,則一個(gè)片就是一個(gè)NALU,一個(gè) NALU 也就是一個(gè)片茅茂。否則捏萍,一個(gè)片由三個(gè) NALU 組成,即DPA空闲、DPB和DPC令杈,對(duì)應(yīng)的nal_unit_type 類(lèi)型為 2、3和4碴倾。
通常情況我們看到的NLAU類(lèi)型就是SPS逗噩、PPS掉丽、SEI、IDR的slice异雁、非IDR這幾種捶障。
上面站在NALU的角度看了NALU的類(lèi)型、結(jié)構(gòu)片迅、數(shù)據(jù)來(lái)源残邀、分層處理的原因等,其中NLAU最主要的目的就是傳輸視頻數(shù)據(jù)壓縮結(jié)果柑蛇。那么站在對(duì)數(shù)據(jù)本身的理解上芥挣,我們看下H.264碼流的層次結(jié)構(gòu)。
H.264層次結(jié)構(gòu):
其實(shí)為了理解H.264是如何看待視頻數(shù)據(jù)耻台,先要了解下視頻的形成過(guò)程空免。其實(shí)你把多副連續(xù)的有關(guān)聯(lián)圖像連續(xù)播就可以形成視頻,這主要利用了人視覺(jué)系統(tǒng)的暫留效應(yīng)盆耽,當(dāng)把連續(xù)的圖片以每秒25張的速度播放蹋砚,人眼基本就感覺(jué)是連續(xù)的視頻了。動(dòng)畫(huà)片就是這個(gè)原理:一張圖像里面相鄰的區(qū)域或者一段時(shí)間內(nèi)連續(xù)圖像的相同位置摄杂,像素坝咐、亮度、色溫差別比較小析恢,所以視頻壓縮本質(zhì)就是利于這種空間冗余和時(shí)間上冗余進(jìn)行編碼墨坚,我們可以選取一段時(shí)間第一幅圖像的YUV值,后面的只需要記錄和這個(gè)的完整圖像的差別即可映挂,同時(shí)即使記錄一副圖像的YUV值泽篮,當(dāng)有鏡頭完全切換時(shí),我們又選取切換后的第一張作為基本圖像柑船,后面有一篇文章回講述下目前視頻壓縮的基本原理帽撑。
所以從這里面就可以引申以下幾個(gè)概念:
GOP:Group Of Pictures(圖像組),指的就是兩個(gè)I幀之間的間隔. 比較說(shuō)GOP為120,如果是720 p60 的話,那就是2s一次I幀.
幀:一副圖像編碼后的視頻數(shù)據(jù)也叫做一幀鞍时,其中有I幀亏拉、B幀、P幀逆巍,前文多次提到及塘,不再贅述;
片:一幀圖像又可以劃分為很多片蒸苇,由一個(gè)片或者多個(gè)片組成磷蛹;
宏塊:視頻編碼的最小處理單元吮旅,承載了視頻的具體YUV信息溪烤,一片由一個(gè)或者多個(gè)宏塊組成味咳;
所以視頻流分析的對(duì)象可以用下面的圖片描述:
如果站在數(shù)據(jù)的角度分析NALU的層次關(guān)系,如下圖:
這里視頻幀被劃分為一個(gè)片或者多個(gè)片檬嘀,其中slice數(shù)據(jù)主要就是通過(guò)NLAU進(jìn)行傳輸槽驶,其中slice數(shù)據(jù)又是由:
一個(gè)Slice = Silce + Slice Data
一幀圖片跟 NALU 的關(guān)聯(lián) :
究竟 NALU 是怎么由一幀圖片變化而來(lái)的呀,H.264究竟為什么這么神奇鸳兽?
一幀圖片經(jīng)過(guò) H.264 編碼器之后掂铐,就被編碼為一個(gè)或多個(gè)片(slice),而裝載著這些片(slice)的載體揍异,就是 NALU 了全陨,我們可以來(lái)看看 NALU 跟片的關(guān)系(slice)。
引用自:http://www.reibang.com/p/9522c4a7818d
Slice片類(lèi)型:
片類(lèi)型 | 含義 |
---|---|
I片 | 只包含I宏塊 |
P片 | 包含P和I宏塊 |
B片 | 包含B和I宏塊 |
SP片 | 包含P 和/或 I宏塊,用于不同碼流之間的切換 |
SI片 | 一種特殊類(lèi)型的編碼宏塊 |
設(shè)置片的目的是限制誤碼的擴(kuò)散和傳輸衷掷,也就是一幀圖像中它們的編碼片是互相獨(dú)立的辱姨,這樣假設(shè)其中一張圖像的某一個(gè)片有問(wèn)題導(dǎo)致解碼花屏,但是這個(gè)影響范圍就控制在這個(gè)片中戚嗅,這就是我們平時(shí)看視頻發(fā)現(xiàn)只有局部花屏和綠屏的原因雨涛。
Slice Data里面?zhèn)鬏數(shù)氖且粋€(gè)個(gè)宏塊,宏塊中的數(shù)據(jù)承載各個(gè)像素點(diǎn)YUV的壓縮數(shù)據(jù)懦胞。一個(gè)圖像通常被我們劃分成宏塊來(lái)研究替久,通常有1616、168等格式躏尉。我們解碼的過(guò)程也就是恢復(fù)這些像素陣列的過(guò)程蚯根,如果知道了每個(gè)像素點(diǎn)的亮度和色度,就能渲染出一張完整的圖像醇份,圖像的快速播放即是視頻稼锅。
剛才提到了宏塊.那么什么是宏塊呢?
宏塊是視頻信息的主要承載者僚纷。一個(gè)編碼圖像通常劃分為多個(gè)宏塊組成.包含著每一個(gè)像素的亮度和色度信息矩距。視頻解碼最主要的工作則是提供高效的方式從碼流中獲得宏塊中像素陣列。
一個(gè)宏塊 = 一個(gè)16*16的亮度像素 + 一個(gè)8×8Cb + 一個(gè)8×8Cr彩色像素塊組成怖竭。(YCbCr 是屬于 YUV 家族的一員,在YCbCr 中 Y 是指亮度分量锥债,Cb 指藍(lán)色色度分量,而 Cr 指紅色色度分量)
其中宏塊MB的類(lèi)型:
宏塊分類(lèi) | 意義 |
---|---|
I宏塊 | 利用從當(dāng)前片中已解碼的像素作為參考進(jìn)行幀內(nèi)預(yù)測(cè) |
P宏塊 | 利用前面已編碼圖像作為參考進(jìn)行幀內(nèi)預(yù)測(cè)痊臭,一個(gè)幀內(nèi)編碼的宏塊可進(jìn)一步作宏塊的分割:即16×16.16×8.8×16.8×8亮度像素塊哮肚。如果選了8×8的子宏塊,則可再分成各種子宏塊的分割广匙,其尺寸為8×8允趟,8×4,4×8鸦致,4×4 |
B宏塊 | 利用雙向的參考圖像(當(dāng)前和未來(lái)的已編碼圖像幀)進(jìn)行幀內(nèi)預(yù)測(cè) |
宏塊的結(jié)構(gòu):
H.264碼流示例分析:
這里我們分析一下H.264的NLAU數(shù)據(jù),其中包括了非VCL的NALU數(shù)據(jù)和VCL的NALU潮剪。
H.264碼流的NLAU單元:
我們發(fā)現(xiàn)視頻流數(shù)據(jù)就是由一系列SPS涣楷、PPS、I抗碰、P狮斗、B幀序列組成,這些數(shù)據(jù)都是通過(guò)NALU進(jìn)行承載的弧蝇;
我們看到了NALU不僅僅可以承載SPS碳褒、PPS、SEI等非VCL數(shù)據(jù)看疗,也可以傳輸I沙峻、B、P幀的切片VCL數(shù)據(jù)两芳。
我們同時(shí)看到了NALU的Data部分专酗,如果是VCL數(shù)據(jù),則就是slice header+silce data這種結(jié)構(gòu)盗扇,其中對(duì)VCL的SODB做了bit填充的字節(jié)對(duì)齊處理祷肯;
4. 這里由于沒(méi)有數(shù)據(jù)分割機(jī)制,所以一個(gè)NALU承載一個(gè)片疗隶,同時(shí)一個(gè)片就是一個(gè)視頻幀佑笋;
4.至于NALU的非VCL數(shù)據(jù)SPS、PPS斑鼻、SEI各個(gè)字段的含義具體解析放到下篇文章蒋纬,這個(gè)信息對(duì)于解碼器進(jìn)行播放視頻很重要,很多播放問(wèn)題都是這個(gè)數(shù)據(jù)有問(wèn)題導(dǎo)致的坚弱;
上面看了視頻的GOP序列蜀备,視頻幀信息和片的組成,下面分析片中的宏塊信息荒叶;
H.264的層次結(jié)構(gòu):
我們能看到整個(gè)視頻的視頻序列碾阁,顯示看了該視頻有I、B些楣、P幀信息脂凶,這和上面的分析結(jié)果是一致的;
中間圖片部分用不同顏色的點(diǎn)顯示了宏塊和子宏塊信息愁茁,右上角是對(duì)宏塊內(nèi)容的具體說(shuō)明蚕钦;
其中不同的幀類(lèi)型上面的宏塊類(lèi)型也是不一樣的;
總結(jié):
本文主要講述了平時(shí)研究和分析視頻流對(duì)象的層次鹅很,然后這些視頻數(shù)據(jù)通過(guò)NALU傳輸時(shí)嘶居,NALU的類(lèi)型和層次關(guān)系,以及NALU數(shù)據(jù)在不同層次的輸出促煮。最后用視頻分析工具分析了H.264裸碼流驗(yàn)證了上述層次關(guān)系邮屁。
所以對(duì)H.264數(shù)據(jù)分析時(shí)胸蛛,一定要了解你現(xiàn)在分析的層次和框架,因?yàn)槊總€(gè)層次我們關(guān)心的數(shù)據(jù)處理對(duì)象是不一樣的樱报,這個(gè)非常重要。
一般H.264的分析工具都是收費(fèi)的泞当,也有一些免費(fèi)和裁剪版本供大家學(xué)習(xí)和使用迹蛤。推薦幾個(gè):Elecard StreamEye、CodecVisa襟士、VideoEye盗飒、H264Analyzer、H264Visa等陋桂,有時(shí)需要交叉使用才能完成對(duì)你關(guān)心信息的分析逆趣,這些都放到我的Git上了,大家獲取使用即可嗜历。
rtsp 保存為.h264分析
25fps i幀間隔50 用分析軟件查看可以看出每50幀一個(gè)i幀宣渗,并發(fā)送sps、pps梨州、sei
sps痕囱、pps、sei暴匠、I幀綁在一起發(fā)送