Android TV Audio基本框架及啓動流程分析

一、Android Audio基本框架:
Android Audio模塊基本框架

1.Audio 是整個 Android 平臺非常重要的一個組成部分,負責音頻數據的採集和輸出、音頻流的控制、音頻設備的管理、音量調節等,主要包括如下部分:

Audio Application Framework:音頻應用框架

AudioTrack:負責回放數據的輸出,屬 Android 應用框架 API 類

AudioRecord:負責錄音數據的採集,屬 Android 應用框架 API 類

AudioSystem: 負責音頻事務的綜合管理,屬 Android 應用框架 API 類

Audio Native Framework:音頻本地框架

AudioTrack:負責回放數據的輸出,屬 Android 本地框架 API 類

AudioRecord:負責錄音數據的採集,屬 Android 本地框架 API 類

AudioSystem: 負責音頻事務的綜合管理,屬 Android 本地框架 API 類

Audio Services:音頻服務

AudioPolicyService:音頻策略的制定者,負責音頻設備切換的策略抉擇、音量調節策略等

AudioFlinger:音頻策略的執行者,負責輸入輸出流設備的管理及音頻流數據的處理傳輸

Audio HAL:音頻硬件抽象層,負責與音頻硬件設備的交互,由 AudioFlinger 直接調用。

與 Audio 強相關的有 MultiMedia,MultiMedia 負責音視頻的編解碼,MultiMedia 將解碼後的數據通過 AudioTrack 輸出,而 AudioRecord 採集的錄音數據交由 MultiMedia 進行編碼。

2.播放聲音可以使用 MediaPlayer 和 AudioTrack,兩者都提供 Java API 給應用開發者使用。兩者的差別在於:MediaPlayer 可以播放多種格式的音源,如 mp3、flac、wma、ogg、wav 等,而 AudioTrack 只能播放解碼後的 PCM 數據流。從上面 Android 音頻系統架構圖來看:MediaPlayer 在 Native 層會創建對應的音頻解碼器和一個 AudioTrack,解碼後的數據交由 AudioTrack 輸出。所以 MediaPlayer 的應用場景更廣,一般情況下使用它也更方便;只有一些對聲音時延要求非常苛刻的應用場景才需要用到 AudioTrack。

二、Android AudioFlinger/AudioPolicyService啓動流程
Audio Native Service啓動流程
PlaybackThread在哪裏啓動的??

其中 mpClientInterface->openOutput() 最終會調用到 AudioFlinger::openOutput():打開輸出流設備,並創建 PlaybackThread 對象:

系統啓動時,就已經打開 primary_out、low_latency、deep_buffer 這三種輸出流設備,並創建對應的 MixerThread 了;而此時 DirectOutputThread 與 OffloadThread 不會被創建,直到標識爲 AUDIO_OUTPUT_FLAG_DIRECT/AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音頻流需要輸出時,纔開始創建 DirectOutputThread/OffloadThread 和打開 direct_out/compress_offload 設備。

AudioPolicyManager 的構造函數將解析音頻策略配置文件,從而獲取到設備所支持的音頻設備信息(包括設備是否支持 Offload、Direct 模式輸出,各輸入輸出 profile 所支持的採樣率、通道數、數據格式等),加載全部 HwModule,爲之創建所有非 direct 輸出類型的 outputStream 和所有 inputStream,並創建相應的 playbackThread 或 recordThread 線程。需要注意的是,Android 7.0上的音頻策略配置文件開始使用 XML 格式,其文件名爲 audio_policy_configuration.xml,而在之前的版本上音頻策略配置文件爲 audio_policy.conf。

AndioFlinger 作爲 Android 的音頻系統引擎,重任之一是負責輸入輸出流設備的管理及音頻流數據的處理傳輸,這是由回放線程(PlaybackThread 及其派生的子類)和錄製線程(RecordThread)進行的,我們簡單看看回放線程和錄製線程類關係:
回放線程與錄製線程關係
ThreadBase:PlaybackThread 和 RecordThread 的基類
RecordThread:錄製線程類,由 ThreadBase 派生
PlaybackThread:回放線程基類,同由 ThreadBase 派生
MixerThread:混音回放線程類,由 PlaybackThread 派生,負責處理標識爲 AUDIO_OUTPUT_FLAG_PRIMARY、AUDIO_OUTPUT_FLAG_FAST、AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音頻流,MixerThread 可以把多個音軌的數據混音後再輸出

DirectOutputThread:直輸回放線程類,由 PlaybackThread 派生,負責處理標識爲 AUDIO_OUTPUT_FLAG_DIRECT 的音頻流,這種音頻流數據不需要軟件混音,直接輸出到音頻設備即可

DuplicatingThread:複製回放線程類,由 MixerThread 派生,負責複製音頻流數據到其他輸出設備,使用場景如主聲卡設備、藍牙耳機設備、USB 聲卡設備同時輸出

OffloadThread:硬解回放線程類,由 DirectOutputThread 派生,負責處理標識爲 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音頻流,這種音頻流未經軟件解碼的(一般是 MP3、AAC 等格式的數據),需要輸出到硬件解碼器,由硬件解碼器解碼成 PCM 數據

PlaybackThread 與輸出流設備的關係:PlaybackThread 實例與輸出流設備是一一對應的,比方說 OffloadThread 只會將音頻數據輸出到 compress_offload 設備中,MixerThread(with FastMixer) 只會將音頻數據輸出到 low_latency 設備中。

從 Audio HAL 中,我們通常看到如下 4 種輸出流設備,分別對應着不同的播放場景:

primary_out:主輸出流設備,用於鈴聲類聲音輸出,對應着標識爲 AUDIO_OUTPUT_FLAG_PRIMARY 的音頻流和一個 MixerThread 回放線程實例
low_latency:低延遲輸出流設備,用於按鍵音、遊戲背景音等對時延要求高的聲音輸出,對應着標識爲 AUDIO_OUTPUT_FLAG_FAST 的音頻流和一個 MixerThread 回放線程實例
deep_buffer:音樂音軌輸出流設備,用於音樂等對時延要求不高的聲音輸出,對應着標識爲 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音頻流和一個 MixerThread 回放線程實例
compress_offload:硬解輸出流設備,用於需要硬件解碼的數據輸出,對應着標識爲 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音頻流和一個 OffloadThread 回放線程實例

三、Audio Track 應用跟蹤流程
Audio Track應用界面
僞代碼:
/**

  • 採樣率(默認44100,每秒44100個點)
    /
    private int sampleRateInHz = 44100;
    /
    *
  • 聲道(默認單聲道)
    /
    private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    /
    *
  • 編碼率(默認ENCODING_PCM_16BIT)
    /
    private int encodingBitrate = AudioFormat.ENCODING_PCM_16BIT;
    private int recBufSize = 0;
    private int playBufSize = 0;
    recBufSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, encodingBitrate);
    playBufSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, encodingBitrate);
    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, encodingBitrate, recBufSize);
    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, encodingBitrate, playBufSize, AudioTrack.MODE_STREAM);
    blnInstantPlay = true;
    new ThreadInstantPlay().start();
    /
    *
  • 即時播放線程

*/
class ThreadInstantPlay extends Thread
{
@Override
public void run()
{
byte[] bsBuffer = new byte[recBufSize];
audioRecord.startRecording();
audioTrack.play();
while(blnInstantPlay)
{
int line = audioRecord.read(bsBuffer, 0, recBufSize);
byte[] tmpBuf = new byte[line];
System.arraycopy(bsBuffer, 0, tmpBuf, 0, line);
audioTrack.write(tmpBuf, 0, tmpBuf.length);
}
audioTrack.stop();
audioRecord.stop();
}
}
AudioTrack應用底層時序圖
四、疑問:
1.耳機、Speaker、藍牙音箱等,PlaybackThread只針對某個設備輸出嗎?
2.聲音輸出怎麼對接功放驅動的?
3.聲音分別調節的策略是怎麼樣的?
4.配置文件中module,device,flag,stream之間什麼關係?

五、參考資料:
https://blog.csdn.net/gjy_it/article/details/71330357
Android 音頻系統:從 AudioTrack 到 AudioFlinger

https://blog.csdn.net/axlecho/article/details/78510478
Android Framework學習筆記 – Audio的播放流程

https://blog.csdn.net/qidi_huang/article/details/72912323
Android音頻模塊啓動流程分析

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