-
App
音頻應用軟體 -
Framework
MediaPlayer 和 MediaRecorder,以及 AudioTrack、AudioRecorder、AudioManager、AudioService 以及 AudioSystem。 -
Libraries
frameworks/av/media/libmedia、libaudioflinger、libmediaplayerservice -
HAL
AudioFlinger、AudioPolicyService。
-
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 來操作的。
附上一張重要的類圖:
Audio 服務的啟動#
- 創建 AudioFlinger 和 AudioPolicyService。
- 解析 Audio Config 文件(audio_policy_configuration.xml),獲取支持的音頻外設列表及各輸入輸出通路詳細參數。
- 根據解析得到的外設列表,加載所有的 Audio HAL 庫。
- 為所有 output 設備打開 outputStream 並創建 PlaybackThread 線程。
- 為所有 input 設備打開 inputStream 並創建 RecordThread 線程。
AudioTrack#
Android 聲音播放都是通過 AudioTrack 進行,包括 MediaPlayer 最終也是創建 AudioTrack 來播放的。通過 AudioTrack 播放聲音主要包括下面幾步:
- 創建 AudioTrack。
- 調用 AudioTrack 的 play () 方法。
- 調用 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.conf
和 vendor/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 模式下的音頻數據流向
- compressed offload 模式下的音頻數據流向
- 音頻錄製
- 打電話
配置解析#
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 的路由策略。
profile 參數包含音頻流一些信息,比如位數、采樣率、通道數,它將被構建為 AudioProfile 對象,保存到 mixPort,然後在存儲到 module。對於 xml 裡面的 devicePort,一般沒有 profile 參數,則會創建一個默認的 profile。當把 mixPort 加入到 Module 時,會進行分類:
即 source 角色保存到 OutputProfileCollection mOutputProfiles,sink 角色保存到 InputProfileCollection mInputProfiles。
而 devicePort 則調用 HwModule::setDeclaredDevices () 保存到 module 的 mDeclaredDevices。
HAL#
Audio HAL 大致的類圖,hal 采用工廠模式,分為 Local 和 HIDL 模式,最後都會調用到 audio_stream_out 或者 audio_stream_in 中,對應調用到 audio_hw.c(由各個廠商實現)中。
藍牙連接例子#
AudioService 的 handleDeviceConnection 調用 AudioPolicyManager 的 setDeviceConnectionStateInt。
checkOutputsForDevice 會檢測所有的 profile(output),查找每個 profile 是否都存在對應的線程,如果沒有則進行創建 checkOutputForAllStrategies 切換 AudioTrack 的寫入數據源。