banner
fwrite

fwrite

好好生活
twitter
github
email

Android 音頻系統

  • App
    音頻應用軟體

  • Framework
    MediaPlayer 和 MediaRecorder,以及 AudioTrack、AudioRecorder、AudioManager、AudioService 以及 AudioSystem。

  • Libraries
    frameworks/av/media/libmedia、libaudioflinger、libmediaplayerservice

  • HAL
    AudioFlinger、AudioPolicyService。

2022-06-29-21-57-07

  • AudioPolicyService:APS 是音頻框架的服務,main_audioserver 啟動,會創建 AudioCommandThread 和 AudioPolicyClient、AudioPolicyManager。它主要由 AudioSystem 通過 binder 調用,也可以由 AudioPolicyClient,AudioPolicyManager 直接調用。它的大部分操作都交給 AudioPolicyManager 來做

  • AudioPolicyClient:APC 是 AudioPolicyService 的內部類。它用於打開關閉輸入輸出,設置流音量,傳遞參數給 hal 層(如 audio_hw.cpp)等;它主要是通過 binder 跨進程調用 AudioFlinger 去完成真正的操作。可以由 AudioManager 通過 mpClientInterface 去調用它。

  • AudioPolicyManager:APM 是 AudioPolicyService 的主要工作類,AudioPolicyService 的大部分操作都由他來執行

  • AudioFlinger:AF 主要承擔音頻混合輸出,是 Audio 系統的核心,從 AudioTrack 來的數據最終都會在這裡處理,並被寫入到 Audio 的 HAL。

  • DevicesFactoryHalLocal:根據名字加載對應的 hal module。比如傳進去 a2dp 相關的名字,會加載到 audio.a2dp.default.so

  • DevicesFactoryHalHidl:跨進行加載 hidl hal module。

  • DevicesFactoryHalInterface:用於創建子類 DevicesFactoryHalHybrid。

  • DevicesFactoryHalHybrid:選擇創建 DevicesFactoryHalLocal 或者 DevicesFactoryHalHidl,我這裡只創建 DevicesFactoryHalLocal。

  • DeviceHalLocal:通過私有成員 audio_hw_device_t *mDev,直接調用 hal 代碼,用來設置和獲取底層參數,打開和關閉 stream。

  • StreamOutHalLocal:通過私有成員 audio_stream_out_t *mStream 直接調用 hal 代碼,用於操作流,比如 start、stop、flush、pause 操作;還有調用 write 函數寫音頻數據到 hal 層。

  • AudioStreamOutSink:它其實是 StreamOutHalLocal 的一個 wrapper,它也有 write 函數,不過是通過 StreamOutHalLocal 來操作的。

    附上一張重要的類圖:

2022-06-29-21-57-45

Audio 服務的啟動#

2022-06-29-21-58-13

  1. 創建 AudioFlinger 和 AudioPolicyService。
  2. 解析 Audio Config 文件(audio_policy_configuration.xml),獲取支持的音頻外設列表及各輸入輸出通路詳細參數。
  3. 根據解析得到的外設列表,加載所有的 Audio HAL 庫。
  4. 為所有 output 設備打開 outputStream 並創建 PlaybackThread 線程。
  5. 為所有 input 設備打開 inputStream 並創建 RecordThread 線程。

AudioTrack#

2022-06-29-21-58-33

Android 聲音播放都是通過 AudioTrack 進行,包括 MediaPlayer 最終也是創建 AudioTrack 來播放的。通過 AudioTrack 播放聲音主要包括下面幾步:

  1. 創建 AudioTrack。
  2. 調用 AudioTrack 的 play () 方法。
  3. 調用 AudioTrack 的 write () 方法寫入音頻數據。

創建 AudioTrack 時重點是通過 AudioPolicyManager 分配了音頻路由通路,同時通知服務端 AudioFlinger 創建對應的 Track,用於接收音頻數據。

  • 調用 play () 方法主要是將創建的 Track 加到 mActiveTracks 並激活沉睡的 PlaybackThread 線程。
  • 調用 write () 方法通過共享內存將數據寫入服務端 AudioFlinger,PlaybackThread 收到數據激活線程,將數據進行混音等處理再寫入對應的 Audio HAL,Audio HAL 再將數據寫入驅動或其它外設。

音頻策略#

音頻調試參考 Android Audio

首先要搞清楚 stream_type,device,strategy 三者之間的關係:

  • AudioSystem::stream_type:音頻流的類型
  • AudioSystem::audio_devices:音頻輸入輸出設備,每一個 bit 代表一種設備。
  • AudioPolicyManagerBase::routing_strategy:音頻路由策略

AudioPolicyManagerBase.getStrategy 根據 stream type,返回對應的 routing strategy 值,AudioPolicyManagerBase.getDeviceForStrategy () 則是根據 routing strategy,返回可用的 device。
首先需要的是加載音頻設備,音頻設備的配置在 system/etc/audio_policy.confvendor/etc/audio_policy,配置文件中表示了各種 audio interface,通過 AudioFlinger 加載音頻設備。
按照一定的優先級選擇符合要求的 Device,然後為 Device 選擇合適的 Output 通道。
可以通過重載 getStrategy 自己劃分 Strategy。

routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);

截取音頻數據#

有三個地方可以獲取到 PCM 數據:

  • 第一个地方 frameworks/av/media/libmedia/AudioTrack.cpp
nsecs_t AudioTrack::processAudioBuffer(){  
{
//在 releaseBuffer 之前 dump 
releaseBuffer(&audioBuffer);  
}  
  • 第二個地方 frameworks/av/services/audioflinger/Tracks.cpp
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
        AudioBufferProvider::Buffer* buffer)
{
    // add dump method
    ServerProxy::Buffer buf;
    size_t desiredFrames = buffer->frameCount;
    buf.mFrameCount = desiredFrames;
    status_t status = mServerProxy->obtainBuffer(&buf);
    buffer->frameCount = buf.mFrameCount;
    buffer->raw = buf.mRaw;
    if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused()) {
        ALOGV("underrun,  framesReady(%zu) < framesDesired(%zd), state: %d",
                buf.mFrameCount, desiredFrames, mState);
        mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
    } else {
        mAudioTrackServerProxy->tallyUnderrunFrames(0);
    }

    return status;
}
  • 第三個地方 hardware/xxx/audio/tinyalsa_hal/audio_hw.c
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
{  
	//add dump method	 
}  

音頻數據流向#

Android 系統 audio 框架中主要有三種播放模式:low latency playback、deep buffer playback 和 compressed offload playback。

  • low latency /deep buffer 模式下的音頻數據流向

2022-06-29-21-59-01

  • compressed offload 模式下的音頻數據流向

2022-06-29-21-59-35

  • 音頻錄製

2022-06-29-21-59-56

  • 打電話

2022-06-29-22-00-13

配置解析#

module 下面有 mixPorts、devicePorts 和 routes 子段,它們下面又分別包含多個 mixPort、devicePort 和 route 的字段,這些字段內標識為 source 和 sink 兩種角色:
devicePorts (source):為實際的硬件輸入設備;
devicePorts (sink):為實際的硬件輸出設備;
mixPorts (source):為經過 AudioFlinger 之後的流類型,也稱 “輸出流設備”,是個邏輯設備而非物理設備,對應 AudioFlinger 裡面的一个 PlayerThread;
mixPorts (sink):為進入 AudioFlinger 之前的流類型,也稱 “輸入流設備”,是個邏輯設備而非物理設備,對應 AudioFlinger 裡面的一个 RecordThread;
routes:定義 devicePort 和 mixPorts 的路由策略。

2022-06-29-22-00-30

profile 參數包含音頻流一些信息,比如位數、采樣率、通道數,它將被構建為 AudioProfile 對象,保存到 mixPort,然後在存儲到 module。對於 xml 裡面的 devicePort,一般沒有 profile 參數,則會創建一個默認的 profile。當把 mixPort 加入到 Module 時,會進行分類:

即 source 角色保存到 OutputProfileCollection mOutputProfiles,sink 角色保存到 InputProfileCollection mInputProfiles。
而 devicePort 則調用 HwModule::setDeclaredDevices () 保存到 module 的 mDeclaredDevices。

2022-06-29-22-00-49

HAL#

2022-06-29-22-01-05

Audio HAL 大致的類圖,hal 采用工廠模式,分為 Local 和 HIDL 模式,最後都會調用到 audio_stream_out 或者 audio_stream_in 中,對應調用到 audio_hw.c(由各個廠商實現)中。

藍牙連接例子#

2022-06-29-22-01-16

AudioService 的 handleDeviceConnection 調用 AudioPolicyManager 的 setDeviceConnectionStateInt。

checkOutputsForDevice 會檢測所有的 profile(output),查找每個 profile 是否都存在對應的線程,如果沒有則進行創建 checkOutputForAllStrategies 切換 AudioTrack 的寫入數據源。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。