在創(chuàng)建一個AudioPolicyService對象時,主要是: 1. 創(chuàng)建兩個AudioCommand線程,一個線程用于Tone的播放,另一個用于聲音的音量及其它參數(shù)設(shè)置; 2.創(chuàng)建音頻策略管理器(AudioPolicyManager)。Android Frameworks為音頻策略管理器定義了統(tǒng)一的接口--AudioPolicyInterface,并提供一個缺省的音頻管理器--AudioPolicyManagerBase,它為運行在模擬器上Android所使用,也可以使用宏激活該通用管理器。不同的硬件廠商,可以編寫一個繼承AudioPolicyInterface的子類,針對自己的硬件平臺,實現(xiàn)自己的音頻策略管理。 在音頻策略服務(AudioPolicyService)中,會根據(jù)一定的條件去判斷創(chuàng)建何種音頻策略管理器: AudioPolicyService::AudioPolicyService() #if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST) // load properties 下圖反映了幾個類之間的關(guān)系:AudioPolicyService繼承自BnAudioPolicyService,所以它提供了IAudioPolicyService接口的真正實現(xiàn),可以通過該接口跨進程調(diào)用到它。AudioPolicyService也繼承了AudioPolicyClientInterface,所以它實現(xiàn)了該抽象類的接口,其實現(xiàn)是通過AudioSystem調(diào)用到AudioFlinger(下圖AudioPolicyService到AudioFlinger的虛線所示)。如上面代碼所示,在創(chuàng)建AudioPolicyService時,會創(chuàng)建一個音頻策略管理器,如Android默認的管理器AudioPolicyManagerBase,也可能創(chuàng)建的是硬件平臺廠家提供的音頻管理器,這些管理器實現(xiàn)了AudioPolicyInterface接口。在創(chuàng)建音頻管理器時,將this指針傳遞給了它,意味著音頻管理器可以通過AudioPolicyClientInterface接口指針(圖中策略管理器到AudioPolicyClientInterface的虛線)來使用AudioPolicyService,進而使用AudioFlinger。
音頻策略管理器(Audio Policy Manager) 音頻策略管理器用于管理聲音的輸入輸出路由,它決定各種類型的聲音(即流類型)優(yōu)先送往系統(tǒng)中的哪種輸出設(shè)備,或優(yōu)先采用哪種輸入設(shè)備進行聲音的采樣。如手機連有藍牙耳機或耳機(HeadPhone)時,將優(yōu)先使用它們作為輸入輸出設(shè)備。在Android中,有一個通用實現(xiàn):類AudioPolicyManagerBase。硬件平臺廠家往往需要實現(xiàn)自己的音頻策略管理器,所有的重新實現(xiàn)必繼承抽象類AudioPolicyInterface,該繼承類定義了統(tǒng)一的接口函數(shù)。繼承自該抽象類的通用實現(xiàn)AudioPolicyManagerBase位于文件frameworks/base/services/audioflinger/AudioPolicyManagerBase.cpp中。 在進行播放時,需使用策略管理器的getOutput函數(shù)去獲得一個音頻輸入輸出句柄audio_io_handle_t。這個句柄用來標識策略管理器中使用的一個數(shù)據(jù)結(jié)構(gòu):音頻輸出描述符AudioOutputDescriptor。一個描述符對應著一次音頻輸出,由輸出句柄標識。音頻輸出描述符包含了某音頻輸出對應的輸出設(shè)備、音量、格式、聲道、采樣率等信息。在策略管理器中,也維護著句柄和描述符的映射表: KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; 音頻輸入輸出句柄audio_io_handle_t,實際上是一個整型數(shù),是在AduioFlinger打開輸出(openOutput)時遞增分配一個整型id號,用于標示新創(chuàng)建的播放線程(DirectOutputThread或MixerThread)。這個id號和新創(chuàng)建的線程作為數(shù)據(jù)對添加到向量表mPlaybackThreads中。AudioFlinger中維護的播放線程向量表如下,由句柄這個id號標識: DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads; 因此,在策略管理中,可以由句柄得到對應的音頻輸出描述符,進而可以得到對應的音頻相關(guān)信息和輸出設(shè)備。在AudioFlinger中,可以由句柄得到對應的播放線程(該線程用于將音頻數(shù)據(jù)送往HAL音頻硬件)。 在Android中,將聲音區(qū)分為不同的流類型。不同的流類型往往使用不同的輸入輸出設(shè)備進行輸出,這就是音頻策略。流類型由AudioSystem統(tǒng)一定義,但音頻策略由平臺實現(xiàn)者定義。因不同的平臺廠商往往有自己的輸入輸出設(shè)備,音頻的輸入輸出與這些設(shè)備有很大的相關(guān)性。因此,音頻策略由平臺廠商定義。在通用的實現(xiàn)AudioPolicyManagerBase中,定義有4種音頻策略類型: enum routing_strategy { 根據(jù)流類型(Stream Type),應該可以得到對應的音頻輸入輸出策略,這是由成員函數(shù)getStrategyForStream(AudioPolicyInterface中定義的接口之一)來實現(xiàn)。在通用實現(xiàn)AudioPolicyManagerBase中,由成員函數(shù)getStrategy真正去完成該功能。該函數(shù)實現(xiàn)的對應關(guān)系如下表:
第一欄是流的類型,第二欄是音頻策略。從表中可以看出,某些流可以使用相同的音頻策略進行輸出或輸入。 通過流確定了策略之后,就可以確定輸入輸出設(shè)備。在通常情況下,一種策略又決定著聲音何種輸入輸出設(shè)備。 這由函數(shù)AudioPolicyManagerBase::getDeviceForStrategy來實現(xiàn):
有時,我們不希望策略管理器選定的設(shè)備,而希望自己選擇輸入輸出設(shè)備,在SDK的API中,提供了兩個函數(shù)來為通話狀態(tài)強制選擇揚聲器和藍牙設(shè)備,這兩個函數(shù)API是:android.media.AudioManager的setSpeakerphoneOn和setBluetoothScoOn。它們?yōu)橥ㄔ挔顟B(tài)(AudioSystem.FOR_COMMUNICATION)強制指定語音輸入和輸出設(shè)備。這兩個API函數(shù)調(diào)用順序如下:android.media.AudioManager->android.media.AudioService->android.media.AudioSystem->JNI層(文件android_media_AudioSystem.cpp)->AudioSystem-> AudioPolicyService->AudioPolicyManagerBase::setForceUse。 通過上述調(diào)用后,函數(shù)setForceUse將記錄強制使用的設(shè)備,以優(yōu)先使用。下表第一欄是四種場景類型,第二欄是可以強制使用的語音輸入輸出設(shè)備:
更多的強制選擇,可以修改setForceUse的對應檢查后,然后依據(jù)自己的代碼所在的層次,參照對setForceUse的調(diào)用層次,調(diào)用適當?shù)暮瘮?shù)。
對于各種輸入輸出設(shè)備是否可用,如藍牙耳機或線控耳機是否連接上,則是在Java層的android.media.AudioSystem中進行檢測處理。當這些外設(shè)連接狀態(tài)發(fā)生變化時,對應的模塊會廣播發(fā)送Intent,而android.media.AudioSystem中的AudioServiceBroadcastReceiver會接收到這些Intent,從而判斷出設(shè)備狀態(tài)變化,通過如下調(diào)用順序來修可用IO設(shè)備列表:android.media.AudioSystem->JNI層 -> AudioSystem-> AudioPolicyService -> AudioPolicyManagerBase的setDeviceConnectionState。
創(chuàng)建AudioPolicyManagerBase時,會在其構(gòu)造函數(shù)里
mpClientInterface = clientInterface;//指向AudioPolicyService的指針 for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) { // devices available by default are speaker, ear piece and microphone #ifdef WITH_A2DP//一般情況下A2DP是打開的 // open hardware output if (mHardwareOutput == 0) {//失敗 從上面的代碼看出,在創(chuàng)建音頻管理器的初始時刻,就會打開一個默認的輸出,這個輸出句柄賦值給mHardwareOutput,并添加到對應的列表中。
可以使用函數(shù)setOutputDevice為輸出句柄(output)指定輸出設(shè)備: void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs) 它首先檢查是復制輸出,是否是A2DP輸出,然后調(diào)用mpClientInterface(實際調(diào)用到AudioFlinger)的setParameters來設(shè)置輸出設(shè)備。還要將輸出設(shè)備信息,更新到句柄output對應的輸出描述符中。
獲取音頻IO句柄 audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream, uint32_t samplingRate, uint32_t format, uint32_t channels, AudioSystem::output_flags flags) 該函數(shù)根據(jù)steam類型得到strategy,再進而得到輸出設(shè)備,然后創(chuàng)建一個輸出描述符AudioOutputDescriptor,儲存相關(guān)信息,最后將句柄和描述符添加到策略管理器維護的映射表中。注意:句柄的獲取實際調(diào)用的是mpClientInterface的openOutput(實際調(diào)用到AudioFlinger的openOutput去準備播放線程進行播放輸出)。
開始輸出 status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream, int session) 主要是調(diào)用setOutputDevice為ouput句柄指定輸出設(shè)備,最后為其設(shè)定流對應的音量
主要是調(diào)用setOutputDevice為ouput句柄恢復為默認的輸出設(shè)備 status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream, int session) 將輸出 獲得輸入句柄 audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource, uint32_t samplingRate, uint32_t format, uint32_t channels, AudioSystem::audio_in_acoustics acoustics, AudioSystem::audio_input_clients *inputClientId)
開始輸入 status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input) 通過輸入句柄得到對應的輸入描述符,然后通過mpClientInterface(實際調(diào)用到AudioFlinger)的setParameters將AudioParameter::keyRouting,inputDesc->mDevice,AudioParameter::keyInputSource,inputDesc->mInputSource
停止輸入 status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input) 類似于輸入,通過setParameters將AudioParameter::keyRouting對應的值修改為0 |
|