開篇
煊赫一時,望而生畏的音視頻開發
時至今日,短視頻App可謂是如日中天,一片興興向榮。隨着短視頻的興起,音視頻開發也愈來愈受到重視,可是因爲音視頻開發涉及知識面比較廣,入門門檻相對較高,讓許許多多開發者望而生畏。web
爲何寫這一系列博文
雖然網上有不少的博文總結了音視頻打怪升級的路線,可是音視頻開發相關的知識都相對獨立,有講「音視頻解碼相關」的,有講「OpenGL相關」的,也有講「FFmpeg相關的」,可是對於新手來講,把全部的知識銜接串聯起來,並很好的理解全部的知識,倒是很是困難的。算法
本人在學習音視頻開發的過程當中,深入體會到了因爲知識的分散,過渡斷層帶來的種種困惑和痛苦,所以,但願經過本身的理解,能夠把音視頻開發相關的知識總結出來,並造成系列文章,按部就班,剖析各個環節,一則對本身所學作一個總結和鞏固,二則但願能夠幫助想入門音視頻開發的開發者小夥伴們。api
【聲 明】
首先,這一系列文章均基於本身的理解和實踐,可能有不對的地方,歡迎你們指正。
其次,這是一個入門系列,涉及的知識也僅限於夠用,深刻的知識網上也有許許多多的博文供你們學習了。
最後,寫文章過程當中,會借鑑參考其餘人分享的文章,會在文章最後列出,感謝這些做者的分享。網絡
碼字不易,轉載請註明出處!數據結構
教程代碼:【Github傳送門】
目錄
1、Android音視頻硬解碼篇:
1,音視頻基礎知識
2,音視頻硬解碼流程
3,音視頻播放:音視頻同步
4,音視頻解封和封裝:生成一個MP4
2、使用OpenGL渲染視頻畫面篇
1,初步瞭解OpenGL ES
2,使用OpenGL渲染視頻畫面
3,OpenGL渲染多視屏,實現畫中畫
4,深刻了解OpenGL之EGL
5,OpenGL FBO數據緩衝區
6,Android音視頻硬編碼:生成一個MP4
3、Android FFmpeg音視頻解碼篇
1,FFmpeg so庫編譯
2,Android 引入FFmpeg
3,Android FFmpeg視頻解碼播放
4,Android FFmpeg+OpenSL ES音頻解碼播放
5,Android FFmpeg+OpenGL ES播放視頻
6,Android FFmpeg簡單合成MP4:視屏解封與從新封裝
7,Android FFmpeg視頻編碼
本文你能夠了解到
做爲開篇的文章,咱們先來看看音視頻由什麼構成的,以及一些常見的術語和概念。框架
1、視頻是什麼?
動畫書
不知道你們小時候是否玩過一種動畫小人書,連續翻動的時候,小人書的畫面就會變成一個動畫,相似如今的gif格式圖片。ide
動畫書:來源網絡學習
原本是一本靜態的小人書,經過翻動之後,就會變成一個有趣的小動畫,若是畫面夠多,翻動速度夠快的話,這其實就是一個小視頻。動畫
而視頻的原理正是如此,因爲人類眼睛的特殊結構,畫面快速切換時,畫面會有殘留,感受起來就是連貫的動做。因此,視頻就是由一系列圖片構成的。編碼
視頻幀
幀,是視頻的一個基本概念,表示一張畫面,如上面的翻頁動畫書中的一頁,就是一幀。一個視頻就是由許許多多幀組成的。
幀率
幀率,即單位時間內幀的數量,單位爲:幀/秒 或fps(frames per second)。如動畫書中,一秒內包含多少張圖片,圖片越多,畫面越順滑,過渡越天然。
幀率的通常如下幾個典型值:
24/25 fps:1秒 24/25 幀,通常的電影幀率。
30/60 fps:1秒 30/60 幀,遊戲的幀率,30幀能夠接受,60幀會感受更加流暢逼真。
85 fps以上人眼基本沒法察覺出來了,因此更高的幀率在視頻裏沒有太大意義。
色彩空間
這裏咱們只講經常使用到的兩種色彩空間。
RGB
RGB的顏色模式應該是咱們最熟悉的一種,在如今的電子設備中應用普遍。經過R G B三種基礎色,能夠混合出全部的顏色。
YUV
這裏着重講一下YUV,這種色彩空間並非咱們熟悉的。這是一種亮度與色度分離的色彩格式。
早期的電視都是黑白的,即只有亮度值,即Y。有了彩色電視之後,加入了UV兩種色度,造成如今的YUV,也叫YCbCr。
Y:亮度,就是灰度值。除了表示亮度信號外,還含有較多的綠色通道量。
U:藍色通道與亮度的差值。
V:紅色通道與亮度的差值。
採用YUV有什麼優點呢?
人眼對亮度敏感,對色度不敏感,所以減小部分UV的數據量,人眼卻沒法感知出來,這樣能夠經過壓縮UV的分辨率,在不影響觀感的前提下,減少視頻的體積。
RGB和YUV的換算
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
——————————————————
R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U
1
2
3
4
5
6
7
2、音頻是什麼?
音頻數據的承載方式最經常使用的是脈衝編碼調製,即PCM。
在天然界中,聲音是接二連三的,是一種模擬信號,那怎樣才能把聲音保存下來呢?那就是把聲音數字化,即轉換爲數字信號。
咱們知道聲音是一種波,有本身的振幅和頻率,那麼要保存聲音,就要保存聲音在各個時間點上的振幅。
而數字信號並不能連續保存全部時間點的振幅,事實上,並不須要保存連續的信號,就能夠還原到人耳可接受的聲音。
根據奈奎斯特採樣定理:爲了避免失真地恢復模擬信號,採樣頻率應該不小於模擬信號頻譜中最高頻率的2倍。
根據以上分析,PCM的採集步驟分爲如下步驟:
模擬信號->採樣->量化->編碼->數字信號
音頻採樣
採樣率和採樣位數
採樣率,即採樣的頻率。
上面提到,採樣率要大於原聲波頻率的2倍,人耳能聽到的最高頻率爲20kHz,因此爲了知足人耳的聽覺要求,採樣率至少爲40kHz,一般爲44.1kHz,更高的一般爲48kHz。
採樣位數,涉及到上面提到的振幅量化。波形振幅在模擬信號上也是連續的樣本值,而在數字信號中,信號通常是不連續的,因此模擬信號量化之後,只能取一個近似的整數值,爲了記錄這些振幅值,採樣器會採用一個固定的位數來記錄這些振幅值,一般有8位、16位、32位。
位數 最小值 最大值
8 0 255
16 -32768 32767
32 -2147483648 2147483647
位數越多,記錄的值越準確,還原度越高。
最後就是編碼了。因爲數字信號是由0,1組成的,所以,須要將幅度值轉換爲一系列0和1進行存儲,也就是編碼,最後獲得的數據就是數字信號:一串0和1組成的數據。
整個過程以下:
[圖片上傳失敗…(image-8c5d9f-1568988187211)]
聲道數
聲道數,是指支持能不一樣發聲(注意是不一樣聲音)的音響的個數。
單聲道:1個聲道
雙聲道:2個聲道
立體聲道:默認爲2個聲道
立體聲道(4聲道):4個聲道
碼率
碼率,是指一個數據流中每秒鐘能經過的信息量,單位bps(bit per second)
碼率 = 採樣率 採樣位數 聲道數
3、爲何要編碼
這裏的編碼和上面音頻中提到的編碼不是同個概念,而是指壓縮編碼。
咱們知道,在計算機的世界中,一切都是0和1組成的,音頻和視頻數據也不例外。因爲音視頻的數據量龐大,若是按照裸流數據存儲的話,那將須要耗費很是大的存儲空間,也不利於傳送。而音視頻中,其實包含了大量0和1的重複數據,所以能夠經過必定的算法來壓縮這些0和1的數據。
特別在視頻中,因爲畫面是逐漸過渡的,所以整個視頻中,包含了大量畫面/像素的重複,這正好提供了很是大的壓縮空間。
所以,編碼能夠大大減少音視頻數據的大小,讓音視頻更容易存儲和傳送。
4、視頻編碼
視頻編碼格式
視頻編碼格式有不少,好比H26x系列和MPEG系列的編碼,這些編碼格式都是爲了適應時代發展而出現的。
其中,H26x(1/2/3/4/5)系列由ITU(International Telecommunication Union)國際電傳視訊聯盟主導
MPEG(1/2/3/4)系列由MPEG(Moving Picture Experts Group, ISO旗下的組織)主導。
固然,他們也有聯合制定的編碼標準,那就是如今主流的編碼格式H264,固然還有下一代更先進的壓縮編碼標準H265。
H264編碼簡介
H264是目前最主流的視頻編碼標準,因此咱們後續的文章中主要以該編碼格式爲基準。
H264由ITU和MPEG共同定製,屬於MPEG-4第十部份內容。
因爲H264編碼算法十分複雜,不是一時半刻可以講清楚的,也不在本人目前的能力範圍內,因此這裏只簡單介紹在平常開發中須要瞭解到的概念。實際上,視頻的編碼和解碼部分一般由框架(如Android硬解/FFmpeg)完成,通常的開發者並不會接觸到。
視頻幀
咱們已經知道,視頻是由一幀一幀畫面構成的,可是在視頻的數據中,並非真正按照一幀一幀原始數據保存下來的(若是這樣,壓縮編碼就沒有意義了)。
H264會根據一段時間內,畫面的變化狀況,選取一幀畫面做爲完整編碼,下一幀只記錄與上一幀完整數據的差異,是一個動態壓縮的過程。
在H264中,三種類型的幀數據分別爲
I幀:幀內編碼幀。就是一個完整幀。
P幀:前向預測編碼幀。是一個非完整幀,經過參考前面的I幀或P幀生成。
B幀:雙向預測內插編碼幀。參考先後圖像幀編碼生成。B幀依賴其前最近的一個I幀或P幀及其後最近的一個P幀。
圖像組:GOP和關鍵幀:IDR
全稱:Group of picture。指一組變化不大的視頻幀。
GOP的第一幀成爲關鍵幀:IDR
IDR都是I幀,能夠防止一幀解碼出錯,致使後面全部幀解碼出錯的問題。當解碼器在解碼到IDR的時候,會將以前的參考幀清空,從新開始一個新的序列,這樣,即使前面一幀解碼出現重大錯誤,也不會蔓延到後面的數據中。
注:關鍵幀都是I幀,可是I幀不必定是關鍵幀
DTS與PTS
DTS全稱:Decoding Time Stamp。標示讀入內存中數據流在何時開始送入解碼器中進行解碼。也就是解碼順序的時間戳。
PTS全稱:Presentation Time Stamp。用於標示解碼後的視頻幀何時被顯示出來。
在沒有B幀的狀況下,DTS和PTS的輸出順序是同樣的,一旦存在B幀,PTS和DTS則會不一樣。
幀的色彩空間
前面咱們介紹了RGB和YUV兩種圖像色彩空間。H264採用的是YUV。
YUV存儲方式分爲兩大類:planar 和 packed。
planar:先存儲全部Y,緊接着存儲全部U,最後是V;
packed:每一個像素點的 Y、U、V 連續交叉存儲。
planar以下:
YUV Planar
packed以下:
YUV Packed
不過pakced存儲方式已經很是少用,大部分視頻都是採用planar存儲方式。
上面說過,因爲人眼對色度敏感度低,因此能夠經過省略一些色度信息,即亮度共用一些色度信息,進而節省存儲空間。所以,planar又區分了如下幾種格式: YUV44四、 YUV42二、YUV420。
YUV 4:4:4採樣,每個Y對應一組UV份量。
YUV 4:2:2採樣,每兩個Y共用一組UV份量。
YUV 4:2:0採樣,每四個Y共用一組UV份量。
其中,最經常使用的就是YUV420。
YUV420格式存儲方式
YUV420屬於planar存儲方式,可是又分兩種類型:
YUV420P:三平面存儲。數據組成爲YYYYYYYYUUVV(如I420)或YYYYYYYYVVUU(如YV12)。
YUV420SP:兩平面存儲。分爲兩種類型YYYYYYYYUVUV(如NV12)或YYYYYYYYVUVU(如NV21)
關於H264的編碼算法和數據結構,涉及的知識和篇幅不少(如網絡抽象層NAL、SPS、PPS),本文再也不深刻細說,網上也有比較多的教程講解,有興趣能夠自行深刻學習。
入門理解H264編碼
5、音頻編碼
音頻編碼格式
原始的PCM音頻數據也是很是大的數據量,所以也須要對其進行壓縮編碼。
和視頻編碼同樣,音頻也有許多的編碼格式,如:WAV、MP三、WMA、APE、FLAC等等,音樂發燒友應該對這些格式很是熟悉,特別是後兩種無損壓縮格式。
可是,咱們今天的主角不是他們,而是另一個叫AAC的壓縮格式。
AAC是新一代的音頻有損壓縮技術,一種高壓縮比的音頻壓縮算法。在MP4視頻中的音頻數據,大多數時候都是採用AAC壓縮格式。
AAC編碼簡介
AAC格式主要分爲兩種:ADIF、ADTS。
ADIF:Audio Data Interchange Format。 音頻數據交換格式。這種格式的特徵是能夠肯定的找到這個音頻數據的開始,不需進行在音頻數據流中間開始的解碼,即它的解碼必須在明肯定義的開始處進行。這種格式經常使用在磁盤文件中。
ADTS:Audio Data Transport Stream。 音頻數據傳輸流。這種格式的特徵是它是一個有同步字的比特流,解碼能夠在這個流中任何位置開始。它的特徵相似於mp3數據流格式。
ADTS能夠在任意幀解碼,它每一幀都有頭信息。ADIF只有一個統一的頭,因此必須獲得全部的數據後解碼。且這兩種的header的格式也是不一樣的,目前通常編碼後的都是ADTS格式的音頻流。
ADIF數據格式:
header raw_data
ADTS 一幀 數據格式(中間部分,左右省略號爲先後數據幀):
ADTS
AAC內部結構也再也不贅述,能夠參考AAC 文件解析及解碼流程
6、音視頻容器
細心的讀者可能已經發現,前面咱們介紹的各類音視頻的編碼格式,沒有一種是咱們平時使用到的視頻格式,好比:mp四、rmvb、avi、mkv、mov…
沒錯,這些咱們熟悉的視頻格式,實際上是包裹了音視頻編碼數據的容器,用來把以特定編碼標準編碼的視頻流和音頻流混在一塊兒,成爲一個文件。
例如:mp4支持H26四、H265等視頻編碼和AAC、MP3等音頻編碼。
mp4是目前最流行的視頻格式,在移動端,通常將視頻封裝爲mp4格式。
7、硬解碼和軟解碼
硬解和軟解的區別
咱們在一些播放器中會看到,有硬解碼和軟解碼兩種播放形式給咱們選擇,可是咱們大部分時候並不能感受出他們的區別,對於普通用戶來講,只要能播放就好了。
那麼他們內部究竟有什麼區別呢?
在手機或者PC上,都會有CPU、GPU或者解碼器等硬件。一般,咱們的計算都是在CPU上進行的,也就是咱們軟件的執行芯片,而GPU主要負責畫面的顯示(是一種硬件加速)。
所謂軟解碼,就是指利用CPU的計算能力來解碼,一般若是CPU的能力不是很強的時候,一則解碼速度會比較慢,二則手機可能出現發熱現象。可是,因爲使用統一的算法,兼容性會很好。
硬解碼,指的是利用手機上專門的解碼芯片來加速解碼。一般硬解碼的解碼速度會快不少,可是因爲硬解碼由各個廠家實現,質量良莠不齊,很是容易出現兼容性問題。
Android平臺的硬解碼
終於來到有關Android的部分了,做爲本文的結尾,也算是爲下一篇文章開一個頭。
MediaCodec 是Android 4.1(api 16)版本引入的編解碼接口,是全部想在Android上開發音視頻的開發人員繞不開的坑。
因爲Android碎片化嚴重,雖然通過多年的發展,Android硬解已經有了很大改觀,但實際上各個廠家實現不一樣, 仍是會有一些意想不到的坑。
相對於FFmpeg,Android原生硬解碼仍是相對容易入門一些,因此接下來,我將會從MediaCodec入手,講解如何實現視頻的編解碼,以及引入OpenGL實現對視頻的編輯,最後才引入FFmpeg來實現軟解,算是一個比較常規的音視頻開發入門流程吧。