Android音頻系統

1 分析思路

  1. Thread如何創建? AudioPolicyService是策略的制定者,AudioFlinger是策略的執行者, 所以: AudioPolicyService根據配置文件使喚AudioFlinger來創建Thread
  2. Thread對應output, output對應哪些設備節點?
  3. AudioTrack和Track的創建過程: AudioTrack對應哪一個Thread, 對應哪一個output?
  4. AudioTrack如何傳輸數據給Thread?
  5. AudioTrack如何播放、暫停、關閉?

2 以例子說明幾個重要概念

stream type, strategy, device, output, profile, module : policy

out flag : 比如對於某個專業APP, 它只從HDMI播放聲音, 這時就可以指定out flag爲AUDIO_OUTPUT_FLAG_DIRECT,這會導致最終的聲音無需混音即直接輸出到對應的device

Android系統裏使用hardware module來訪問硬件, 比如聲卡 聲卡上有喇叭、耳機等等,稱爲device 爲了便於管理, 把一個設備上具有相同參數的一組device稱爲output, 一個module能支持哪些output,一個output能支持哪些device,使用配置文件/system/etc/audio_policy.conf來描述

app要播放聲音, 要指定聲音類型: stream type有那麼多的類型, 來來來, 先看它屬於哪一類(策略): strategy

根據strategy確定要用什麼設備播放: device, 喇叭、耳機還是藍牙? 根據device確定output, 進而知道對應的playbackthread, 把聲音數據傳給這個thread

一個stream如何最終選擇到一個device,這些stream如何互相影響(一個高優先級的聲音會使得其他聲音靜音),等等等, 統稱爲policy (政策)

輸出、輸入設備: https://blog.csdn.net/zzqhost/article/details/7711935

3 所涉及文件形象講解

系統服務APP:

frameworks/av/media/mediaserver/main_mediaserver.cpp

AudioFlinger :

AudioFlinger.cpp (frameworks/av/services/audioflinger/AudioFlinger.cpp)

Threads.cpp (frameworks/av/services/audioflinger/Threads.cpp)

Tracks.cpp (frameworks/av/services/audioflinger/Tracks.cpp)

audio_hw_hal.cpp (hardware/libhardware_legacy/audio/Audio_hw_hal.cpp)

AudioHardware.cpp (device/friendly-arm/common/libaudio/AudioHardware.cpp)

AudioPolicyService:

AudioPolicyService.cpp (frameworks/av/services/audiopolicy/AudioPolicyService.cpp)

AudioPolicyClientImpl.cpp (frameworks/av/services/audiopolicy/AudioPolicyClientImpl.cpp)

AudioPolicyInterfaceImpl.cpp(frameworks/av/services/audiopolicy/AudioPolicyInterfaceImpl.cpp)

AudioPolicyManager.cpp (device/friendly-arm/common/libaudio/AudioPolicyManager.cpp)

AudioPolicyManager.h (device/friendly-arm/common/libaudio/AudioPolicyManager.h)

AudioPolicyManagerBase.cpp (hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp)

堪誤: 上面3個文件被以下文件替代 AudioPolicyManager.cpp (frameworks/av/services/audiopolicy/AudioPolicyManager.cpp)

應用程序APP所用文件:

AudioTrack.java (frameworks/base/media/java/android/media/AudioTrack.java)

android_media_AudioTrack.cpp (frameworks/base/core/jni/android_media_AudioTrack.cpp)

AudioTrack.cpp (frameworks/av/media/libmedia/AudioTrack.cpp)

AudioSystem.cpp (frameworks/av/media/libmedia/AudioSystem.cpp)

4 AudioPolicyService啓動過程分析

比如一部典型的手機,它既有聽筒、耳機接口,還有藍牙設備。假設默認情況下播放音樂是通過聽筒喇叭輸出的,那麼當用戶插入耳機時,這個策略就會改變——從耳機輸出,而不再是聽筒;又比如在機器插着耳機時,播放音樂不應該從喇叭輸出,但是當有來電鈴聲時,就需要同時從喇叭和耳機輸出音頻。這些“音頻策略”的制定,主導者就是AudioPolicyService

在AudioFlinger小節,我們反覆強調它只是策略的執行者,而AudioPolicyService則是策略的制定者。這種分離方式有效地降低了整個系統的藕合性,而且爲各個模塊獨立擴展功能提供了保障。

  1. 加載解析/vendor/etc/audio_policy.conf/system/etc/audio_policy.conf
    • 對於配置文件裏的每一個module項, new HwModule(name), 放入mHwModules數組;
    • 對於module裏的每一個output, new IOProfile,放入module的mOutputProfiles
    • 對於module裏的每一個input, new IOProfile, 放入module的mInputProfiles
  2. 根據module的name加載廠家提供的so文件(通過AudioFlinger來加載)
  3. 打開對應的output(通過AudioFlinger來open output)

爲了讓大家對AudioPolicyService有個感性的認識,我們以下圖來形象地表示它與AudioTrack及AudioFlinger間的關係:

5 AudioFlinger啓動過程分析

  1. 註冊AudioFlinger服務
  2. 被AudioPolicyService調用以打開廠家提供的so文件
    1. 加載哪個so文件? 文件名是什麼? 文件名從何而來? 名字從/system/etc/audio_policy.conf得到 : primary 所以so文件就是 : audio.primary.XXX.so, eg. audio.primary.tiny4412.so
    2. 該so文件由什麼源文件組成? 查看Android.mk

    audio.primary.$(TARGET_DEVICE) : device/friendly-arm/common/libaudio/AudioHardware.cpp libhardware_legacy libhardware_legacy : hardware/libhardware_legacy/audio/audio_hw_hal.cpp

  3. 對硬件的封裝:
AudioFlinger       : AudioHwDevice (放入mAudioHwDevs數組中)
audio_hw_hal.cpp   : audio_hw_device
廠家               : AudioHardware (派生自: AudioHardwareInterface)

AudioHwDevice是對audio_hw_device的封裝,audio_hw_device中函數的實現要通過AudioHardware類對象

6 AudioTrack創建過程概述

  1. 體驗測試程序: frameworks/base/media/tests/audiotests/shared_mem_test.cpp

frameworks/base/media/tests/mediaframeworktest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java

播放聲音時都要創建AudioTrack對象,java的AudioTrack對象創建時會導致c++的AudioTrack對象被創建;

所以分析的核心是c++的AudioTrack類,創建AudioTrack時涉及一個重要函數: set()函數

  1. 猜測創建過程的主要工作
    1. 使用AudioTrack的屬性, 根據AudioPolicy找到對應的output、playbackThread
    2. 在playbackThread中創建對應的track
    3. APP的AudioTrack 和 playbackThread的mTracks中的track之間建立共享內存
  2. 源碼時序圖

7 AudioTrack創建過程_Track和共享內存

回顧:

  1. APP創建AudioTrack AudioFlinger中PlaybackThread創建對應的Track

b. APP給AudioTrack提供音頻數據有2種方式: 一次性提供(MODE_STATIC)、邊播放邊提供(MODE_STREAM)

問:

  1. 音頻數據存在buffer中, 這個buffer由誰提供? APP 還是 PlaybackThread ?
  2. APP提供數據, PlaybackThread消耗數據, 如何同步?

8 音頻數據的傳遞

  1. APP創建AudioTrack, playbackThread創建對應的Track 它們之間通過共享內存傳遞音頻數據
  2. APP有2種使用共享內存的方式:
    1. MODE_STATIC: APP創建共享內存, APP一次性填充數據
    2. MODE_STREAM: APP使用obtainBuffer獲得空白內存, 填充數據後使用releaseBuffer釋放內存
  3. playbackThread使用obtainBuffer獲得含有數據的內存, 使用數據後使用releaseBuffer釋放內存
  4. AudioTrack中含有mProxy, 它被用來管理共享內存, 裏面含有obtainBuffer, releaseBuffer函數。Track中含有mServerProxy, 它被用來管理共享內存, 裏面含有obtainBuffer, releaseBuffer函數。對於不同的MODE, 這些Proxy指向不同的對象
  5. 對於MODE_STREAM, APP和playbackThread使用環型緩衝區的方式傳遞數據

9 PlaybackThread處理流程

  1. prepareTracks_l : 確定enabled track, disabled track 對於enabled track, 設置mState.tracks[x]中的參數
  2. threadLoop_mix : 處理數據(比如重採樣)、混音 確定hook: 逐個分析mState.tracks[x]的數據, 根據它的格式確定tracks[x].hook 再確定總的mState.hook 調用hook: 調用總的mState.hook即可, 它會再去調用每一個mState.tracks[x].hook 混音後的數據會放在mState.outputTemp臨時BUFFER中 然後轉換格式後存入 thread.mMixerBuffer
  3. memcpy_by_audio_format : 把數據從thread.mMixerBuffer或thread.mEffectBuffer複製到thread.mSinkBuffer
  4. threadLoop_write: 把thread.mSinkBuffer寫到聲卡上
  5. threadLoop_exit
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章