前言
學(xué)習(xí)之前請(qǐng)容許我悼念下雷霄驊前輩,他的博客是我學(xué)習(xí)視頻編解碼資料的主要來(lái)源,雷霄驊前輩博客地址.我們主要是來(lái)學(xué)習(xí)FFmpeg和SDL,學(xué)習(xí)的第一階段我們要使用它們來(lái)做一款跨平臺(tái)的播放器,作者是從事iOS開(kāi)發(fā)的,所以會(huì)將這個(gè)跨平臺(tái)播放器集成于iOS平臺(tái),各位有什么好的建議可以在文章下方留言.FFmpeg主要是為了處理音視頻的編碼,而SDL則是為了展示畫(huà)面,至于具體的知識(shí)還是需要閱讀雷哥的博客去學(xué)習(xí).
- 視頻解碼器(FFmpeg)
主要掌握幾個(gè)常用的FFmpeg解碼的函數(shù),主要實(shí)現(xiàn)H.264->YUV的轉(zhuǎn)換
- 視頻顯示(SDL)
掌握SDL顯示視頻的函數(shù),實(shí)現(xiàn)YUV -> 設(shè)備屏幕
視頻播放器原理
-
這是雷哥的一張圖,簡(jiǎn)單介紹了視頻播放器原理.我們?nèi)粘I钪?視頻,音頻的結(jié)尾都會(huì)有類(lèi)似.MP4之類(lèi)的字符,這里就是他們的封裝格式.我們拿到這個(gè)視頻文件如果想播放的話,會(huì)將它解封裝成音頻壓縮數(shù)據(jù)和視頻壓縮數(shù)據(jù),在對(duì)這兩個(gè)數(shù)據(jù)進(jìn)行解碼,獲得音頻采樣數(shù)據(jù)和視頻像素?cái)?shù)據(jù),他們分別會(huì)送到我們的聲卡和顯卡最終到達(dá)播放設(shè)備,這里需要注意的是我們需要解決聲畫(huà)同步的問(wèn)題,這個(gè)我們以后再說(shuō).
視頻播放原理
封裝格式(MP4,RMVB,TS,F(xiàn)LV,AVI)
- 上圖講的是比較形象的封裝格式,同時(shí)也列舉了一些封裝格式的推出機(jī)構(gòu)以及目前使用的主要領(lǐng)域.下圖舉例了兩種比較代表性的封裝格式,他們的具體區(qū)別圖上可以看出,MPEG2 - TS這種封裝格式在損壞了某個(gè)TS包時(shí)依舊可以繼續(xù)播放,而FLV若損壞了文件頭那么他將無(wú)法播放.
視頻編碼數(shù)據(jù)(H.264,MPEG2,VC-1)
- 上圖主要講的幾種視頻編碼數(shù)據(jù),通俗的講視頻編碼其實(shí)就是將我們?nèi)庋劭吹降拿恳环鶊D進(jìn)行壓縮,壓縮成流,當(dāng)然編碼和壓縮肯定是不能畫(huà)等號(hào)的.這些流中也有集中壓縮散發(fā)我們后面會(huì)講到,壓縮和不壓縮前后體積大概相差百倍,這就對(duì)傳輸和存儲(chǔ)帶來(lái)了巨大的壓力,所以我們才要對(duì)其進(jìn)行視頻編碼,在我們的日常生活中H.264目前是最常見(jiàn)的,所以許多做視頻的公司招聘上都會(huì)要求了解FFmpeg,H.264等.下面我們主要對(duì)H.264中最常見(jiàn)的IPB壓縮算法來(lái)進(jìn)行介紹.
IPB
視頻流中的每一張靜止的畫(huà)面其實(shí)都是一幀,在壓縮的算法中IPB算是一種比較常見(jiàn)的算法了.I幀是關(guān)鍵幀表示一副畫(huà)面的完整數(shù)據(jù),所以解碼的時(shí)候只需要自身就可以完成,P幀是差別幀表示當(dāng)前畫(huà)面和上一幅畫(huà)面的區(qū)別,上一幅畫(huà)面的數(shù)據(jù)IPB三種幀都有可能,他的解碼依賴于上一幀數(shù)據(jù),最終實(shí)際上還是要依賴I幀才能實(shí)現(xiàn)解碼.B幀是雙向關(guān)鍵幀,表示該畫(huà)面數(shù)據(jù)與上一幀和下一幀的區(qū)別,所以解碼的時(shí)候我們需要對(duì)他的前后幀都進(jìn)行解碼,優(yōu)點(diǎn)是壓縮率高,但是處理起來(lái)計(jì)算機(jī)的處理負(fù)擔(dān)會(huì)比較大.
從上面的解釋看,我們知道I和P的解碼算法比較簡(jiǎn)單,資源占用也比較少,I只要自己完成就行了,P呢,也只需要解碼器把前一個(gè)畫(huà)面緩存一下,遇到P時(shí)就使用之前緩存的畫(huà)面就好了,如果視頻流只有I和P,解碼器可以不管后面的數(shù)據(jù),邊讀邊解碼,線性前進(jìn),大家很舒服。但網(wǎng)絡(luò)上的電影很多都采用了B幀,因?yàn)锽幀記錄的是前后幀的差別,比P幀能節(jié)約更多的空間,但這樣一來(lái),文件小了,解碼器就麻煩了,因?yàn)樵诮獯a時(shí),不僅要用之前緩存的畫(huà)面,還要知道下一個(gè)I或者P的畫(huà)面(也就是說(shuō)要預(yù)讀預(yù)解碼),而且,B幀不能簡(jiǎn)單地丟掉,因?yàn)锽幀其實(shí)也包含了畫(huà)面信息,如果簡(jiǎn)單丟掉,并用之前的畫(huà)面簡(jiǎn)單重復(fù),就會(huì)造成畫(huà)面卡(其實(shí)就是丟幀了),并且由于網(wǎng)絡(luò)上的電影為了節(jié)約空間,往往使用相當(dāng)多的B幀,B幀用的多,對(duì)不支持B幀的播放器就造成更大的困擾,畫(huà)面也就越卡。 一般平均來(lái)說(shuō),I的壓縮率是7(跟JPG差不多),P是20,B可以達(dá)到50,可見(jiàn)使用B幀能節(jié)省大量空間,節(jié)省出來(lái)的空間可以用來(lái)保存多一些I幀,這樣在相同碼率下,可以提供更好的畫(huà)質(zhì)。
上面這段話參考自CSDN Rachel-Zhang博主的一篇文章.
音頻編碼數(shù)據(jù)(AAC,MP3,AC-3)
- 一般音頻編碼的重要性遠(yuǎn)不如視頻編碼重要,因?yàn)橐纛l數(shù)據(jù)就算不壓縮,他的體積我們也是可以接受的.音頻壓縮中最常用的AAC原始碼流他是由一個(gè)一個(gè)的ADTS frame組成的,每個(gè)ADTS的大小還不是固定的.其中每個(gè)ADTS frame之間通過(guò)syncword(同步字)進(jìn)行分隔。同步字為0xFFF(二進(jìn)制“111111111111”)。AAC碼流解析的步驟就是首先從碼流中搜索0x0FFF,分離出ADTS frame;然后再分析ADTS frame的首部各個(gè)字段。這就是AAC碼流解碼時(shí)的主要邏輯.
視頻像素?cái)?shù)據(jù)(YUV420P,RGB)
- 我們生活中比較常見(jiàn)的像素?cái)?shù)據(jù)主要有兩種,RGB個(gè)式和YUV格式,其中以后者的YUV420P居多.RGB格式的原理很簡(jiǎn)單,使用RGB我們可以把一幅圖片的每個(gè)點(diǎn)上的顏色記錄下來(lái),同時(shí)生活中所有的顏色都是由三原色組成的,差別為非就是三者的比例不同罷了.YUV的原理主要就是人眼對(duì)亮度敏感而對(duì)色度并不敏感,所以對(duì)色度進(jìn)行了比較深的壓縮,可以看出對(duì)U和V分別壓縮到了Y的四分之一,也就是寬壓縮二分之一而長(zhǎng)壓縮二分之一,YUV先從左上角到右下角一行行存儲(chǔ)了圖片的Y信息,再然后是U信息,最后才是V信息,我們播放YUV視頻時(shí)一般是無(wú)法直接播放的,因?yàn)樗麤](méi)有文件頭所以我們要對(duì)視頻的一些基本信息(例如畫(huà)面寬高)進(jìn)行設(shè)置才可以播放.
音頻采樣數(shù)據(jù)(PCM)
- 學(xué)習(xí)PCM前我們需要了解什么是采樣,這個(gè)在大學(xué)教材數(shù)字信號(hào)處理有講
聲波其實(shí)是一個(gè)連續(xù)的模擬信號(hào),我們?nèi)绻朐谟?jì)算機(jī)中對(duì)他進(jìn)行處理,那么必須要將他轉(zhuǎn)換為數(shù)字信號(hào),我們對(duì)這個(gè)連續(xù)的模擬信號(hào)(類(lèi)似正弦波)進(jìn)行每隔一定的時(shí)間就進(jìn)行取點(diǎn),獲取到該點(diǎn)的縱坐標(biāo)值,然后將這些點(diǎn)用平滑的曲線連接起來(lái),就能完成模擬信號(hào)轉(zhuǎn)數(shù)字信號(hào),當(dāng)然我們還要對(duì)其中的噪聲等因素進(jìn)行處理,這些都是后話,這個(gè)采樣的時(shí)間間隔越短我們所轉(zhuǎn)化的數(shù)字信號(hào)的還原程度就越高.采樣率我們一般根據(jù)人耳的識(shí)別頻率以及奈奎斯特定理設(shè)為44.1kHZ,PCM格式也是不包含文件頭的,所以我們直接播放時(shí)要將它的采樣率和采樣精度設(shè)置好,否則就會(huì)出問(wèn)題.