標籤:andoid 音頻 mediacodec 編解碼 opus
前面四篇文章分別介紹了音頻開發必備的基礎知識、如何採集一幀音頻、如何播放一幀音頻、如何儲存和解析wav格式的檔案,建議有興趣的小夥伴們先讀一讀,本文則重點關注如何對一幀音頻資料進行編碼和解碼。
1. Android 官方的 MediaCodec API
首先,我們瞭解一下 Android 官方提供的音頻編解碼的 API,即 MediaCodec 類,該 API 是在 Andorid 4.1 (API 16) 版本引入的,因此只能工作於 Android 4.1 以上的手機上。
1.1 MediaCodec 基本介紹
(1)提供了一套訪問 Android 底層多媒體模組的介面,主要是音視頻的編解碼介面
(2)Android 底層多媒體模組採用的是 OpenMax 架構,任何 Android 底層編解碼模組的實現,都必須遵循 OpenMax 標準。Google 官方預設提供了一系列的軟體轉碼器:包括:OMX.google.h264.encoder,OMX.google.h264.encoder, OMX.google.aac.encoder, OMX.google.aac.decoder 等等,而硬體編解碼功能,則需要由晶片廠商依照 OpenMax 架構標準來完成,所以,一般採用不同晶片型號的手機,硬體編解碼的實現和效能是不同的
(3)Android 應用程式層統一由 MediaCodec API 來提供各種音視頻編解碼功能,由參數配置來決定採用何種編解碼演算法、是否採用硬體編解碼加速等等
1.2 MediaCodec 核心原理
我不準備詳細介紹 MediaCodec API 的每個函數是怎麼用,範例程式碼大家可以在後面給出的資源連結中查看和學習。
這裡我準備重點介紹一下 MediaCodec 的核心工作原理,因為只有搞清楚了這一點,你才會明白為什麼 MediaCodec API 提供的介面是這個樣子的。
MediaCodec 使用的基本流程是:
- createEncoderByType/createDecoderByType- configure- start- while(1) { - dequeueInputBuffer - queueInputBuffer - dequeueOutputBuffer - releaseOutputBuffer}- stop- release
由此可以看到,Buffer 隊列的操作是其最核心的部分之一,關於 MediaCodec 的 Buffer 隊列 ,如下:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/7E/7B/wKioL1cCSojSz4HdAAD0xxQxvwg230.png" title="Image.png" style="float:left;" alt="wKioL1cCSojSz4HdAAD0xxQxvwg230.png" />
MediaCodec 架構上採用了2個緩衝區隊列,非同步處理資料,下面描述的 Client 和 MediaCodec 模組是並行工作的(註:這裡的 Client 就是指 “開發人員,API 的使用者”):
(1)Client 從 input 緩衝區隊列申請 empty buffer [dequeueInputBuffer]
(2)Client 把需要編解碼的資料拷貝到 empty buffer,然後放入 input 緩衝區隊列 [queueInputBuffer]
(3)MediaCodec 模組從 input 緩衝區隊列取一幀資料進行編解碼處理
(4)編解碼處理結束後,MediaCodec 將未經處理資料 buffer 置為 empty 後放回 input 緩衝區隊列,將編解碼後的資料放入到 output 緩衝區隊列
(5)Client 從 output 緩衝區隊列申請編解碼後的 buffer [dequeueOutputBuffer]
(6)Client 對編解碼後的 buffer 進行渲染/播放
(7)渲染/播放完成後,Client 再將該 buffer 放回 output 緩衝區隊列 [releaseOutputBuffer]
MediaCodec 在架構上,其實是採用了一種基於“環形緩衝區”的“生產者-消費者”模式,它設計了 2 個基於 idx 序號的“環形緩衝區” ,注意,是 2 個,一個在 input 端, 一個在 output 端。
我曾經在 Github 上分享過一段 Linux C 代碼,名叫:“rw_queue”,就是這種環形緩衝區的簡化版,大家有興趣可以看看,地址:https://github.com/Jhuster/clib/tree/master/rw_queue
基於 idx 的環形緩衝區的總體如下,圖中,wp 代表 “寫指標”,指向的是 “empty buffer”, 而 rp 代表 “讀指標”,指向的是 “filled buffer”:
650) this.width=650;" src="http://s5.51cto.com/wyfs02/M01/7E/7B/wKioL1cCStbQJjLDAAB7AoEE3KU663.png" title="rw_queue.png" alt="wKioL1cCStbQJjLDAAB7AoEE3KU663.png" />
“生產者”和“消費者”其實是共用這一個緩衝區隊列,“生產者”負責從隊列中取出未使用的 Buffer,填入資料,然後放回隊列,“消費者”則負責取出填入資料後的 Buffer,進行處理,處理結束後,再把 Buffer 標記為“空”,退回到隊列中去以供“生產者”繼續填充資料。
在 input 端,“Client”是這個環形緩衝區“生產者”,“MediaoCodec 模組”是“消費者”。
在 output 端,“MediaoCodec 模組”是這個環形緩衝區“生產者”,而“Client”則變成了“消費者”。
這就是其核心的工作原理,其實並不複雜,大家靜下心來,很快就能理解其中的奧妙。
1.3 參考資源
關於 MediaCodec 的範例程式碼,網上其實也很多了,我就直接給出一些個人覺得不錯的連結,有興趣的小夥伴們可以去研究一下。
(1)Android 官方文檔: 《MediaCodec》
(2)《Android MediaCodec stuff》
(3)《HWEncoderExperiments》
(4)一些開源的播放器 Android 源碼,如 VLC、ijkplayer
2. 第三方音頻編解碼的庫
官方的 MediaCodec API 雖然支援硬體編解碼加速,但是問題和局限還是很多的,一方面是只能在 Android 4.1 以上機型上才能使用,另一方面,由於 Android 手機種類繁多,廠商對底層源碼的修改各不相同,導致 MediaCodec API 在實際使用中,會遇到很多坑,有很多相容性的問題,因此,我們也可以考慮採用第三方的編解碼庫。
這裡,我簡單推薦幾款第三方音頻編解碼庫(可以移植到 Android 平台的),大家可以直接去官網或者項目首頁瞭解其詳細資料。
(1) opus 編解碼庫
很喜歡 opus,低碼率下 opus 完勝曾經優勢明顯的 HE AAC,我曾經用它實現了一款 Android 區域網路的VoIP網路電話應用:“飛鴿電話”,效果很不錯。
opus 官網地址:https://www.opus-codec.org
註:如今 Android 5.0 已經官方支援 opus 格式了,關於 Android 支援的多媒體格式列表可以查看 Android developer guide:《Supported Media Formats》
(2) Speex 編解碼庫
老牌的音頻處理庫,不僅是編解碼,還提供了包括音頻去噪、回聲消除、靜音檢測等功能,官網地址:http://www.speex.org
(3) ffmpeg
大名鼎鼎的 ffmpeg 肯定不能錯過,官網:https://www.ffmpeg.org
(4)Android AAC Encoder
一款輕量級的 Android aac 編碼庫:https://github.com/timsu/android-aac-enc
(5)opencore-amr-android
從 opencore 抽取出來的 amr 編解碼庫,地址:https://github.com/kevinho/opencore-amr-android
(6)iLBC-Android
iLBC 是著名的 WebRTC 項目的音頻編解碼模組,iLBC-Android 是從中抽取 iLBC 模組移植到 Android 平台的個人項目,地址:https://github.com/lukeweber/iLBC-Android
關於第三方編解碼庫就簡單介紹到這裡了,最後三個是個人項目,我沒有使用過,真心感謝這些作者的無私奉獻,另外,更多的第三方庫歡迎大家留言或者來信補充。
3. 小結
關於如何在 Android 平台編解碼音頻資料就簡單介紹到這裡了,文章中有不清楚的地方歡迎留言或者來信 [email protected] 交流,或者關注我的新浪微博 @盧_俊 或者 公眾號 @Jhuster 擷取最新的文章和資訊。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/7E/7B/wKioL1cCUUuAWhLvAACb8XAe6Uo758.jpg" title="weixin_jhuster.jpg" alt="wKioL1cCUUuAWhLvAACb8XAe6Uo758.jpg" />
本文出自 “Jhuster的專欄” 部落格,請務必保留此出處http://ticktick.blog.51cto.com/823160/1760191
Android音頻開發(5):音頻資料的編解碼