1.1.1 AudioPolicyService的路由實現
我們在AudioPolicyService小節曾將其比作是一個“路由器”,不過還沒有深入解析它是如何完成路由選擇的。這部分的功能與使用者——AudioTrack有很大關聯,所以我們特別將它的實現原理剖析放在這裏,以使讀者可以綜合起來理解。
路由器功能由如下幾個部分組成:
l 與發送方(AudioTrack)的接口
就好像路由器首先要接收到一個IP數據包,它纔會去做路由處理,否則AudioPolicyService就成了“無源之水”了
l 與接收方(AudioFlinger)的接口
道理和上面是類似的,AudioPolicyService內部擁有當前系統中所有音頻設備的信息,就好比一個路由器也需要預先知道它有多少個節點,纔可能把音頻數據發送到正確的終點一樣
l 路由路徑的選擇策略
路徑選擇策略是AudioPolicyService的重點。和傳統的路由器不同,它的路徑選擇算法並不是固定的,而是通過靈活的方式先產生一個策略制定者,然後再由它來生成具體的策略
大家應該還記得前面AudioTrack小節中,我們調用了AudioSystem::getOutput,即:
status_t AudioTrack::set(…)
{…
audio_io_handle_t output =AudioSystem::getOutput(streamType, sampleRate, format, channelMask, flags);
…}
AudioSystem只是一箇中介,其中的實現還是由AudioPolicyService完成的:
audio_io_handle_t AudioSystem::getOutput(…)
{
constsp<IAudioPolicyService>& aps =AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
returnaps->getOutput(stream, samplingRate, format, channels, flags);
}
顯然是直接調用了AudioPolicyService的服務接口:
audio_io_handle_t AudioPolicyService::getOutput(...)
{ …
Mutex::Autolock _l(mLock);
return mpAudioPolicy->get_output(mpAudioPolicy,stream, samplingRate, format, channels, flags);
}
變量mpAudioPolicy便是由策略制定者“生產”出來的Policy。在原生態的實現中它代表的是legacy_audio_policy::policy@Audio_policy_hal.cpp,因而上面實際上調用的是如下函數:
static audio_io_handle_t ap_get_output(struct audio_policy *pol,…)
{
struct legacy_audio_policy*lap = to_lap(pol);
returnlap->apm->getOutput((AudioSystem::stream_type)stream, sampling_rate,(int) format, channels,
(AudioSystem::output_flags)flags);
}
也就是說,前面的apm->getOutput的接口實現最終是落在getOutput @ AudioPolicyManagerBase(AudioPolicyManagerDefault繼承自AudioPolicyManagerBase,而後者又繼承自AudioPolicyInterface)。
我們先來看下AudioPolicyManagerBase的getOutput實現。
/*hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp*/
audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_typestream,
uint32_t samplingRate, uint32_t format,
uint32_t channelMask, AudioSystem::output_flags flags)
{
audio_io_handle_t output = 0;
uint32_t latency = 0;
/*Step 1. 獲取stream類型對應的Strategy*/
routing_strategy strategy= getStrategy((AudioSystem::stream_type)stream);
audio_devices_t device = getDeviceForStrategy(strategy, false/*fromCache*/);
…
/*Step 2. 應用策略,判斷哪些Output符合用戶傳入的Stream類型*/
SortedVector<audio_io_handle_t>outputs = getOutputsForDevice(device);
/*Step 3. 選擇一個最適合的Output*/
output =selectOutput(outputs, flags);
return output;
}
我們將這個函數分爲三個步驟。
Step1@AudioPolicyManagerBase::getOutput. 每種Stream類型都有對應的strategy,比如AudioSystem::TTS 和AudioSystem::MUSIC對應的是STRATEGY_MEDIA,AudioSystem::NOTIFICATION對應的是STRATEGY_SONIFICATION_RESPECTFUL。具體的對應關係如下表所示:
表格 13‑5 Stream類型與Strategy對照表
STREAM_TYPE | STRATEGY |
VOICE_CALL | STRATEGY_PHONE |
BLUETOOTH_SCO | |
RING | STRATEGY_SONIFICATION |
ALARM | |
NOTIFICATION | STRATEGY_SONIFICATION_RESPECTFUL |
DTMF | STRATEGY_DTMF |
SYSTEM | STRATEGY_MEDIA |
TTS | |
MUSIC | |
ENFORCED_AUDIBLE | STRATEGY_ENFORCED_AUDIBLE |
不同的Stream類型有可能會被劃歸同一個Strategy,比如TTS、MUSIC及SYSTEM類型的音頻,它們在路由策略上都遵循STRATEGY_MEDIA。當然我們也可以通過重載getStrategy來按自己的要求劃分Strategy。
當找到某Stream類型對應的Strategy後,接下來getDeviceForStrategy進一步爲這一Strategy查找最佳匹配的音頻設備(以STRATEGY_MEDIA爲例):
audio_devices_tAudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, boolfromCache)
{
uint32_t device = 0;
…
switch(strategy) {
case STRATEGY_MEDIA: {
uint32_t device2 = 0;
if (mHasA2dp&& (mForceUse[AudioSystem::FOR_MEDIA] !=
AudioSystem::FORCE_NO_BT_A2DP)&& (getA2dpOutput() != 0) && !mA2dpSuspended) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
}
…
device |= device2;
if (device) break;
device =mDefaultOutputDevice;
if (device == 0) {
ALOGE("getDeviceForStrategy() no device found forSTRATEGY_MEDIA");
}
} break;
…
上面的代碼看上去很長,但邏輯比較簡單——按照一定的優先級來匹配系統中已經存在的音頻設備。這個優先級的設定因Strategy不同而有所差異。在STRATEGY_MEDIA這種情況下,其優先級如下所示:
² 在有藍牙A2dp的平臺上,且設備可以正常打開,沒有掛起,當前也沒有強制不使用A2dp,那麼通過匹配mAvailableOutputDevices來尋找合適的A2dp設備,比如A2dp_headphone、A2dp_Speaker
² 要注意的是,只有在上一步匹配失敗(即找不到合適的設備,變量device2爲0)的情況下,纔會繼續執行下一優先級的判斷。這裏處於第二等級的是wired headphone
² 繼續尋找是否有wired headset
² 尋找是否有usb accessory
² 尋找是否有usb device
等等。。。
正常情況下getDeviceForStrategy都能獲得符合要求的device。我們再回到前面的getOutput,看下接下來步驟的執行。
Step2@AudioPolicyManagerBase::getOutput
SortedVector<audio_io_handle_t> AudioPolicyManagerBase::getOutputsForDevice(audio_devices_tdevice)
{
SortedVector<audio_io_handle_t> outputs;
for (size_t i = 0; i <mOutputs.size(); i++) {
if ((device &mOutputs.valueAt(i)->supportedDevices()) == device) {
outputs.add(mOutputs.keyAt(i));
}
}
return outputs;
}
這個函數用於獲得所有支持device設備的Output,並添加到outputs中。Output是AudioFlinger::openOutput得到的結果,AudioPolicyService會把它們存儲到mOutputs鍵值對中。因爲每個Output通常都支持若干種音頻設備,不同的Output支持的音頻設備類型也是不限的,所以系統中很可能存在多個支持device的Output。
Step 3@AudioPolicyManagerBase::getOutput. 到目前爲止,符合要求的Output可能不止一個,所以要選擇一個最適合的。
audio_io_handle_t AudioPolicyManagerBase::selectOutput(constSortedVector<audio_io_handle_t>& outputs,
AudioSystem::output_flags flags)
{
/*Step 1. 處理一些特殊情況*/
if (outputs.size() == 0) {
return 0;
}
if (outputs.size() == 1) {
return outputs[0];
}
先處理一些特殊情況,比如沒有任何output存在的情況下只能返回空;同樣的如果只有一個output的情況也沒得選擇,直接返回該output。
/*Step 2. 開始判斷擇優*/
int maxCommonFlags = 0;
audio_io_handle_toutputFlags = 0;
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i <outputs.size(); i++) {
AudioOutputDescriptor*outputDesc = mOutputs.valueFor(outputs[i]);
if(!outputDesc->isDuplicated()) {
int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
if (commonFlags > maxCommonFlags) {
outputFlags =outputs[i];
maxCommonFlags= commonFlags;
ALOGV("selectOutput() commonFlags foroutput %d, %04x", outputs[i], commonFlags);
}
if(outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
outputPrimary= outputs[i];
}
}
}
這個循環是整個函數的核心,我們來分析下它的決策準繩是什麼。大家只要仔細看下循環中的判斷語句,就可以發現它實際上是在尋找最大值maxCommonFlags,所以問題就轉化爲,什麼東西的最大值?
int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
上面這句代碼用通俗的話來講,就是計算outputDesc->mProfile->mFlags與入參flags的相似度有多大。可選的Flags如下所示:
AUDIO_OUTPUT_FLAG_NONE = 0x0,
AUDIO_OUTPUT_FLAG_DIRECT = 0x1, //output直接把track導向一個output stream,沒有混音器
AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, //primary output,它是唯一的並且必需存在
AUDIO_OUTPUT_FLAG_FAST = 0x4, //支持fasttracks的output
AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 //使用deepaudio buffer的output
音頻系統中名爲output_flags的數據類型非常多,不過仔細回溯的話,可以發現這個flags是在AudioTrack的set函數中指定的。另外,如果在查找過程中發現primaryoutput,則用outputPrimary表示,這在後面會用到。
/*Step 3. 根據優先級做出選擇*/
if (outputFlags != 0) {
return outputFlags;
}
if (outputPrimary != 0) {
return outputPrimary;
}
return outputs[0];
}
優先級的排列很簡單,即:
² Flags與要求相似度高的output
² Primaryoutput
² 如果上面兩種都找不到,則默認返回第一個output
這樣子AudioPolicyService就完成了整個路由路徑的選擇,AudioTrack則是通過AudioSystem::getOutput間接調用到AudioPolicyService的這一功能。