【Android 音視頻開發打怪升級:音視頻硬解碼篇】一、音視頻基礎知識

開篇

炙手可熱,望而生畏的音視頻開發

時至今日,短視頻App可謂是如日中天,一片興興向榮。隨着短視頻的興起,音視頻開發也越來越受到重視,但是由於音視頻開發涉及知識面比較廣,入門門檻相對較高,讓許許多多開發者望而生畏。

爲什麼寫這一系列博文

雖然網上有很多的博文總結了音視頻打怪升級的路線,但是音視頻開發相關的知識都相對獨立,有講“音視頻解碼相關”的,有講“OpenGL相關”的,也有講“FFmpeg相關的”,但是對於新手來說,把所有的知識銜接串聯起來,並很好的理解所有的知識,卻是非常困難的。

本人在學習音視頻開發的過程中,深刻體會到了由於知識的分散,過渡斷層帶來的種種困惑和痛苦,因此,希望通過自己的理解,可以把音視頻開發相關的知識總結出來,並形成系列文章,循序漸進,剖析各個環節,一則對自己所學做一個總結和鞏固,二則希望可以幫助想入門音視頻開發的開發者小夥伴們。

【聲 明】

首先,這一系列文章均基於自己的理解和實踐,可能有不對的地方,歡迎大家指正。
其次,這是一個入門系列,涉及的知識也僅限於夠用,深入的知識網上也有許許多多的博文供大家學習了。
最後,寫文章過程中,會借鑑參考其他人分享的文章,會在文章最後列出,感謝這些作者的分享。

碼字不易,轉載請註明出處!

教程代碼:【Github傳送門

目錄

一、Android音視頻硬解碼篇:
  • 1,音視頻基礎知識
  • 2,音視頻硬解碼流程
  • 3,音視頻播放:音視頻同步
  • 4,音視頻解封和封裝:生成一個MP4
二、使用OpenGL渲染視頻畫面篇
  • 1,初步瞭解OpenGL ES
  • 2,使用OpenGL渲染視頻畫面
  • 3,OpenGL渲染多視屏,實現畫中畫
  • 4,深入瞭解OpenGL之EGL
  • 5,OpenGL FBO數據緩衝區
  • 6,Android音視頻硬編碼:生成一個MP4
三、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視頻編碼

本文你可以瞭解到

作爲開篇的文章,我們先來看看音視頻由什麼構成的,以及一些常見的術語和概念。

一、視頻是什麼?

動畫書

不知道大家小時候是否玩過一種動畫小人書,連續翻動的時候,小人書的畫面就會變成一個動畫,類似現在的gif格式圖片。

動畫書:來源網絡

本來是一本靜態的小人書,通過翻動以後,就會變成一個有趣的小動畫,如果畫面夠多,翻動速度夠快的話,這其實就是一個小視頻。

而視頻的原理正是如此,由於人類眼睛的特殊結構,畫面快速切換時,畫面會有殘留,感覺起來就是連貫的動作。所以,視頻就是由一系列圖片構成的

視頻幀

幀,是視頻的一個基本概念,表示一張畫面,如上面的翻頁動畫書中的一頁,就是一幀。一個視頻就是由許許多多幀組成的。

幀率

幀率,即單位時間內幀的數量,單位爲:幀/秒 或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

二、音頻是什麼?

音頻數據的承載方式最常用的是脈衝編碼調製,即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)

碼率 = 採樣率 * 採樣位數 * 聲道數

三、爲什麼要編碼

這裏的編碼和上面音頻中提到的編碼不是同個概念,而是指壓縮編碼

我們知道,在計算機的世界中,一切都是0和1組成的,音頻和視頻數據也不例外。由於音視頻的數據量龐大,如果按照裸流數據存儲的話,那將需要耗費非常大的存儲空間,也不利於傳送。而音視頻中,其實包含了大量0和1的重複數據,因此可以通過一定的算法來壓縮這些0和1的數據。

特別在視頻中,由於畫面是逐漸過渡的,因此整個視頻中,包含了大量畫面/像素的重複,這正好提供了非常大的壓縮空間。

因此,編碼可以大大減小音視頻數據的大小,讓音視頻更容易存儲和傳送。

四、視頻編碼

視頻編碼格式

視頻編碼格式有很多,比如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又區分了以下幾種格式: YUV444、 YUV422、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編碼

五、音頻編碼

音頻編碼格式

原始的PCM音頻數據也是非常大的數據量,因此也需要對其進行壓縮編碼。

和視頻編碼一樣,音頻也有許多的編碼格式,如:WAV、MP3、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 文件解析及解碼流程

六、音視頻容器

細心的讀者可能已經發現,前面我們介紹的各種音視頻的編碼格式,沒有一種是我們平時使用到的視頻格式,比如:mp4、rmvb、avi、mkv、mov…

沒錯,這些我們熟悉的視頻格式,其實是包裹了音視頻編碼數據的容器,用來把以特定編碼標準編碼的視頻流和音頻流混在一起,成爲一個文件。

例如:mp4支持H264、H265等視頻編碼和AAC、MP3等音頻編碼。

mp4是目前最流行的視頻格式,在移動端,一般將視頻封裝爲mp4格式。

七、硬解碼和軟解碼

硬解和軟解的區別

我們在一些播放器中會看到,有硬解碼和軟解碼兩種播放形式給我們選擇,但是我們大部分時候並不能感覺出他們的區別,對於普通用戶來說,只要能播放就行了。

那麼他們內部究竟有什麼區別呢?

在手機或者PC上,都會有CPU、GPU或者解碼器等硬件。通常,我們的計算都是在CPU上進行的,也就是我們軟件的執行芯片,而GPU主要負責畫面的顯示(是一種硬件加速)。

所謂軟解碼,就是指利用CPU的計算能力來解碼,通常如果CPU的能力不是很強的時候,一則解碼速度會比較慢,二則手機可能出現發熱現象。但是,由於使用統一的算法,兼容性會很好。

硬解碼,指的是利用手機上專門的解碼芯片來加速解碼。通常硬解碼的解碼速度會快很多,但是由於硬解碼由各個廠家實現,質量參差不齊,非常容易出現兼容性問題。

Android平臺的硬解碼

終於來到有關Android的部分了,作爲本文的結尾,也算是爲下一篇文章開一個頭。

MediaCodec 是Android 4.1(api 16)版本引入的編解碼接口,是所有想在Android上開發音視頻的開發人員繞不開的坑。

由於Android碎片化嚴重,雖然經過多年的發展,Android硬解已經有了很大改觀,但實際上各個廠家實現不同, 還是會有一些意想不到的坑。

相對於FFmpeg,Android原生硬解碼還是相對容易入門一些,所以接下來,我將會從MediaCodec入手,講解如何實現視頻的編解碼,以及引入OpenGL實現對視頻的編輯,最後才引入FFmpeg來實現軟解,算是一個比較常規的音視頻開發入門流程吧。

參考文章

音視頻開發基礎知識

YUV顏色編碼解析

YUV數據格式

音頻基礎知識

AAC 文件解析及解碼流程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章