Android音頻系統之AudioTrack(二)


 8093人閱讀 評論(2) 收藏 舉報
 分類:

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的這一功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章