使用Android高性能音频--OpenSL ES和AAudio
AAudio的概念介紹:
AAudio 是作為 OpenSL ES 庫的輕量級原生 Android 替代項而開發。 與 OpenSL ES 相比,AAudio API 不僅較小,而且容易使用。
AAudio 是在 Android O 版本中引入的全新 Android C API。
因此 API 是專為需要低延遲的高性能音頻應用而設計。 應用通過讀取并將數據寫入流來與 AAudio 進行通信。
GitHub :AAudio Android demo地址http:// https://github.com/googlesamples/android-audio-high-performance/tree/master/aaudio
使用Android系統底層的OpenSL ES或者AAudio都可以實現一個高性能的音頻程序,尤其是AAudio更是簡單易用,性能上,功能上都更佳,但是AAudio 是在 Android O 版本中才引入的全新 Android C API,在以前的系統版本中只能使用OpenSL ES。
- 那么我們需要做的是在新版本系統中使用AAudio
- 在不支持AAudio的系統版本中使用OpenSL ES,兩套 API 同時使用。
不要怕困難,因為這是一個即面向未來,又兼顧現在的優秀方案。不要害怕這有多么困難,Google已經幫我們實現了——Oboe。
OpenSL ES 谷歌使用文檔介紹如下:
注意:開發者應考慮使用開源 Oboe 庫,這個庫可在?GitHub?上獲得。Oboe 是一個 C++ 封裝容器,提供與?AAudio?非常相似的 API。Oboe 在 AAudio 可用時對其進行調用,并在 AAudio 不可用時回退到 OpenSL ES。
?OpenSL ES?? API 規范的 Android 專用實現。利用這個庫,不論您是編寫合成器、數字音頻工作站、卡拉 OK 應用、游戲還是其他實時應用,都可以使用 C 或 C++ 實現高性能、低延遲的音頻。
- OpenSL ES 與 Android Java 框架中的?MediaPlayer?和?MediaRecorder?API 提供類似的音頻功能。
- OpenSL ES 提供 C 語言接口和 C++ 綁定,讓您可以從使用任意一種語言編寫的代碼中調用 API。
OpenSL ES API 可以幫助您開發和提升應用的音頻性能。
如何使用oboe?
官方指導文檔在這里,可以通過兩種方式使用oboe,這里是把源碼拉下來編譯到項目。
1. 下載oboe庫->https://github.com/google/oboe
Oboe GitHub 主頁 : GitHub/Oboe
① 簡單使用 : Getting Started
② Oboe 全指南 :?Full Guide To Oboe
③ Oboe API 參考 : API reference
④ Android 音頻框架發展 : Android audio history
OpenSL 的生命周期如下:
為 OpenSL ES 播放器設置 采樣率 與 采樣緩沖區參數 :
( 1 ) 在 Java 層獲取采樣率與采樣緩沖區大小 :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);// 獲取采樣率String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);int defaultSampleRate = Integer.parseInt(sampleRateStr);// 獲取采樣緩沖區String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);// 調用 Native 方法傳入本地層 native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst); }( 2 ) 在 C++ 代碼中設置 OpenSL ES 的參數 :
JNIEXPORT void JNICALL Java_com_google_sample_oboe_hellooboe_MainActivity_native_1setDefaultStreamValues(JNIEnv *env,jclass type,jint sampleRate,jint framesPerBurst) {oboe::DefaultStreamValues::SampleRate = (int32_t) sampleRate;oboe::DefaultStreamValues::FramesPerBurst = (int32_t) framesPerBurst; }上述的采樣率 和 緩沖區大小是用于 設置 Android 設備內置音頻設備 的 , 如 內置揚聲器 , 聽筒 ;
外置設備 , 如耳機 , 藍牙音箱 需要設置更大的緩沖區 ;
Oboe 音頻流
Oboe 的主要作用是在 Android 應用 和 Android 設備中的 音頻 輸入 / 輸出 設備 之間 操作移動音頻數據 ;
Android 應用 輸入 / 輸出 數據 方案 :
- 通過使用 回調函數 實現
- 直接從 音頻流 中 讀取 / 寫出 數據
音頻數據讀寫調用 , 可以是 阻塞的 ( 同步 ) , 也可以是 非阻塞的 ( 異步 ) ;
- 阻塞 : 調用后 , 阻塞等待回應 , 回應收到后 , 才往下執行 ;
- 非阻塞 : 調用后 , 不等待回應 , 直接向后執行后續代碼 ;
音頻流根據如下屬性定義 :
- 音頻流方向 : 音頻設備作為 數據源 或 流數據接收器 ( 數據目的地 ) ;
- 共享模式 : 獨占模式 / 共享模式 ; 獨占模式 下音頻流獨占該設備 , 其它音頻流不允許訪問該設備 , 性能高 ; 共享模式 , 多個音頻流可以同時訪問該設備 , 性能低 ;
- 采樣格式 : 音頻流數據的采樣格式 ;
?
Oboe 音頻設備
音頻設備與音頻流對應關系 : 每個 Oboe 音頻流都需要關聯一個單獨的音頻設備 ; 注意對應關系 , 一個音頻設備可以關聯多個音頻流 , 但是 一個音頻流只能關聯一個音頻設備 ;
音頻設備作用 : 音頻設備是一個硬件接口或者虛擬端口 , 一般作為 連續的數字音頻數據流的 源端 或 目的端 ; 音頻設備作為 數據源 或 流數據接收器 ( 數據目的地 ) ;
音頻設備舉例 : Android 設備的 內置麥克風 , 揚聲器 , 電話聽筒 , 或外接的耳機 , 藍牙音箱 等 ;
獲取音頻設備 : Android 6.0 Marshmallow( API Level 23 ) 及以上的版本 , 可以通過調用 AudioManager 的 getDevices() 方法 , 獲取當前的可用音頻設備 , 該方法會返回設備的類型和信息 ;
音頻設備 ID : 每個音頻設備都有一個 唯一的 ID 標識 , 使用該標識 , 可以實現將 音頻流 與 指定的 音頻設備進行綁定 ; 多數情況下 , 用戶不需要自己設置音頻設備 , Oboe 會自動選擇主設備 , 推薦讓 Oboe 自動選擇 , 不要進行手動干預 ;
音頻流方向 : 音頻設備 可以 決定該音頻流是 輸入流 還是 輸出流
- 輸入流 : 麥克風 , 采集音頻數據 ; 設備 -> 內存 ;
- 輸出流 : 揚聲器 , 播放音頻數據 ; 內存 -> 設備 ;
打開 Oboe 音頻流時 , 系統會檢查音頻流方向 , 如果你設置的是麥克風 , 但是音頻流方向設置成了輸出方向 , 那么打開音頻流操作就會出錯 ;
使用總結:
第一步:導入oboe頭文件
第二步:創建builder并設置參數
第三步:設置回調接口
第四步:播放音頻
第五步:關閉音頻流
下面提供一下Oboe使用實例:
錄制器:
setupCommonStreamParameters(oboe::AudioStreamBuilder *builder) {builder->setAudioApi(mAudioApi)->setSharingMode(oboe::SharingMode::Exclusive)->setPerformanceMode(oboe::PerformanceMode::LowLatency);return builder;setupRecordingStreamParameters(oboe::AudioStreamBuilder *builder ,bool isCallBack) {builder->setCallback(this)->setDirection(oboe::Direction::Input)return setupCommonStreamParameters(builder);oboe::AudioStream *mRecordingStream = nullptr;oboe::AudioStreamBuilder builder;setupRecordingStreamParameters(&builder);oboe::Result result = builder.openStream(&mRecordingStream);if (result == oboe::Result::OK && mRecordingStream) {oboe::Result result = mRecordingStream->requestStart(); //開始錄制。}播放器:
setupCommonStreamParameters(oboe::AudioStreamBuilder *builder) {builder->setAudioApi(mAudioApi)->setSharingMode(oboe::SharingMode::Exclusive)->setPerformanceMode(oboe::PerformanceMode::LowLatency);return builder;setupPlayStreamParameters(oboe::AudioStreamBuilder *builder ,bool isCallBack) {builder->setCallback(this)->setDirection(oboe::Direction::output)return setupCommonStreamParameters(builder);oboe::AudioStream *mPlayStream = nullptr;oboe::AudioStreamBuilder builder;setupPlayStreamParameters(&builder);oboe::Result result = builder.openStream(&mPlayStream);if (result == oboe::Result::OK && mPlayStream) {oboe::Result result = mPlayStream->requestStart(); //開始播放。}oboe::DataCallbackResult LiveEffectEngine::onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) {render(audioData,numFrames); //audioData放入數據即可播放。return oboe::DataCallbackResult::Continue;}?
總結
以上是生活随笔為你收集整理的使用Android高性能音频--OpenSL ES和AAudio的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 Cuttlefish 虚拟 And
- 下一篇: 解决SurfaceView渲染的各种疑难