音頻策略的構建
1、概述
2、AudiopolicyService
2.1 任務
2.2 啟動流程
2.2.1 加載audio_policy.conf(xml)配置文件
2.2.2 初始化各種音頻流對應的音量調節點
2.2.3 加載audio policy硬件抽象庫
2.2.4設置輸出設備
ps:audiopatch流程簡介
2.2.5打開輸出設備
2.3 策略制定總結
3、AudioFlinger
3.1 任務
3.2 啟動流程
3.3 打開output
4、總結
1、概述
本文講解音頻系統的“核心組成:音頻策略的制定者AudiopolicyService以及音頻策略的執行者AudioFlinger。其它的文章深度都不高,而且獨立性較強,分別講解audiofligner和audiopolicyservice服務能夠獨立完成的業務功能,本篇重點講解audioflinger和audiopolicyservice如何協同工作以及分工,共同保障音頻系統的正常工作。
其中 AudioPolicy 相當于軍師的角色,專門來制定 Audio 播放時的相關的策略及設定相關的參數,而 AudioFlinger 相當于將軍,會根據軍師的策略來執行。
上面的話只要是學過音頻相關知識的人都會說,都了解的。***那么什么是策略呢?什么是執行呢?策略和執行能放到一起嗎?策略和執行的隔離程度是什么級別的?***把這個問題想清楚,你的思維就比別人跟進一步。
***策略就是組織各種數據的關系,定義整體的結構,制定事務的流程。***而執行則是處理具體的事務,負責流程中某個具體的步驟,管理某個數據。
策略和執行從代碼的角度來看完全可以放在一起。但是根據我們開發的經驗可以知道,策略的修改頻率要遠高于執行。如果我們把策略和執行放到一個庫里面,那么每次修改策略都會對執行引入風險。所以我們要將策略和執行分開。這也是軟件設計思想,分離變化的一種體現。
策略和執行(policy和finger)的隔離程度是庫隔離,但是還是運行在一個進程。如果其中一個模塊發生崩潰還是會連累另外一個模塊。
隔離程度簡單劃分有三種類隔離,庫隔離,進程隔離。
2、AudiopolicyService
2.1 任務
管理輸入輸出設備,包括設備的斷連,設備的選擇和切換等(加載audio_policy.default.so庫得到audio_policy_module模塊)
管理系統的音頻策略,比如通話時播放音樂、或者播放音樂時來電的一系列處理(通過audio_policy_module模塊打開audio_policy_device設備)
管理系統的音量
上層的一些音頻參數也可以通過AudioPolicyService設置到底層去 (通過audio_policy_device
設備創建audio_policy)
2.2 啟動流程
AudioPolicyService服務運行在audioserver進程中, 隨著audioserver進程啟動而啟動。
//frameworks/av/media/audioserver/main_audioserver.cpp
int main(int argc __unused, char **argv)
......
sp<ProcessState> proc(ProcessState::self());sp<IServiceManager> sm = defaultServiceManager();ALOGI("ServiceManager: %p", sm.get());AudioFlinger::instantiate();//實例化AudioFlinger服務AudioPolicyService::instantiate();//實例化AudioPlicyService服務
其中,AudioPolicyService::instantiate()并不由AudioPolicyService實現,而是BinderService類的一個實現。包括AudioFlinger,AudioPolicyservice等在內的幾個服務都繼承自這個統一的Binder的服務類,具體實現在BinderService.h中。如果后續我們需要自己開發一個基于binder的服務那么也可以繼承binderservice來實現。
/frameworks/native/include/binder
static status_t publish(bool allowIsolated = false,int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {sp<IServiceManager> sm(defaultServiceManager());//SERVICE是文件中定義的一個模板,AudioPolicyService調用了instantiate()函數,//所以當前的SERVICE為AudioPolicyServicereturn sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,dumpFlags);
publish()函數獲取到ServiceManager的代理,然后new一個調用instantiate的service對象并把它添加到ServiceManager中。所以下一步就是去分析AudioPolicyService的構造函數。
// frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
AudioPolicyService::AudioPolicyService(): BnAudioPolicyService(),mAudioPolicyManager(NULL),mAudioPolicyClient(NULL),mPhoneState(AUDIO_MODE_INVALID),mCaptureStateNotifier(false) {
}
它的構造函數里面初始化了一些變量,那么AudioPolicyService所作的初始化的事情是在什么地方進行的呢,繼續分析上面的構造函數,AudioPolicyService是繼承自BnAudioPolicyService的,一步步往上推,最終發現它的祖先是RefBase,根據強指針的特性,目標對象在第一次被引用時會調用onFirstRef()的,我們就去看一下AudioPolicyService::onFirstRef()。不難發現,android代碼中的構造函數一般都用于初始化一些成員變量。大部分的工作都是在其它函數中實現。比如onfirstref。
void AudioPolicyService::onFirstRef()
{{Mutex::Autolock _l(mLock);// start audio commands threadmAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);//創建ApmAudio線程用于執行
audio命令// start output activity command threadmOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);//創建ApmOutpur線程用于執行
輸出命令mAudioPolicyClient = new AudioPolicyClient(this);//實例化AudioPolicyClient對象mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);//實例化AudioPolicyManager對象}
......// load audio processing modules//解析audio_effects.conf 文件,得到并加載系統支持的音效庫。初始化各個音效對應的參數,將各音效和對應的輸入和輸出
//流綁定在一起,這樣,當上層要使用音效時,就會在對應的threadloop中調用process_l音效處理函數。sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();//初始化音效相關
#ifdef SPRD_CUSTOM_AUDIO_POLICY{Mutex::Autolock _l(mLock);mAudioPolicyEffects = audioPolicyEffects;//}pthread_t uidpolicy;
這里audiopolicyservice為什么要起兩個commandthread?audioflinger和audiopoliyservice是在同一個進程。如果audiopolicymanager直接調用audioflinger的接口就會阻塞住。(也就是同步的)。而通過線程去調用audioflinger的話或者處理一些耗時的任務,audiopolicymanager就可以很快返回不需要阻塞住。我自己的看法如果沒了這兩個thread。audiopolicyservice都不能叫service了,直接和manager合二為一算了
AudioPolicyClient,AudioPolicyClient類定義在AudioPolicyService.h中
//frameworks/av/services/audiopolicy/service/AudioPolicyService.h
class AudioPolicyClient : public AudioPolicyClientInterface
{
public:explicit AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {}......
}
它的實現在frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp中。創建完AudioPolicyClient之后通過調用createAudioPolicyManager方法創建了一個AudioPolicyManager對象,下面看一下createAudioPolicyManager方法中是怎樣創建
AudioPolicyManager的。
//frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{return new AudioPolicyManager(clientInterface);//將會調用類AudioPolicyManager的構造函數,接下來將重點分析該構
造函數,這是我們分析AudioPolicyService的關鍵。
}
可見他是直接new了一個AudioPolicyManager然后把剛才創建的AudioPolicyClient傳了進去,使用AudioPolicyClientInterface對象來構造AudioPolicyManager對象,AudioPolicyManager繼承于AudioPolicyInterface
AudioPolicyManager
直接看其構造函數:最新的android版本應該是在onfirstref函數
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) :......
{//1.加載解析配置文件//配置文件路徑:/vendor/etc/ 或者 /system/etc/ConfigParsingUtils::loadConfig(....);//2.初始化各種音頻流對應的音量調節點checkAndSetVolume;setVolumeCurveInsdex//3.加載audio policy硬件抽象庫即根據名字加載mHwmodule對應的so文件,該文件由參加提供hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));//4.打開output輸出設備,同時會創建playbackthread,其會得到一個output整數(該函數的參數),這個整數會對應播放線程,
也可以對應SwAudioOutputDescriptor*/status_t status = mpClientInterface->openOutput(outProfile-
>getModuleHandle(),&output,&config,&outputDesc->mDevice,address,&outputDesc->mLatency,outputDesc->mFlags);//5.,根據output添加outputDesc描述符,保存輸出設備描述符對象addOutput(output, outputDesc);//6.設置輸出設備setOutputDevice(....);//里面還有一些根據屬性選擇output,getOutputForAttr等等,根據stratery選擇設備由enfine完
成//7.打開輸入設備mpClientInterface->openInput(....);//8.更新輸出設備updateDevicesAndOutputs();
}
通過上面的1到8步基本就把整個音頻處理系統的架子給搭起來!!!!接下來看看其中的這幾個重要的函數
2.2.1 加載配置文件
在AudioPolicyManager(的構造函數中,能找到loadConfig,這個函數負責解析xml配置文件,在這個文件中會找出所有的Interface(audio_hw_module關鍵字定義的模塊)比如primary、usb、a2dp、r_submix(WiDi用)。在每個interface下面會包含若干個Outputs,有的還有Inputs,同時在每個Outputs/Inputs下面又包含若干個Profile,每個Profile描述了該input/output支持sample rate,channel mask, devices &flags。
status_t AudioPolicyManager::loadConfig(const char *path)
status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
void AudioPolicyManager::loadHwModule(cnode *root)
......AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();//加載配置文件initialize();//根據配置文件做動作
}void AudioPolicyManager::loadConfig() {
#ifdef USE_XML_AUDIO_POLICY_CONF
if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
#else
if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR)
&& (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) {
#endif
ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();
}
}系統中的xml被讀取之后,以 mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices,mDefaultOutputDevice, static_cast<VolumeCurvesCollection*>(mVolumeCurves.get())的形式保存,后續需要和底層interface交互時,就可以通過這些成員獲取相關信息;
......//加載xml配置文件
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];//其中#define AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH 128//#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"//#define AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME \
在AudioPolicyManager創建過程中會通過加載(AudioPolicyManager的構造函數)audio_policy_configuration.xml配置文件來加載音頻設備,Android為每種音頻接口定義了對應的硬件抽象層。每種音頻接口定義了不同的輸入輸出,一個接口可以具有多個輸入或者輸出,每個輸入輸出可以支持不同的設備,通過讀取audio_policy_configuration.xml文件可以獲取系統支持的音頻接口參數,在AudioPolicyManager中會優先加載/vendor/etc/audio_policy_configuration.xml配置文件, 如果該配置文件不存在, 則加載/system/etc/audio_policy_configuration.xml配置文件,統中的audio_policy_configuration.xml被讀取之后,以profile的形式保存,后續打開/關閉設備的時候,就通過調用profile獲取所有的配置參數;當AudioPolicyManager構造時,它會根據用戶提供的audio_policy_configuration.xml來分析系統中有哪些audio接口(primary,a2dp以及usb),然后通過AudioFlinger::loadHwModule加載各audio接口對應的庫文件,并依次打開其中的output(openOutput)和input(openInput)打開音頻輸出時創建一個audio_stream_out通道,并創建AudioStreamOut對象以及新建PlaybackThread播放線程
打開音頻輸入時創建一個audio_stream_in通道,并創建AudioStreamIn對象以及創建RecordThread錄音線程。
2.2.2 初始化各種音頻流對應的音量調節點
在AudioPolicyManager.cpp的構造函數中會執行兩個方法
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();initialize();
}
其中loadConfig方法便會去解析配置文件audio_policy_configuration.xml
void AudioPolicyManager::loadConfig() {if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("could not load audio policy configuration file, setting defaults");getConfig().setDefault();}
}
調用deserializeAudioPolicyXmlConfig(getConfig()) 方法去解析配置文件時,使用
getConfig()方法傳入一個參數,這個參數的類型是AudioPolicyConfig
在AudioPolicyConfig.h中:
class AudioPolicyConfig
{
public:AudioPolicyConfig(......: ......mVolumeCurves(volumes),//音頻曲線......
然后再來看一下deserializeAudioPolicyXmlConfig()的實現
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];std::vector<const char*> fileNames;status_t ret;
......fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);for (const char* fileName : fileNames) {for (const auto& path : audio_get_configuration_paths()) {......return ret;
}
這段代碼做了一個循環,解析了”/odm/etc”, “/vendor/etc”, “/system/etc”這三個路徑和
AUDIO_POLICY_XML_CONFIG_FILE_NAME拼接的文件,而
AUDIO_POLICY_XML_CONFIG_FILE_NAME就是audio_policy_configuration.xml
#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"
通過include的方式包含了兩個xml文件
//audio_policy_configuration.xml<xi:include href="audio_policy_volumes.xml"/>//規定了音頻流、輸出設備和音量曲線的關系<xi:include href="default_volume_tables.xml"/>//規定了具體音頻曲線的值
因為不同的音頻流使用不同的音頻曲線,而同一音頻流在輸出設備不同時也采用不同的音頻曲線,
所以必須規定這三者的對應關系,xml中的這種對應關系被serializer.deserialize方法解析后,在代
碼中體現為VolumeCurvesCollection-VolumeCurvesForStream-VolumeCurve的對應關系
audio_policy_volumes.xml:這個配置文件描述f(stream,device )= volume-curve的映射關系。
<volume stream="音頻類型" deviceCategory="輸出設備"ref="音頻曲線"/><volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"ref="DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE"/>
default_volume_tables.xml:這個文件描述不同類型的音量曲線。
......
<reference name="DEFAULT_MEDIA_VOLUME_CURVE">//DEFAULT_MEDIA_VOLUME_CURVE曲線上點的xy值<!-- Default Media reference Volume Curve --><point>1,-5800</point><point>20,-4000</point><point>60,-1700</point><point>100,0</point>
</reference>
......
所以如果想要修改音頻曲線,只要修改default_volume_tables.xml文件就行了。想要對(stream,device)使用不同的音頻曲線就修改audio_policy_volumes.xml。
AudioPolicymanger調節音量的接口,setStreamVolumeIndex,設置特定流特定設備的音量其中參數device是 Stream->Strategy->Device的方式獲得, 這也是AudioPolicy獲得設備的標準方式。在分析audiopolicymanager如何實現調節stream的音量時我們先進行一些思考。首先我們知道android的音量有兩種描述方式index和DB。這里我們的入參是index。我們首先要完成的一個工作是將index轉化為DB。其次,音量調節需要作用到具體的數據流中。而audioflinger是負責具體音頻數據流的處理工作!那么我們就需要把音量值(DB)設置給audioflinger。audioflinger當中具體進行數據處理的類是threads。從設計的角度看,audiopolicymanager是不需要知道threads的信息的。但是audiopolicymanager又需要將具體的音量值通知給threads。這怎么辦呢?從上面的內容我們可以知道audiopolicymanager在初始化的時候會打開所有的ouput。而audioflinger內部通過map保存了output和thread之間的關系。那么audiopolicymanager在設置音量的時候只需要知道所有支持入參的stream和device的output,并將音量設置給該toutput就行。事實上,setStreamVolumeIndex也真是按照這個思路實現的。
//AudioPolicyManager.cpp
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,int index, audio_devices_t device)// 1. 判斷傳入的參數// 音量不可大于流的最大音量,小于最小音量值if ((index < mVolumeCurves->getVolumeIndexMin(stream)) ||(index > mVolumeCurves->getVolumeIndexMax(stream)))return BAD_VALUE;...// 設備需要是輸出設備if (!audio_is_output_device(device))
return BAD_VALUE;...// 如果傳入的流不能被Mute, 強制使用該流的最高音量// canBeMuted,現在代碼中,沒有設置canBeMuted的接口,默認被設置為trueif (!volumeCurves.canBeMuted()) index = volumeCurves.getVolumeIndexMax();...// 2. 更新與傳入的流,設備的音量值for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {// return (stream1 == stream2)if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {continue;}// 更新特定流,特定設備的音量值VolumeCurves->addCurrentVolumeIndex( device, index);}...// 3. 遍歷已經打開的所有的output,對所有的符合條件的output和流設置音量status_t status = NO_ERROR;for (size_t i = 0; i < mOutputs.size(); i++) {// 獲得該output的配置信息sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);// curSrcDevice 是根據當前output使用的設備得出的。output和設備的關系是通過route確定的。// 其中主要對雙設備做了處理,一般雙設備只選取了speakeraudio_devices_t curSrcDevice = Volume::getDeviceForVolume(desc->device());//更新for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {...// (1) 請求的流必須在當前output中Active(可以理解為正在播放)// 遍歷所有的流,僅對跟請求的流符合的流(當前的代碼下可以認為自有請求的流)if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {continue;}// 判斷流是不是Activeif (!(desc->isStreamActive((audio_stream_type_t)curStream) ||(isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {continue;}// (2) 判斷請求的設備是否跟當前獲得的設備匹配// 獲得請求的流在當前場景下應該使用的設備routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(curStrategy, false /*fromCache*/));// 請求的設備跟curStreamDevice是否有相同的設備, 是否是默認設備// 如果兩個條件都不符合,不會調整當前流的音量if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&((curStreamDevice & device) == 0)) {continue;}... bool applyVolume;// (3) OutPut的當前設備是否與請求的設備或者請求的設備的子設備相同if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {curStreamDevice |= device;applyVolume = (curDevice & curStreamDevice) != 0;} else {// (4) 如果請求的設備是默認設備,需要curStreamDevice沒有音量配置applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(stream, curStreamDevice);}if (applyVolume) {// 調用checkAndSetVolume應用該音量值status_t volStatus =checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,(stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
我認為上面代碼的邏輯是這樣的:我們要找到output。但是output可能支持不同的streamtype。這點我們從配置文件可以分析得出。因為output的約束主要是flag,采樣率,音頻格式,位寬等,而不涉及sreamtype。所以第一步判斷output上我們希望修改的streamtype是否是活躍的。其次看該output上輸出的設備是否就是我們期望的設備。這里是不是為了避免:outputA上stream是活躍的,ouputB上stream也是活躍的。但是device在outputA上支持,在outputB上不支持。思考一下這種情況可不可能出現。首先stream通過做策略一定要在devcice上播出,而outputB不支持device,stream也就不應該在outputB上活躍。上面的代碼是否可以優化一下!
最后調用checkAndSetVolume,( Audiopoicy真正通知Audofinger調節音量的接口是checkAndSetVolume)。
此軟件音量曲線的加載就完成了,調節音量時,會根據傳入的stream參數先找到getVolumeCurvesForStreamType對象,再根據傳入的device參數找到具體的VolumeCurve,最后根據index參數及音量曲線計算出音量的分貝值。(上層設置到AudioPolicy的音量是用戶設置的
音量值, 而底層把音量值轉換成分貝值才能處理該音量。音量曲線的作用就是做這種轉換)在后面就是checkAndSetVolume調用AudioFlinger的setstreamvolume去執行Playbackthread的setstreamvolume操作了。
2.2.3 加載audio policy硬件抽象庫
AudioPolicyManager加載完配置文件后,就知道了系統支持的所有音頻接口參數,可以為選擇音頻輸出的依據。audio_policy_configuration.xml同時定義了多個audio接口,每一個audio接口包含若干output和input,而每個output和input又同時支持多種輸入輸出模式,每種輸入輸出模式又支持若干種設備.最終會去調用到AudioFlinger去加載生成的動態庫so
//frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name)
{sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();if (af == 0) {ALOGW("%s: could not get AudioFlinger", __func__);return AUDIO_MODULE_HANDLE_NONE;}return af->loadHwModule(name);//這里直接調用了AudioFlinger::loadHwModule()。
}
我們進到AudioFlinger看看做了啥
audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
{if (name == NULL) {return AUDIO_MODULE_HANDLE_NONE;}if (!settingsAllowed()) {return AUDIO_MODULE_HANDLE_NONE;}Mutex::Autolock _l(mLock);return loadHwModule_l(name);
}
......audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{//1.是否已經加載過這個interfacefor (size_t i = 0; i < mAudioHwDevs.size(); i++) {if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {ALOGW("loadHwModule() module %s already loaded", name);return mAudioHwDevs.keyAt(i);}}//2.加載audio interfaceint rc = mDevicesFactoryHal->openDevice(name, &dev);//3.初始化rc = dev->initCheck();//4.添加到全局變量中audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
}
loadHwModule_l是通過調用openDevice方法來打開加載audio設備的,該方法的實現類是
DevicesFactoryHalHybrid
//frameworks/av/media/libaudiohal/DevicesFactoryHalHybrid.cpp
status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) {return mHidlFactory->openDevice(name, device); //Hidl方式加載}return mLocalFactory->openDevice(name, device); //舊版本本地加載
}
到DevicesFactoryHalHidl::openDevice看
//DevicesFactoryHalHidl.cpp
status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {if (mDevicesFactory == 0) return NO_INIT;IDevicesFactory::Device hidlDevice;status_t status = nameFromHal(name, &hidlDevice);if (status != OK) return status;Result retval = Result::NOT_INITIALIZED;Return<void> ret = mDevicesFactory->openDevice(
DevicesFactory->openDevice:
/DevicesFactory.cpp
Return<void> DevicesFactory::openDevice(IDevicesFactory::Device device, openDevice_cb _hidl_cb) {int halStatus = loadAudioInterface(moduleName, &halDevice);//加載audio interface......_hidl_cb(retval, result);//將加載的設備通過回調匿名方法傳遞回去return Void();
}
int DevicesFactory::loadAudioInterface(const char *if_name, audio_hw_device_t **dev)
{const hw_module_t *mod;int rc;//在system/lib/hw/等目錄下查找對應的動態庫并加載rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);//打開對應的device,并獲取hw_device_t指針類型的設備對象rc = audio_hw_device_open(mod, dev);......
}
loadHwModlule_l. 加載指定的audiointerface,比如“primary”、“a2dp”或者“usb”。函數load_audio_interface用來加載設備所需的庫文件,然后打開設備并創建一個audio_hw_device_t實例。音頻接口設備所對應的庫文件名稱是有一定格式的,比如a2dp的模塊名可能是audio.a2dp.so或者audio.a2dp.default.so等等。每種音頻設備接口由一個對應的so庫提供支持。那么AudioFlinger怎么會知道當前設備中支持上述的哪些接口,每種接口又支持哪些具體的音頻設備呢?這是AudioPolicyService的責任之一,即根據用戶配置來指導AudioFlinger加載設備接口。當AudioPolicyManager構造時,它會讀取廠商關于音頻設備的描述文件,然后據此來打開音頻接口(如果存在的話)。這一過程最終會調用loadHwModule(AudioFlinger)。完成了audiointerface的模塊加載只是萬里長征的第一步。因為每一個interface包含的設備通常不止一個,Android系統目前支持的音頻設備如下列表所示
大家可能會有疑問:
這么多的輸出設備,那么當我們回放音頻流(錄音也是類似的情況)時,該選擇哪一種呢?
而且當前系統中audio interface也很可能不止一個,應該如何選擇?
顯然這些決策工作將由AudioPolicyService來完成,我們會在下一小節做詳細闡述。這里先給大家
分析下,AudioFlinger是如何打開一個Output通道的(一個audiointerface可能包含若干個
output)。
2.2.4設置輸出設備
App構造audiotrack時制定了stream type,然后根據stream type來設置屬性Attributes,然后
audiomanager根據屬性來選擇strategy,從再根據strategy類別來獲得從哪個設備播放,AudioStream在
Audio Base.h中有定義,包含以下內容:((同時,在AudioSystem.java定義的流類型與audiobase.h中定義的audio_stream_type_t結構體一一對應,所以在JNI中經常可以將其類型強制轉換))
// /system/media/audio/include/system/audio-base.h
typedef enum {AUDIO_STREAM_DEFAULT = -1, // (-1)AUDIO_STREAM_MIN = 0,AUDIO_STREAM_VOICE_CALL = 0,AUDIO_STREAM_SYSTEM = 1,AUDIO_STREAM_RING = 2,AUDIO_STREAM_MUSIC = 3,AUDIO_STREAM_ALARM = 4,AUDIO_STREAM_NOTIFICATION = 5,AUDIO_STREAM_BLUETOOTH_SCO = 6,AUDIO_STREAM_ENFORCED_AUDIBLE = 7,AUDIO_STREAM_DTMF = 8,AUDIO_STREAM_TTS = 9,AUDIO_STREAM_ACCESSIBILITY = 10,AUDIO_STREAM_ASSISTANT = 11,
#ifndef AUDIO_NO_SYSTEM_DECLARATIONS/** For dynamic policy output mixes. Only used by the audio policy */AUDIO_STREAM_REROUTING = 12,/** For audio flinger tracks volume. Only used by the audioflinger */AUDIO_STREAM_PATCH = 13,/** stream for corresponding to AUDIO_USAGE_CALL_ASSISTANT */AUDIO_STREAM_CALL_ASSISTANT = 14,
#endif // AUDIO_NO_SYSTEM_DECLARATIONS
} audio_stream_type_t;
在現在的大多數fw架構中,AudioStream僅用來標識音頻的音量,使用音頻屬性
AudioAttributes(屬性)和AudioStream共同決定AudioStrategy(策略),因為AudioAttributes可以
攜帶比音頻流更多的信息,如:Content、Usage、flag等,
//services/audiopolicy/enginedefault/src/Engine.h
enum legacy_strategy {STRATEGY_NONE = -1,STRATEGY_MEDIA,STRATEGY_PHONE,STRATEGY_SONIFICATION,STRATEGY_SONIFICATION_RESPECTFUL,STRATEGY_DTMF,STRATEGY_ENFORCED_AUDIBLE,STRATEGY_TRANSMITTED_THROUGH_SPEAKER,STRATEGY_ACCESSIBILITY,STRATEGY_REROUTING,STRATEGY_CALL_ASSISTANT,
};
根據attributes獲取strategy
getStrategyForAttr
然后根據strategy來選擇設備
//frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
DeviceVector Engine::getDevicesForStrategyInt
最后根據設備來獲取output
//frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
audio_io_handle_t AudioPolicyManager::getOutputForDevices(......
比如說,下面的例子是播放音樂(AUDIO_STREAM_MUSIC)的時候選中的Strategy:
//frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp
case STRATEGY_REROUTING:
case STRATEGY_MEDIA: {if (strategy != STRATEGY_SONIFICATION)......if (isInCall() && (strategy == STRATEGY_MEDIA)) {//是否在電話中且strage屬于媒體類if ((devices2.isEmpty()) &&(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) {devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID);}if ((devices2.isEmpty()) &&(getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) {devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER);}
......//都是一些優先級的判斷
根據上面的代碼,簡單做個總結吧:
播放音樂選設備優先級如下
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP(藍牙高保真設備)
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES(普通藍牙耳機)
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER(藍牙小音箱)
//此處屬于setForceUse的強制插隊,音頻焦點,特殊情況如何處理,比如來電后,音樂聲音要變小
(if FORCE_SPEAKER)AUDIO_DEVICE_OUT_SPEAKER(揚聲器)
AUDIO_DEVICE_OUT_WIRED_HEADPHONE(普通耳機,只能聽,不能操控播放)
AUDIO_DEVICE_OUT_LINE
AUDIO_DEVICE_OUT_WIRED_HEADSET(線控耳機)
AUDIO_DEVICE_OUT_USB_HEADSET(USB耳機)
…
AUDIO_DEVICE_OUT_SPEAKER(揚聲器)
選好設備就返回device,然后getOutputForDevice
AudioPolicyManager::getOutputForDevice//處理入參flags
if ((flags & AUDIO_OUTPUT_FLAG_XXX) != 0) {flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_XXX);
}
//咱一般不是AUDIO_OUTPUT_FLAG_DIRECT,當然是
goto non_direct_output;
......
//播放音樂什么之類的,mediaplayer已經做完decodec.這里一般都是pcmif (audio_is_linear_pcm(format)) {//根據指定的stream類型獲取匹配的output.實際的路由改變需要等到startOutput被調用的時候//注意這個函數是getOutputsForDevice,要獲取的是一些output,而我們當前討論的函數是獲取一個output.這些outputs從
mOutputs中來.那么mOutputs來自哪里?SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);// 從匹配到的outputs(請注意,是復數)中選出一個outputoutput = selectOutput(outputs, flags, format);}
我們分析ouput的創建和使用過程,不難發現outputs和線程池有很大的相似之處。此外每個ouput都有一個thread。這就是典型的享元設計模式。
那么mOutputs來自哪里?
void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
{outputDesc->setIoHandle(output);mOutputs.add(output, outputDesc);updateMono(output); // update mono status when adding to output listselectOutputForMusicEffects();nextAudioPortGeneration();
}
從mOutputs中選出和匹配的一些output之后,用selectOutput選中我們真正需要的那一個.
AudioPolicyManager::selectOutput// select one output among several that provide a path to a particular device or set of// devices (the list was previously build by getOutputsForDevice()).// The priority is as follows:// 1: the output with the highest number of requested policy flags// 2: the output with the bit depth the closest to the requested one// 3: the primary output// 4: the first output in the list
......
//在幾個提供一個特定設備或一組路徑的路徑中選擇一個輸出//設備(該列表以前由getOutputsForDevice()構建)。//優先級如下:// 1:請求的policy flags數量最多的輸出// 2:bit depth最接近請求的輸出// 3:主輸出// 4:列表中的第一個輸出
然后返回選中的output即可.
整個getOutputForAttr就完成了.(attr>>strategy>>device>>output)
setoutputdevices這個函數里還創建patch,也就是建立起output和device之間的關系。
AudioPolicyManager::setOutputDevice //Duplicated output,output1和output2各來一遍setOutputDeviceif (outputDesc->isDuplicated()) {muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);return muteWaitMs;}...if (device == AUDIO_DEVICE_NONE) {resetOutputDevice(outputDesc, delayMs, NULL);} else {DeviceVector deviceList;if ((address == NULL) || (strlen(address) == 0)) {//mAvailableOutputDevices在APM構造的時候就已經準備好了//setDeviceConnectionStateInt中也會對新設備做adddeviceList = mAvailableOutputDevices.getDevicesFromType(device);} else {deviceList = mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address));}if (!deviceList.isEmpty()) {struct audio_patch patch;outputDesc->toAudioPortConfig(&patch.sources[0]);patch.num_sources = 1;patch.num_sinks = 0;for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);patch.num_sinks++;}//從mAudioPatches中取出patch的indexssize_t index;if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {index = mAudioPatches.indexOfKey(*patchHandle);} else {index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());}//每個output有自己的patchhandle。maudiopatch是整個系統所有的audiopatch。//處理afPatchHandlesp< AudioPatch> patchDesc;audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;if (index >= 0) {patchDesc = mAudioPatches.valueAt(index);afPatchHandle = patchDesc->mAfPatchHandle;}
關于Audio Patch(就是一個有source和sink的一個結構體)的分析,比較復雜,我們不做贅述了,有興趣的可以去網上查查資料,它最終會調用AudioFlinger::PlaybackThread::createAudioPatch_l
status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,audio_patch_handle_t *handle)
{...if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();status = hwDevice->create_audio_patch(hwDevice,patch->num_sources,
patch->sources,
patch->num_sinks,
patch->sinks,
handle);} else {char *address;if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {//FIXME: we only support address on first sink with HAL version < 3.0address = audio_device_address_to_parameter(patch->sinks[0].ext.device.type,patch->sinks[0].ext.device.address);} else {address = (char *)calloc(1, 1);}AudioParameter param = AudioParameter(String8(address));free(address);param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);status = mOutput->stream->common.set_parameters(&mOutput->stream->common,param.toString().string());*handle = AUDIO_PATCH_HANDLE_NONE;}if (configChanged) {mPrevOutDevice = type;sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);}...
}
如果supportsAudioPatches.那么就繼續createAudioPatch.分別會經過 libaudiohal底下
DeviceHalHidl::createAudioPatch和hardware底下的Device.cpp.然后進入
底下的audio_hw.c中實現的create_audio_patch函數.
如果hal版本過低,不支持audiopatch,AudioPolicy下發的AudioPatch會在AudioFlinger中轉
化為set_parameters向Hal下發
out_set_parameter或in_set_parameter完成設備下發
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{struct stream_out *out = (struct stream_out *)stream;struct audio_device *adev = out->dev;struct str_parms *parms;char value[32];int ret = 0, val = 0, err;ALOGE("%s: enter: usecase(%d: %s) kvpairs: %s",__func__, out->usecase, use_case_table[out->usecase], kvpairs);...
}
關于參數說明:
out->usecase:來自于audiofinger的openOutputStream方法,調用路徑請查看:<音頻輸出設備是
如何決定的>章節,需要注意的是每條音頻路徑的openOutputStream只會在APS初始化的時候調用一
次,之后音頻播放的時候不會再次調用,它會傳遞到Audio_hw.c的adev_open_output_stream。
adev_open_output_stream
} else if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
...
out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;
上Hal會根據傳入設備的類型來決定usecase的值。
關于第二個參數
kvpairs來自于上文提到的createAudioPatch_l,注意下面這個字段:
param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
在APS初始化的時候,會將系統可用音頻路徑的PlaybackThread創建好(offload的除外),在這個過程中也會調用可用音頻路徑的默認輸出設備(一般是Speaker,Voice的是聽筒)的setOutputDevice,從而為每條可用音頻路徑設置默認的路由。因此,APS初始化后,會打出如Log:
01-27 05:26:07.504 4588 4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.506 4588 4595 E AudioFlinger: Jon,address =
01-27 05:26:07.506 4588 4595 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(1: low-latencyplayback) kvpairs: routing=2
01-27 05:26:07.518 4588 4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.519 4588 4597 E AudioFlinger: Jon,address =
01-27 05:26:07.520 4588 4597 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(12: audio-ullplayback) kvpairs: routing=2
01-27 05:26:07.524 4588 4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.525 4588 4598 E AudioFlinger: Jon,address =
01-27 05:26:07.525 4588 4598 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(0: deep-bufferplayback) kvpairs: routing=2
01-27 05:26:07.530 4588 4588 E APM_AudioPolicyManager: Jon,deviceList size = 1
01-27 05:26:07.531 4588 4600 E AudioFlinger: Jon,address =
01-27 05:26:07.531 4588 4600 E audio_hw_primary: out_set_parameters: Jon, enter: usecase(38: afe-proxyplayback) kvpairs: routing=65536
但是由于初始化階段,所有的stream并沒有被active,因此select_devices沒有機會在這個時候
被調度到
既然如此,那么問題來了,Hal是在什么時候為Stream真正設置輸出的呢,答案是在track->start
后真正向Hal下發數據的時候,如下:
//audio_hw.c
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,size_t bytes)
{...if (out->standby) {out->standby = false;pthread_mutex_lock(&adev->lock);if (out->usecase == USECASE_COMPRESS_VOIP_CALL)ret = voice_extn_compress_voip_start_output_stream(out);elseret = start_output_stream(out);pthread_mutex_unlock(&adev->lock);/* ToDo: If use case is compress offload should return 0 */if (ret != 0) {out->standby = true;goto exit;}if (last_known_cal_step != -1) {ALOGD("%s: retry previous failed cal level set", __func__);audio_hw_send_gain_dep_calibration(last_known_cal_step);}}...
}
答案就在start_output_stream中,而且很明顯在單次音頻的播放中,start_output_stream只會被
調用一次。
int start_output_stream(struct stream_out *out)
{...//Hal選擇音頻的輸出設備select_devices(adev, out->usecase);...pcm_openpcm_preparepcm_start...
}
在后面就是hal與alsa的交互了,我們這里不做贅述
int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{...//獲取在start_output_stream中設置的usecaseusecase = get_usecase_from_list(adev, uc_id);...//禁止當前聲卡設備if (usecase->out_snd_device != SND_DEVICE_NONE) {disable_audio_route(adev, usecase);disable_snd_device(adev, usecase->out_snd_device);}//使能新的聲卡設備if (out_snd_device != SND_DEVICE_NONE) {//檢查是否有其他的usecase需要切換路由check_usecases_codec_backend(adev, usecase, out_snd_device);enable_snd_device(adev, out_snd_device);}//這里的enable_snd_device的作用可以理解為通過tinymix進行下面的操作:
//tinymix 'RX3 MIX1 INP1' 'RX1'
//tinymix 'SPK' ‘Switch’
//也就是將BE DAIs ----> device之間的硬件通路打開...//使能新的音頻路由enable_audio_route(adev, usecase);...
}//這里的enable_audio_route的作用可以理解為通過tinymix進行下面的操作:
//tinymix 'PRI_MI2S_RX Audio Mixer MultiMedia1' 1
//也就是將FE PCMs---->BE DAIs之間的硬件通路打開
//
audiopatch流程簡介
getNewOutputDevice:檢查AudioPatch,如果沒有,就用checkStrategyRoute(參數
一:getStrategy(stream),參數二:output)
然后不管兩種用哪一個都會AudioPolicyManager::setOutputDevice (從mAudioPatches中取出
patch的index, 處理afPatchHandle)
經過一堆調用流程之后,代碼會走到這里
status_t AudioFlinger::PlaybackThread::createAudioPatch_l
通過Device.cpp進入hal的audio_hw.c中實現的static int adev_create_audio_patch函數
//會打fw傳下來的log:LOG_I("in_device:%x in_device_name:%s",sources_device_type,devicetostring(sources_device_type));LOG_I("out_device:%x out_device_name:%s",sinks_device_type,devicetostring(sinks_device_type));LOG_I("sinks type:%x sources type:%x",sinks[0].type,sources[0].type);
select_devices會拿set_audio_patch的設備。
2.2.5打開輸出設備
取得output之后,需要應用它.
首先
//AudioTrack::createTrack_l()
...
AudioSystem::getLatency(output, &mAfLatency);
...
AudioSystem::getFrameCount(output, &mAfFrameCount);
...
AudioSystem::getFrameCountHAL(output, &afFrameCountHAL);
...
AudioSystem::getSamplingRate(output, &mAfSampleRate);
注意上方調用的參數,第一個入參,第二個是出參(指針)!
以上過程獲得了mAfLatency,mAfFrameCount,afFrameCountHAL,mAfSampleRate四個值.注意,全
是
af打頭的,表示AudioFlinger端對output的設置.做一些修正調整,變成
mSampleRate,temp(mAfFrameCount和mAfFrameCount,mAfLatency三個參數綜合計算).
然后
sp<IAudioTrack> track = audioFlinger->createTrack(streamType,mSampleRate,
mFormat,
mChannelMask,
&temp,
&flags,
mSharedBuffer,
output,
mClientPid,
tid,
&mSessionId,
mClientUid,
&status,
mPortId);
我們跳到AudioFlinger端去看看
//AudioFlinger::createTrack
...
PlaybackThread *thread = checkPlaybackThread_l(output);
...
首先checkPlaybackThread_l從mPlaybackThreads中檢索出output對應的回放線程.
我們之前說過,在AudioPolicyManager構造的時候,會根據audio_policy.conf中的配置,挨個調用
mpClientInterface->openOutput.(最終調用AudioFlinger::openOutput_l)
//frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
status_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module,audio_io_handle_t *output,audio_config_t *config,audio_devices_t *devices,const String8& address,uint32_t *latencyMs,audio_output_flags_t flags)
{sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();if (af == 0) {ALOGW("%s: could not get AudioFlinger", __func__);return PERMISSION_DENIED;}return af->openOutput(module, output, config, devices, address, latencyMs, flags);//這里直接調用了
AudioFlinger::openOutput()。
}
AudioFlinger打開output
//frameworks/av/services/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::openOutput()
{......sp<ThreadBase> thread = openOutput_l(module, output, config, *devices, address, flags);......
}
......
sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(......)//最終調用AudioFlinger::openOutput_
{// 查找相應的audio interface,查找合適的設備并打開AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices);......AudioStreamOut *outputStream = NULL;//在硬件設備上打開一個輸出流status_t status = outHwDev->openOutputStream(&outputStream,*output,devices,flags,config,address.string());
......
//創建PlaybackThread*
sp<PlaybackThread> thread;if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {//AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD音頻流,創建OffloadThread實例thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);ALOGV("openOutput_l() created offload output: ID %d thread %p",*output, thread.get());} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)|| !isValidPcmSinkFormat(config->format)|| !isValidPcmSinkChannelMask(config->channel_mask)) {//若是AUDIO_OUTPUT_FLAG_DIRECT音頻,則創建DirectOutputThread實例 thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);ALOGV("openOutput_l() created direct output: ID %d thread %p",*output, thread.get());} else {//其他音頻流,則創建MixerThread實例thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);//關聯output和PlaybackThread,添加播放線程mPlaybackThreads.add(*output, thread);
......
//Primary output情況下的處理:如果當前設備是主設備,則還需要進行相應的設置,包括模式、主音量等等
if ((mPrimaryHardwareDev== NULL) &&flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {undefinedALOGI("Usingmodule %d has the primary audio interface", module);mPrimaryHardwareDev = outHwDev;AutoMutexlock(mHardwareLock);mHardwareStatus =AUDIO_HW_SET_MODE;outHwDev->set_mode(outHwDev, mMode);//模式
......mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; //測試設備是否支持主音量獲取
Android系統是怎么從AudioTrack的start開始之后,一路走到AudioFlinger的Track::start的,我們略
過不提了,畢竟今天的主題是策略.我們知道,AudioFlinger::Track::start中,調用
PlaybackThread::addTrack_l,使沉睡的PlaybackThread被喚醒,然后就調用到了我們重要的
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{status = AudioSystem::startOutput(mId, track->streamType(),track->sessionId());...
}
status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{status = AudioSystem::startOutput(mId, track->streamType(),track->sessionId());...
}//mId就是PlaybackThread的序號,標識,output!
接下來我們轉入startOutput函數
status_t AudioPolicyManager::startOutput(audio_io_handle_t output,audio_stream_type_t stream,
audio_session_t session)
{//根據output這個id.找出outputDescssize_t index = mOutputs.indexOfKey(output);sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);//if (outputDesc->mPolicyMix != NULL) {...} else if (mOutputRoutes.hasRouteChanged(session)) {//選用新的設備newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);checkStrategyRoute(getStrategy(stream), output);}...status_t status = startSource(outputDesc, stream, newDevice, address, &delayMs);...
}
2.3 策略制定總結
AudioPolicyService加載解析/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
根據module的name加載廠家提供的so文件 (通過AudioFlinger來加載)
打開對應的output (通過AudioFlinger來open output)