最魯棒的Android聲音錄制和播放封裝庫了

Werner7873 8年前發布 | 45K 次閱讀 JNI 安卓開發 Android開發 移動開發

來自: http://blog.piasy.com/Robust-Android-Audio-encapsulation/

安卓開發過程中一旦開始和硬件打交道,以及涉及到一定的native代碼之后,各種閃退就開始浮出水面了,聲音錄制和播放當然不例外。本文總結了YOLO安卓客戶端大半年來的安卓音頻實踐,整理出一套系統API的封裝,命名為 RxAndroidAudio

概覽

安卓平臺和聲音錄制與播放相關的主要是4個類:MediaRecorder,MediaPlayer,AudioRecord和AudioTrack。

MediaRecorder可以錄制視頻和音頻到文件,MediaPlayer可以播放視頻和音頻文件,AudioRecord可以提供接口讀取音頻流數據(byte數組或者short數組),AudioTrack提供接口用于播放音頻流數據。

小結一下,其中MediaRecorder和AudioRecord用于聲音錄制,MediaPlayer和AudioTrack用于聲音播放。AudioRecord和AudioTrack用于操作音頻流數據,操作對象是byte數組(或者short數組),而MediaRecorder和MediaPlayer提供了經過更高層抽象和封裝接口,直接對文件進行操作,而且他倆功能更豐富,同時支持音頻和視頻。

本文會涉及到部分關于聲音錄制和播放更底層的實現,詳細的分析并不是本文的內容范圍了,此外本文主要目標并不是聲音錄制和播放的使用教程,本文主要關注的是上述4個類的使用過程中需要注意的地方。

基于文件的操作

使用MediaRecorder錄制聲音到文件:

  • mRecorder.prepare() 調用需要 捕獲 IOException 和 RuntimeException ,注意需要捕獲 RuntimeException 而不是 IllegalStateException ,盡管Java doc中只聲明了會拋出IllegalStateException,但是查看 jni層代碼 可以看到,prepare的調用也是可能會觸發RuntimeException的;
  • mRecorder.start() , mRecorder.stop() , mRecorder.reset() 調用也需要捕獲 RuntimeException ,理由同上;
  • 無論是prepare還是start,拋出異常之后 都需要reset和release
  • 需要保證不會對jni層進行多線程的調用,以免出現下面這樣的“靜默閃退”( 參考資料 ),RxAndroidAudio通過 單例synchronized方法 來保證這一點:

    A/libc: Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1), thread 9302 (RxComputationTh)

  • 當用戶錄完聲音,需要停止錄音,調用stop的時候, 需要sleep一段時間 ,以免最后幾百毫秒錄不上,這有可能是安卓系統音頻編碼器的bug, 參考資料
  • 當prepare返回后,有些低端的設備需要再延遲一段時間開始說話,以免開頭幾百毫秒錄不上,可以 在prepare返回后延遲幾百毫秒(例如300ms)再顯示初始化完畢的UI ,原因還需要繼續尋找;

使用MediaPlayer播放聲音文件:

  • 和MediaRecorder一樣,prepare,start,stop,reset,release函數的調用都需要捕獲異常;
  • 和MediaRecorder一樣,需要保證不會對jni層進行多線程的調用;
  • MediaPlayer提供了兩種音頻文件播放方式:通過文件絕對路徑指定播放文件,或者使用資源文件id指定;絕對路徑的方式 需要調用 mPlayer.prepare() ,而資源文件id方式不需要;
  • 在 MediaPlayer.OnCompletionListener 的 onCompletion 回調中,需要 延遲一定時間再釋放MediaPlayer ,否則可能導致下次緊接著的播放無法成功(靜默失敗,不會拋出異常),原因還需要繼續尋找;

基于數據流的操作

使用AudioRecord錄制音頻流數據:

  • 和MediaRecorder一樣,startRecording需要捕獲異常;
  • 和MediaRecorder一樣,需要保證不會對jni層進行多線程的調用;
  • mAudioRecord.read 的返回值需要進行錯誤檢查;
  • mAudioRecord.read 傳入的參數類型需要進行區分 , ENCODING_PCM_16BIT 格式的錄音需要傳入short數組, ENCODING_PCM_8BIT 格式的錄音需要傳入byte數組,盡管在 jni層的實現 都是一樣的,但是 Java doc 明確說明了這一注意事項,理應遵循;
  • RxAndroidAudio使用 ExecutorService 來執行異步任務(從AudioRecord中循環讀取數據);

使用AudioTrack播放音頻流數據:

  • 和MediaRecorder一樣,write需要捕獲異常;
  • 和MediaRecorder一樣,需要保證不會對jni層進行多線程的調用;

Bonus Part

Reactive!

RxAndroidAudio之所以叫Rx,就是因為它盡可能的提供了Reactive的API。

RxAudioPlayer播放聲音文件:

</div>

 本文由用戶 Werner7873 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!