Android audio(8)-native音頻服務的啟動與協作(audiopolicyservice和audioflinger)

音頻策略的構建
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)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72705.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72705.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72705.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

DeepSeek:從入門到精通

DeepSeek是什么&#xff1f; DeepSeek是一家專注通用人工智能&#xff08;AGI&#xff09;的中國科技公司&#xff0c;主攻大模型研發與應 用。DeepSeek-R1是其開源的推理模型&#xff0c;擅長處理復雜任務且可免費商用。 Deepseek可以做什么&#xff1f; 直接面向用戶或者支持…

【一起來學kubernetes】17、Configmap使用詳解

前言概述核心特性創建 ConfigMap使用 ConfigMap1. **環境變量**2. **Volume 掛載**3. **命令行參數** 更新與熱重載Docker容器中Java服務使用Configmap**一、通過環境變量注入****步驟說明****示例配置** **二、通過 Volume 掛載配置文件****步驟說明****示例配置** **三、動態…

【八股文】從瀏覽器輸入一個url到服務器的流程

1.url解析與DNS解析 瀏覽器解析用戶輸入的URL&#xff0c;提取協議&#xff08;HTTP\HTTPS&#xff09;、域名、端口及路徑等信息 瀏覽器首先檢查本地DNS緩存和系統DNS緩存&#xff0c;若未命中&#xff0c;查詢本地hosts文件 最后遞歸查詢向本地DNS服務器發起請求&#xff…

網絡空間安全(34)安全防御體系

前言 安全防御體系是一個多層次、多維度的系統&#xff0c;旨在保護組織或個人的信息資產免受各種網絡攻擊和威脅。 一、技術層面 網絡邊界防御 防火墻&#xff1a;部署在網絡邊界&#xff0c;通過設定規則允許或阻止特定流量的進出&#xff0c;保護內部網絡不受外部攻擊。入侵…

Linux 入門:權限的認識和學習

目錄 一.shell命令以及運行原理 二.Linux權限的概念 1.Linux下兩種用戶 cannot open directory .: Permission denied 問題 2.Linux權限管理 1).是什么 2).為什么&#xff08;權限角色目標權限屬性&#xff09; 3).文件訪問者的分類&#xff08;角色&#xff09; 4).文…

【筆記】計算機網絡——數據鏈路層

概述 鏈路是從一個結點到相鄰結點的物理路線&#xff0c;數據鏈路則是在鏈路的基礎上增加了一些必要的硬件和軟件實現 數據鏈路層位于物理層和網絡層之間&#xff0c;它的核心任務是在直接相連的節點&#xff08;如相鄰的交換機&#xff0c;路由器&#xff09;之間提供可靠且…

ngx_url_t

定義在 src/core/ngx_inet.h typedef struct {ngx_str_t url;ngx_str_t host;ngx_str_t port_text;ngx_str_t uri;in_port_t port;in_port_t default_port;in_port_t …

搜廣推校招面經五十四

美團推薦算法 一、手撕Transformer的位置編碼 1.1. 位置編碼的作用 Transformer 模型沒有顯式的序列信息&#xff08;如 RNN 的循環結構&#xff09;&#xff0c;因此需要通過位置編碼&#xff08;Positional Encoding&#xff09;為輸入序列中的每個位置添加位置信息。位置…

網絡爬蟲【爬蟲庫urllib】

我叫不三不四&#xff0c;很高興見到大家&#xff0c;歡迎一起學習交流和進步 今天來講一講爬蟲 urllib介紹 Urllib是Python自帶的標準庫&#xff0c;無須安裝&#xff0c;直接引用即可。 Urllib是一個收集幾個模塊來使用URL的軟件包&#xff0c;大致具備以下功能。 ● urlli…

LabVIEW棉花穴播器排種自動監測系統

一、項目背景與行業痛點 1. 農業需求驅動 我國棉花主產區&#xff0c;種植面積常年超250萬公頃&#xff0c;傳統人工播種存在兩大核心問題&#xff1a; 效率瓶頸&#xff1a;人均日播種面積不足0.5公頃&#xff0c;難以匹配規模化種植需求&#xff1b; 精度缺陷&#xff1a;人…

解決diffusers加載stablediffusion模型,輸入prompt總是報錯token數超出clip最大長度限制

1. StableDiffusion1.5 在加載huggingface中的擴散模型時&#xff0c;輸入prompt總是會被報錯超過clip的最大長度限制。 解決方案&#xff1a;使用compel庫 from diffusers import AutoPipelineForText2Image import torch import pdb from compel import Compeldevice torc…

jmeter配件元素

jmeter配件元素 CSV Data Set Config名詞解釋測試場景Recycle on EOF:False配置測試結果 Recycle on EOF:True配置測試結果 Sharing mode:All Threads配置測試結果 Sharing mode:Current thread group配置測試結果 Sharing mode:Current thread配置測試結果 HTTP Header Manage…

Navicat SqlServer 設置自增主鍵

Navicat是一款優秀的數據庫管理工具&#xff0c;可以連接很多類型的數據庫。使用它可以極大的提高工作效率。 Navicat 不能設置SqlServer自增字段&#xff0c;只能通過sql語句來實現 建表時設置 create table <表名> ( <字段1-主鍵> int identity (1,1) primar…

Elasticsearch搜索引擎 3(DSL)

Elasticsearch提供了基于JSON的DSL&#xff08;Domain Specific Language&#xff09;語句來定義查詢條件&#xff0c;其JavaAPI就是在組織DSL條件。 1.DSL查詢 葉子查詢&#xff08;Leaf query clauses&#xff09;&#xff1a;在特定的字段里查詢特定值&#xff0c;屬于簡單…

final 在 java 中有什么作用?

final 在 java 中有什么作用&#xff1f; 修飾變量 修飾基本數據類型變量&#xff1a; 當用final修飾基本數據類型變量時&#xff0c;該變量就變成了常量&#xff0c;其值在初始化后不能被改變。 final int num 10; // num 20; // 這行代碼會導致編譯錯誤&#xff0c;因…

Dubbo/Hession2序列化Immutable類型的集合異常問題

問題排查 根據堆棧信息可見&#xff0c;dubbo使用默認的hession2進行序列化時出現了異常&#xff0c;異常堆棧根原因為&#xff1a;null array 位于java.util.CollSer#readResolve方法中&#xff0c;即在序列化集合時&#xff0c;集合數組為空。 向上追溯jdk.internal.ref…

目標檢測任務,如何區分兩個相近似的目標

首先&#xff0c;要了解清楚檢測的場景下&#xff0c;肉眼能否區分出目標的差異性。 如果可以區分&#xff0c;那觀察數據周圍背景的差異是否較大&#xff0c;可以先通過添加樣本來提升模型的檢測精度。添加樣本時一定要注意&#xff0c;樣本標注的準確性&#xff0c;樣本的豐…

Java面試黃金寶典1

1. 8 種基本數據類型 整數類型 byte&#xff1a; 它是最小的整數類型&#xff0c;占用 1 個字節&#xff08;8 位&#xff09;。在一些對內存使用要求極高的場景&#xff0c;比如嵌入式系統開發、數據傳輸時對數據量有嚴格限制的情況&#xff0c;會使用 byte 類型。例如&#x…

OSGEarth

OSGEarth 基于 OpenSceneGraph 構建的一個擴展庫&#xff0c;專門用于地球科學和地理信息系統&#xff08;GIS&#xff09;數據的可視化。它允許開發者創建逼真的三維地球模型&#xff0c;并在其上展示各種地理空間數據。 高端一點的表述 基于三維引擎osg開發的三維數字地球…

Word 小黑第34套

對應大貓34 設置第二頁水印&#xff0c;取消第一頁的&#xff1a;取消第二頁頁眉鏈接&#xff0c;刪除第一張水印圖片&#xff08;delete&#xff09; 調整水印圖片&#xff1a;點開頁眉頁腳 雙擊圖片 可以調整 郵件合并 -創建標簽 橫標簽數3 豎標簽5 表布局 -查看網格線 插…