本篇隸屬于文集:《H264/AVC 句法和語義詳解》,查看文集全部文章,請點(diǎn)擊文字鏈接。
想看最新文章,可以直接關(guān)注微信公眾號(hào):金架構(gòu)
在這篇文章中,我們會(huì)涉及三個(gè)非常重要的問題:
1、如何獲取一條主線和多條輔線,來學(xué)習(xí)h264解碼器
2、為什么描述子,是正確打開碼流解析的第一步
3、學(xué)習(xí)描述子
在前面幾篇中,我們對(duì)h264的碼流結(jié)構(gòu)有了初步的了解。但是這還遠(yuǎn)遠(yuǎn)不夠,因?yàn)槲覀兊哪繕?biāo),是通過學(xué)習(xí)h264的解碼流程,去探索h264里面的每個(gè)知識(shí)點(diǎn)。所以在這個(gè)時(shí)候,讓讀者明白我們現(xiàn)在身處的位置,是一件至關(guān)重要的事情。因?yàn)橹挥羞@樣,我們才不會(huì)局限于某一篇文章,或某一個(gè)知識(shí)點(diǎn),而是從全局考慮,為什么要這樣做。
而且,我希望看我文章的讀者,看完文章后獲取的,不只是關(guān)于音視頻的各個(gè)知識(shí)點(diǎn)。還有更重要的,那就是思考和自學(xué)的能力!在這篇文章結(jié)束時(shí),無論新手還是有經(jīng)驗(yàn)的同學(xué),都可以從宏觀出發(fā),看出這個(gè)系列文章未來十篇的走勢。并且我們還可以共同學(xué)習(xí),一起研究。
1. 如何獲取一條學(xué)習(xí)主線
在快餐文化盛行的今天,很多人都在學(xué)習(xí)零零碎碎的知識(shí),卻沒有將各個(gè)知識(shí)點(diǎn),形成一種互相聯(lián)系的知識(shí)結(jié)構(gòu)。就比如關(guān)于h264,很多人可能知道幀內(nèi)預(yù)測、幀間預(yù)測、變換、量化、熵編碼,但如果你問他一個(gè)h264編碼器或解碼器該怎么做,他可能就一頭霧水。
所以這時(shí)候一條學(xué)習(xí)主線就非常重要,這是在你學(xué)習(xí)過程中,無論遇到什么困難,都能夠把你拉回到問題本身,并讓你知道現(xiàn)在所處位置的關(guān)鍵。
而在h.264里,這樣的一條學(xué)習(xí)主線也非常重要,它就是h.264解碼器框架!
1.1 學(xué)習(xí)主線:H.264解碼器框架
很多人并不會(huì)把流程圖當(dāng)成一回事,其實(shí)一個(gè)流程圖,有可能是一篇文章、一個(gè)系列文章、書中一個(gè)章節(jié)或一本書的一個(gè)核心。
而根據(jù)上圖,我們可以制定兩個(gè)學(xué)習(xí)思路:
(1)根據(jù)如圖所示流程,從最右側(cè)碼流開始,一步步各個(gè)擊破,最終學(xué)完解碼器各個(gè)知識(shí)點(diǎn)。
(2)編碼器和解碼器其實(shí)只是步驟相反罷了,一邊學(xué)習(xí)解碼器各個(gè)知識(shí)點(diǎn),一邊反過來思考,編碼過程如何實(shí)現(xiàn)。
當(dāng)然,我們還可以對(duì)上述路線進(jìn)行分段,因?yàn)橥暾囊粭l學(xué)習(xí)主線,會(huì)包含一段段進(jìn)階路線。
1.2 學(xué)習(xí)主線分段
我個(gè)人是這樣進(jìn)行分段的:
這樣分段是有根據(jù)的,因?yàn)樽铋_始我們手上有的,就是一個(gè)后綴為.h264的碼流文件而已。所以我們首先要做的,就是先把句法元素解析出來。而如圖所示,每一步我們需要學(xué)習(xí)的技術(shù)如下:
(1)h264句法元素的解析:NALU的結(jié)構(gòu)和熵解碼,同時(shí)熵解碼又包括指數(shù)哥倫布編碼、CAVLC、CABAC。只要掌握了NALU和熵解碼,我們就可以從碼流中解析出各個(gè)句法元素的值。即使我們完全是個(gè)小白,也可以按照h264協(xié)議完成這步。
(2)數(shù)據(jù)準(zhǔn)備:DCT變換、量化、重排序,和它們相關(guān)的句法元素及語義。這個(gè)過程是為后面的重建圖像做準(zhǔn)備,這時(shí)我們已經(jīng)解析出各個(gè)句法元素,實(shí)現(xiàn)該過程需要配合各步驟所需要的句法元素。
(3)重建圖像:幀內(nèi)預(yù)測、幀間預(yù)測、去塊效應(yīng)濾波器。這時(shí)候碼流數(shù)據(jù)已經(jīng)完全解壓縮,就差拿著殘差數(shù)據(jù)、參考圖像和預(yù)測所需的句法元素,預(yù)測出預(yù)測數(shù)據(jù)。預(yù)測數(shù)據(jù)經(jīng)過去塊效應(yīng)后即可得解碼宏塊,當(dāng)前圖像的所有宏塊解碼完成,就可以得到重建圖像用于顯示。
所以我們目前所處的位置,即將到達(dá)熵解碼階段。
2. 如何獲取多條學(xué)習(xí)輔線
學(xué)習(xí)輔線這種事情,相當(dāng)于知識(shí)點(diǎn)的各個(gè)擊破,也相當(dāng)于知識(shí)點(diǎn)的聯(lián)系和延伸。我們可以一開始就設(shè)置多條輔線,而且在學(xué)的同時(shí),還可以再設(shè)置輔線。就拿h264的解碼學(xué)習(xí)來說,我們可以設(shè)置如下輔線:
(1)h.264 POC的計(jì)算
(2)h.264的加權(quán)預(yù)測
(3)h.264 FMO
(4)h.264參考圖像列表
(5)加權(quán)預(yù)測
(6)量化
(7)幀內(nèi)預(yù)測
(8)片、宏塊之間的關(guān)系
(9)片類型與宏塊類型
(10)NALU
(11)指數(shù)哥倫布編碼
(12)熵編碼
(13)YUV顏色空間
(14)殘差
這些問題,可以是各個(gè)知識(shí)點(diǎn),也可以是自己問自己的問題,它們的意義,在于在學(xué)習(xí)主線上,一路設(shè)置哨崗,相當(dāng)于摸著石頭過河。要知道學(xué)習(xí)新知識(shí)的重大突破,就是你得有石頭可摸!
3. 為什么描述子是正確打開碼流解析的第一步
為了要清楚的解釋這個(gè)問題,我們得先知道什么是描述子?
3.1 什么是描述子?
還記得在NALU Header的解析中,我們說過,forbidden_zero_bit的值對(duì)應(yīng)1個(gè)bit,nal_ref_idc的值對(duì)應(yīng)2個(gè)bit,nal_unit_type的值對(duì)應(yīng)5個(gè)bit。但是我沒說,我是怎么知道哪個(gè)句法元素的值,對(duì)應(yīng)幾個(gè)bit的?或者說,我是怎么知道,句法元素的值是怎么計(jì)算的?
這就是描述子的作用,比如我們之前計(jì)算過的NALU Header的句法元素,它在h.264協(xié)議中規(guī)定如下:
右側(cè)標(biāo)紅框的,就是各句法元素對(duì)應(yīng)的描述子,它表示了,這個(gè)句法元素的值是如何計(jì)算的。其中f(1)、u(2)、u(5)功能一樣,為順序讀取1、2和5個(gè)bit位,作為句法元素的值。所以我們才說,forbidden_zero_bit、nal_ref_idc、nal_unit_type的值,分別對(duì)應(yīng)1、2、5個(gè)bit。
3.2 描述子種類
那是不是所有的語法元素都是以,連續(xù)讀取接下來的n個(gè)比特這種模式來計(jì)算的呢?不是的,在h.264協(xié)議中,規(guī)定有如下描述子:
描述子乍一看很多,其實(shí)我們可以把它們分為三類:
(1)連續(xù)讀取n(包含b(8))個(gè)比特:b(8)、f(n)、i(n)、u(n),其中只有i(n)為有符號(hào)整數(shù),并且?guī)缀跤貌坏?,其他情況則順序從左至右,讀取固定數(shù)量的n個(gè)bit即可。如上面所講的NALU Header的句法元素
(2)指數(shù)哥倫布編碼:ue(v)、me(v)、se(v)、te(v),這四個(gè)描述子,都是指數(shù)哥倫布編碼。注意到它們使用的變量是v而不是n,它們的值,并不是直接等于讀取固定長度的比特。而是先根據(jù)其他句法元素的值,來確定讀取多少比特,然后再將讀取到的比特,進(jìn)行轉(zhuǎn)換才能得到所求句法元素的值。
關(guān)于它們,我們后面會(huì)單獨(dú)開幾篇來介紹。
(3)CAVLC、CABAC:ae(v)、ce(v),同指數(shù)哥倫布編碼一樣,CAVLC和CABAC也屬于變長編碼,這也是我們需要學(xué)習(xí)的一大重點(diǎn)。
3.3 利用描述子解析句法元素
所以這時(shí),只要我們懂得,各個(gè)描述子是如何計(jì)算的,我們就能根據(jù)它們解析出句法元素的值。不過需要注意的是,有時(shí)候我們會(huì)看到這種情況:
可以看到,在解析宏塊層mb_type的時(shí)候,該句法元素對(duì)應(yīng)了兩個(gè)描述子,分別為ue(v)和ae(v),并且它們之間用豎線 “|” 分隔開。
H.264協(xié)議規(guī)定,出現(xiàn)這種情況,得根據(jù)另一句法元素entropy_coding_mode_flag 的值來判斷:
如果entropy_coding_mode_flag等于0,則使用左邊的描述子,這時(shí)為ue(v)。
如果entropy_coding_mode_flag等于1,則使用右邊的描述子,這時(shí)為ae(v)。
3.4 為什么描述子是正確打開碼流解析的第一步
這個(gè)時(shí)候,我們就可以來回答最開始這個(gè)問題了。
如1.2學(xué)習(xí)主線分段所說,我們?nèi)绻M(jìn)行碼流解析,第一步則是進(jìn)行句法元素的解析,而句法元素的解析,又依賴于剛才所講的那幾種描述子。通過剛才的學(xué)習(xí)我們也知道,學(xué)習(xí)描述子,其實(shí)就相當(dāng)于學(xué)習(xí)熵編碼。
所以我們接下來,就從學(xué)習(xí)描述子開始。