文章目錄
H264幀基礎(chǔ)知識(shí)
一組圖像 GOP
IDR幀與I幀
P幀
B幀
H264 profile level
H264碼率控制
H264 Annexb byte-stream格式
NALU header
NALU start-code
H264 AVCC格式
extradata結(jié)構(gòu)詳解
H264 Annexb與AVCC格式轉(zhuǎn)換
H264 Annexb轉(zhuǎn)為AVCC
AVCC 轉(zhuǎn)為 H264 Annexb
進(jìn)入音視頻領(lǐng)域也快一年了,空閑時(shí)間思考一下,覺得有必要把一些關(guān)于H264與流媒體的知識(shí)總結(jié)一下,對(duì)自己在音視頻領(lǐng)域內(nèi)的知識(shí)進(jìn)行梳理,不至于隨著時(shí)間的流逝而遺忘。
H264幀基礎(chǔ)知識(shí)
一組圖像 GOP
所謂GOP就是1組圖像Group of Picture,在這一組圖像中有且只有1個(gè)I幀,多個(gè)P幀或B幀,兩個(gè)I幀之間的幀數(shù),就是一個(gè)GOP。
GOP一般設(shè)置為編碼器每秒輸出的幀數(shù),即每秒幀率,一般為25或30,當(dāng)然也可設(shè)置為其他值。
在一個(gè)GOP中,P、B幀是由I幀預(yù)測得到的,當(dāng)I幀的圖像質(zhì)量比較差時(shí),會(huì)影響到一個(gè)GOP中后續(xù)P、B幀的圖像質(zhì)量,直到下一個(gè)GOP 開始才有可能得以恢復(fù),所以GOP值也不宜設(shè)置過大。
由于P、B幀的復(fù)雜度大于I幀,所以過多的P、B幀會(huì)影響編碼效率,使編碼效率降低。另外,過長的GOP還會(huì)影響Seek操作的響應(yīng)速度,由于P、B幀是由前面的I或P幀預(yù)測得到的,所以Seek操作需要直接定位,解碼某一個(gè)P或B幀時(shí),需要先解碼得到本GOP內(nèi)的I幀及之前的N個(gè)預(yù)測幀才可以,GOP值越長,需要解碼的預(yù)測幀就越多,seek響應(yīng)的時(shí)間也越長。
IDR幀與I幀
在I幀中,所有宏塊都采用幀內(nèi)預(yù)測的方式,因此解碼時(shí)僅用I幀的數(shù)據(jù)就可重構(gòu)完整圖像,不需要參考其他畫面而生成。
H.264中規(guī)定了兩種類型的I幀:普通I幀(normal Iframes)和IDR幀(InstantaneousDecoding Refresh, 即時(shí)解碼刷新)。 IDR幀實(shí)質(zhì)也是I幀,使用幀內(nèi)預(yù)測。IDR幀的作用是立即刷新,會(huì)導(dǎo)致DPB(Decoded Picture Buffer參考幀列表)清空,而I幀不會(huì)。所以IDR幀承擔(dān)了隨機(jī)訪問功能,一個(gè)新的IDR幀開始,可以重新算一個(gè)新的Gop開始編碼,播放器永遠(yuǎn)可以從一個(gè)IDR幀播放,因?yàn)樵谒鬀]有任何幀引用之前的幀。如果一個(gè)視頻中沒有IDR幀,這個(gè)視頻是不能隨機(jī)訪問的。所有位于IDR幀后的B幀和P幀都不能參考IDR幀以前的幀,而普通I幀后的B幀和P幀仍然可以參考I幀之前的其他幀。IDR幀阻斷了誤差的積累,而I幀并沒有阻斷誤差的積累。
一個(gè)GOP序列的第一個(gè)圖像叫做 IDR 圖像(立即刷新圖像),IDR 圖像都是 I 幀圖像,但I(xiàn)幀不一定都是IDR幀,只有GOP序列的第1個(gè)I幀是IDR幀。
I幀:幀內(nèi)編碼幀 ,I幀表示關(guān)鍵幀,你可以理解為這一幀畫面的完整保留;解碼時(shí)只需要本幀數(shù)據(jù)就可以完成(因?yàn)榘暾嬅妫?/p>
它是一個(gè)幀內(nèi)壓縮編碼幀,壓縮比約為7。它將全幀圖像信息進(jìn)行JPEG壓縮編碼及傳輸;
解碼時(shí)僅用I幀的數(shù)據(jù)就可重構(gòu)完整圖像;
I幀描述了圖像背景和運(yùn)動(dòng)主體的詳情;
I幀不需要參考其他畫面而生成;
I幀是P幀和B幀的參考幀(其質(zhì)量直接影響到同組中以后各幀的質(zhì)量);
幀是幀組GOP的基礎(chǔ)幀(第一幀),在一組中只有一個(gè)I幀;
I幀不需要考慮運(yùn)動(dòng)矢量;
I幀所占數(shù)據(jù)的信息量比較大。
疑問:按照GOP、IDR幀、I幀的解釋,如果一個(gè)GOP出現(xiàn)除去第一個(gè)IDR幀之外的I幀,是不存在的,那這樣的話,就不存在非IDR的I幀了,可是為什么還要說明非IDR的I幀呢。
解答:H264編碼存在多種編碼方式CBR、VBR、CVBR、ABR等等,VBR編碼模式下圖像內(nèi)容變化差異很大時(shí),會(huì)動(dòng)態(tài)調(diào)整I幀的數(shù)量,因此GOP的概念需要修正:兩個(gè)IDR幀之間的間隔為一組GOP,一組GOP中可以出現(xiàn)非IDR的I幀。
P幀
P幀:前向預(yù)測編碼幀。P幀表示的是這一幀跟之前的一個(gè)關(guān)鍵幀(或P幀)的差別,解碼時(shí)需要用之前緩存的畫面疊加上本幀定義的差別,生成最終畫面,P幀沒有完整畫面數(shù)據(jù),只有與前一幀的畫面差異的數(shù)據(jù)。P幀的壓縮率20
P幀是I幀后面相隔1~2幀的編碼幀;
P幀采用運(yùn)動(dòng)補(bǔ)償?shù)姆椒▊魉退c前面的I或P幀的差值及運(yùn)動(dòng)矢量(預(yù)測誤差);
解碼時(shí)必須將I幀中的預(yù)測值與預(yù)測誤差求和后才能重構(gòu)完整的P幀圖像;
P幀屬于前向預(yù)測的幀間編碼。它只參考前面最靠近它的I幀或P幀;
P幀可以是其后面P幀的參考幀,也可以是其前后的B幀的參考幀;
由于P幀是參考幀,它可能造成解碼錯(cuò)誤的擴(kuò)散;
由于是差值傳送,P幀的壓縮比較高。
B幀
B幀:雙向預(yù)測內(nèi)插編碼幀。B幀是雙向差別幀,也就是B幀記錄的是本幀與前后幀的差別,要解碼B幀,不僅要取得之前的緩存畫面,還要解碼之后的畫面,通過前后畫面的與本幀數(shù)據(jù)的疊加取得最終的畫面。B幀壓縮率高,約為50,但是解碼時(shí)CPU會(huì)比較累。
B幀是由前面的I或P幀和后面的P幀來進(jìn)行預(yù)測的
B幀傳送的是它與前面的I或P幀和后面的P幀之間的預(yù)測誤差及運(yùn)動(dòng)矢量
B幀是雙向預(yù)測編碼幀
B幀壓縮比最高,因?yàn)樗环从潮麉⒖紟g運(yùn)動(dòng)主體的變化情況,預(yù)測比較準(zhǔn)確;
B幀不是參考幀,不會(huì)造成解碼錯(cuò)誤的擴(kuò)散。
H264 profile level
BP-Baseline Profile:基本畫質(zhì)。支持I/P 幀,只支持無交錯(cuò)(Progressive)和CAVLC;
EP-Extended profile:進(jìn)階畫質(zhì)。支持I/P/B/SP/SI 幀,只支持無交錯(cuò)(Progressive)和CAVLC;
MP-Main profile:主流畫質(zhì)。提供I/P/B 幀,支持無交錯(cuò)(Progressive)和交錯(cuò)(Interlaced),也支持CAVLC 和CABAC 的支持;
HP-High profile:高級(jí)畫質(zhì)。在main Profile 的基礎(chǔ)上增加了8x8內(nèi)部預(yù)測、自定義量化、無損視頻編碼和更多的YUV 格式。
一般可以輸出H264幀的USB攝像頭,使用的是BP-Baseline Profile,只有I幀與P幀。
H264碼率控制
VBR:Variable BitRate,動(dòng)態(tài)比特率,其碼率可以隨著圖像的復(fù)雜程度的不同而變化,因此其編碼效率比較高,Motion發(fā)生時(shí),馬賽克很少。碼率控制算法根據(jù)圖像內(nèi)容確定使用的比特率,圖像內(nèi)容比較簡單則分配較少的碼率(似乎碼字更合適),圖像內(nèi)容復(fù)雜則分配較多的碼字,這樣既保證了質(zhì)量,又兼顧帶寬限制。這種算法優(yōu)先考慮圖像質(zhì)量。
ABR:Average BitRate,平均比特率 是VBR的一種插值參數(shù)。ABR在指定的文件大小內(nèi),以每50幀 (30幀約1秒)為一段,低頻和不敏感頻率使用相對(duì)低的流量,高頻和大動(dòng)態(tài)表現(xiàn)時(shí)使用高流量,可以做為VBR和CBR的一種折衷選擇。
CBR:Constant BitRate,是以恒定比特率方式進(jìn)行編碼,有Motion發(fā)生時(shí),由于碼率恒定,只能通過增大QP來減少碼字大小,圖像質(zhì)量變差,當(dāng)場景靜止時(shí),圖像質(zhì)量又變好,因此圖像質(zhì)量不穩(wěn)定。優(yōu)點(diǎn)是壓縮速度快,缺點(diǎn)是每秒流量都相同容易導(dǎo)致空間浪費(fèi)。
CVBR:Constrained Variable it Rate,VBR的一種改進(jìn),兼顧了CBR和VBR的優(yōu)點(diǎn):在圖像內(nèi)容靜止時(shí),節(jié)省帶寬,有Motion發(fā)生時(shí),利用前期節(jié)省的帶寬來盡可能的提高圖像質(zhì)量,達(dá)到同時(shí)兼顧帶寬和圖像質(zhì)量的目的。這種方法通常會(huì)讓用戶輸入最大碼率和最小碼率,靜止時(shí),碼率穩(wěn)定在最小碼率,運(yùn)動(dòng)時(shí),碼率大于最小碼率,但是又不超過最大碼率。
H264 Annexb byte-stream格式
SODB:String of Data Bits,數(shù)據(jù) bit 流,最原始的編碼數(shù)據(jù)
RBSP:Raw Byte Sequence Payload,原始字節(jié)序列載荷,在SODB的后面填加了結(jié)尾比特,RBSP trailing bits 一個(gè)bit“1”,若干比特“0”,以便字節(jié)對(duì)齊
EBSP:Encapsulated Byte Sequence Payload,擴(kuò)展字節(jié)序列載荷,在RBSP基礎(chǔ)上填加了仿校驗(yàn)字節(jié)(0x03)。
Start-code:在NALU加到Annexb即byte-stream格式時(shí),需要在每組NALU之前添加開始碼StartCode,如果該NALU對(duì)應(yīng)的slice為1個(gè)GOP開始則用4位字節(jié)表示,0x00000001,否則用3位字節(jié)表示0x000001(也不一定)。
NALU header
bit位描述
F
禁止位,0表示正常,1表示錯(cuò)誤,一般都是0
NRI
重要級(jí)別,11表示非常重要,一般取值為11、10、01
Type:nal_unit_type
表示該NALU的類型是什么,類型的具體取值可見下表
nal_unit_typeNAL類型
0
未使用
1
非IDR的片
2
片數(shù)據(jù)A分區(qū)
3
片數(shù)據(jù)B分區(qū)
4
片數(shù)據(jù)C分區(qū)
5
一個(gè)序列的第一個(gè)圖像叫做 IDR 圖像(立即刷新圖像),IDR 圖像都是 I 幀
6
補(bǔ)充增強(qiáng)信息單元(SEI)
7
序列參數(shù)集(SPS)
8
圖像參數(shù)集(PPS)
9
分界符
10
序列結(jié)束
11
碼流結(jié)束
12
填充
13…23
保留
24…31
未使用
NALU Header常見的取值:0x67 0x68 0x65 0x61,0x47 0x48 0x45 0x41,0x27 0x28 0x25 0x21
NALU header描述
0x67,0x47,0x27
SPS, 序列參數(shù)集,重要級(jí)別分別為11、10、01
0x68,0x48,0x28
PPS,圖像參數(shù)集,重要級(jí)別分別為11、10、01
0x65,0x45,0x25
IDR幀,重要級(jí)別分別為11、10、01
0x61,0x41,0x21
非IDR幀,重要級(jí)別分別為11、10、01
NALU start-code
一個(gè)NALU包中的數(shù)據(jù)并不包含它的大小(長度)信息,因此不能簡單的連接NALU包來建立一個(gè)流,因?yàn)槟悴恢酪粋€(gè)包從哪里結(jié)束,另一個(gè)包從哪里開始。
Annex B格式用起始碼來解決這個(gè)問題,即給每個(gè)NALU加上前綴碼:2個(gè)或者3個(gè)0x00,后面再加一個(gè)0x01, 如:0x000001或者0x00000001。
4字節(jié)類型的開始碼通常只用于標(biāo)識(shí)流中的隨機(jī)訪問點(diǎn),如SPS PPS AUD和IDR,然后其他地方都用3字節(jié)類型的開始碼以減少數(shù)據(jù)量。
如果該NALU對(duì)應(yīng)的slice為1個(gè)GOP開始則用4位字節(jié)表示,0x00000001,否則用3位字節(jié)表示0x000001(不一定)
防競爭字節(jié):為了使NALU主體中不包括與開始碼相沖突的,在編碼時(shí),就插入一個(gè)字節(jié)的0x03;解碼時(shí)將0x03去掉。也稱為脫殼操作。
編碼器將每個(gè)NAL各自獨(dú)立、完整地放入一個(gè)分組,因?yàn)榉纸M都有頭部,解碼器可以方便地檢測出NAL的分界,并依次取出NAL進(jìn)行解碼。每個(gè)NAL前有一個(gè)起始碼 0x00 00 01(或者0x00 00 00 01),解碼器檢測每個(gè)起始碼,作為一個(gè)NAL的起始標(biāo)識(shí),當(dāng)檢測到下一個(gè)起始碼時(shí),當(dāng)前NAL結(jié)束。同時(shí)H.264規(guī)定,當(dāng)檢測到0x000000時(shí),也可以表征當(dāng)前NAL的結(jié)束。那么NAL中數(shù)據(jù)出現(xiàn)0x000001或0x000000時(shí)怎么辦?H.264引入了防止競爭機(jī)制,如果編碼器檢測到NAL數(shù)據(jù)存在0x000001或0x000000時(shí),編碼器會(huì)在最后個(gè)字節(jié)前插入一個(gè)新的字節(jié)0x03,這樣:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解碼器檢測到0x000003時(shí),把03拋棄,恢復(fù)原始數(shù)據(jù)。解碼器在解碼時(shí),首先逐個(gè)字節(jié)讀取NAL的數(shù)據(jù),統(tǒng)計(jì)NAL的長度,然后再開始解碼。
H264 AVCC格式
Annex-B:沒有NALU長度字節(jié),使用start code分隔NALU,start code為三字節(jié)或四字節(jié),0x000001或0x00000001,一般是四字節(jié);SPS和PPS按流的方式寫在一組GOP之前。
AVCC:使用NALU長度,固定字節(jié),通常為4字節(jié),分隔NALU;一般在每個(gè)NALU頭部為4字節(jié)大端格式的長度字節(jié),在一組GOP的頭部包含extradata結(jié)構(gòu),用于存儲(chǔ)sequence-header、SPS、PPS數(shù)據(jù)。
雖然AVCC格式不使用起始碼,防競爭字節(jié)仍然存在
extradata結(jié)構(gòu)詳解
namelength, unit: bitvalue
version
8bit
0x01
avc profile
8bit
0x64
avc compatibility
8bit
0x00
avc level
8bit
0x0A
NALULengthSizeMinusOne
8bit
0xFF,高6位保留,默認(rèn)為1,低2位為11,表示NALU長度用3+1=4字節(jié)表示
number of SPS NALUs
8bit
0xE1,高3位保留,低5位表示有幾個(gè)SPS,通常只有1個(gè)SPS
SPS size
16bit
大端格式的SPS長度,0x0019,表示25字節(jié)SPS
SPS NALU data
SPS size × 8bit
0x67到0x80,表示SPS數(shù)據(jù)
number of PPS NALUs
8bit
0x01PPS個(gè)數(shù),通常只有1個(gè)PPS
PPS size
8bit
0x07,表示PPS的數(shù)據(jù)長度
PPS NALU data
PPS size × 8bit
0x68到0x30,表示PPS數(shù)據(jù)
NALULengthSizeMinusOne解釋:這個(gè)變量告訴我們用幾個(gè)字節(jié)來存儲(chǔ)NALU的長度,如果NALULengthSizeMinusOne是0,那么每個(gè)NALU使用一個(gè)字節(jié)的前綴來指定長度,那么每個(gè)NALU包的最大長度是255字節(jié),這個(gè)明顯太小了,使用2個(gè)字節(jié)的前綴來指定長度,那么每個(gè)NALU包的最大長度是64K字節(jié),也不一定夠,一般分辨率達(dá)到1280*720 的圖像編碼出的I幀,可能大于64K;3字節(jié)是比較完美的,但是因?yàn)橐恍┰颍ɡ鐚?duì)齊)沒有被廣泛支持;因此4字節(jié)長度的前綴是目前使用最多的方式,
H264 Annexb與AVCC格式轉(zhuǎn)換
H264 Annexb轉(zhuǎn)為AVCC
1、對(duì)于一個(gè)GOP的開始,根據(jù)start-code,分離出SPS、PPS幀,并分別計(jì)算出長度
2、根據(jù)SPS, PPS創(chuàng)建出extradata,附加到GOP的頭部
3、從IDR幀開始,搜索start-code,分離出每一個(gè)NALU,計(jì)算長度,然后將start-code轉(zhuǎn)為4字節(jié)的NALU長度
AVCC 轉(zhuǎn)為 H264 Annexb
1、對(duì)于一個(gè)GOP的開始,首先檢索出extradata部分的數(shù)據(jù)
2、根據(jù)extradata數(shù)據(jù)創(chuàng)建出SPS幀,并用4字節(jié)的start-code:0x00000001附加在SPS數(shù)據(jù)的頭部
3、根據(jù)extradata數(shù)據(jù)創(chuàng)建出PPS幀,并用4字節(jié)的start-code:0x00000001附加在PPS數(shù)據(jù)的頭部,并將PPS數(shù)據(jù)連接到SPS后面
4、根據(jù)NALU的長度字段,分離出每一個(gè)NALU,然后用用4字節(jié)的start-code:0x00000001替換長度字段
5、在以上過程中需要計(jì)算每一個(gè)NALU的長度,尤其是GOP的IDR幀,一般在IDR幀前還有SPS與PPS幀,其長度需要一起計(jì)算。
本文對(duì)多篇博客進(jìn)行了參考,進(jìn)行了總結(jié),著重確定了我想理解的部分,謝謝廣大博友的貢獻(xiàn)。
聯(lián)系客服