<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                路由這個詞聽上去很專業,其實它的目的很簡單,就是為DSP選擇數據出口,例如是從耳機、聽筒還是揚聲器傳出。下面分析這樣一個場景: - 假設我們在用揚聲器聽歌,這時把耳機插上,會發生什么呢? 1. 耳機插拔事件處理 耳機插上后,系統會發一個廣播,Java層的AudioService會接收這個廣播,其中的內部類AudioServiceBroadcastReceiver會處理該事件,處理函數是onReceive。 這段代碼在AudioSystem.java中。一起來看: (1)耳機插拔事件接收 看這段代碼,如下所示: **AudioSystem.java::AudioServiceBroadcastReceiver的onReceive()** ~~~ private class AudioServiceBroadcastReceiverextends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); ...... //如果該事件是耳機插拔事件 elseif (action.equals(Intent.ACTION_HEADSET_PLUG)) { //取得耳機的狀態 int state = intent.getIntExtra("state", 0); int microphone =intent.getIntExtra("microphone", 0); if (microphone != 0) { //察看已連接設備是不是已經有了耳機,耳機的設備號為0x4, //這個和AudioSystem.h定義的設備號是一致的 boolean isConnected =mConnectedDevices.containsKey( AudioSystem.DEVICE_OUT_WIRED_HEADSET); //如果之前有耳機而現在沒有,則認為是耳機拔出事件 if (state == 0 &&isConnected) { //設置Audio系統的設備連接狀態,耳機為Unavailable AudioSystem.setDeviceConnectionState( AudioSystem.DEVICE_OUT_WIRED_HEADSET, AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); //從已連接設備中去掉耳機設備 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); } //如果state為1,并且之前沒有耳機連接,則處理這個耳機插入事件 else if (state == 1 && !isConnected){ //設置Audio系統的設備連接狀態,耳機為Available AudioSystem.setDeviceConnectionState( AudioSystem.DEVICE_OUT_WIRED_HEADSET, AudioSystem.DEVICE_STATE_AVAILABLE, ""); //已連接設備中增加耳機 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); } } ...... ~~~ 從上面的代碼中可看出,不論耳機插入還是拔出,都會調用AudioSystem的setDeviceConnectionState函數。 (2)setDeviceConnectionState:設置設備連接狀態 這個函數被定義為Native函數。下面是它的定義: **AudioSystem.java** ~~~ publicstatic native int setDeviceConnectionState(int device, int state, String device_address); //注意我們傳入的參數,device為0X4表示耳機,state為1,device_address為”” ~~~ 該函數的Native實現,在android_media_AudioSystem.cpp中,對應函數是: **android_media_AudioSystem.cpp** ~~~ static int android_media_AudioSystem_setDeviceConnectionState( JNIEnv*env, jobject thiz, jint device,jint state, jstring device_address) { constchar *c_address = env->GetStringUTFChars(device_address, NULL); intstatus = check_AudioSystem_Command( //調用Native AudioSystem的setDeviceConnectionState AudioSystem::setDeviceConnectionState( static_cast<AudioSystem::audio_devices>(device), static_cast<AudioSystem::device_connection_state>(state), c_address)); env->ReleaseStringUTFChars(device_address, c_address); returnstatus; } ~~~ 從AudioSystem.java轉入到AudioSystem.cpp,現在來看Native的對應函數: **AudioSystem.cpp** ~~~ status_tAudioSystem::setDeviceConnectionState(audio_devices device, device_connection_state state, const char *device_address) { constsp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if(aps == 0) return PERMISSION_DENIED; //轉到AP去,最終由AMB處理 returnaps->setDeviceConnectionState(device, state, device_address); } ~~~ Audio代碼不厭其煩地把函數調用從這一類轉移到另外一類,請直接看AMB的實現: **AudioPolicyManagerBase.cpp** ~~~ status_tAudioPolicyManagerBase::setDeviceConnectionState( AudioSystem::audio_devicesdevice, AudioSystem::device_connection_statestate, const char *device_address) { //一次只能設置一個設備 if(AudioSystem::popCount(device) != 1) return BAD_VALUE; ...... //根據設備號判斷是不是輸出設備,耳機肯定屬于輸出設備 if(AudioSystem::isOutputDevice(device)) { switch (state) { case AudioSystem::DEVICE_STATE_AVAILABLE: //處理耳機插入事件,mAvailableOutputDevices保存已連接的設備 //這個耳機是剛連上的,所以不走下面if分支 if (mAvailableOutputDevices & device) { //啟用過了,就不再啟用了。 return INVALID_OPERATION; } //現在已連接設備中多了一個耳機 mAvailableOutputDevices |= device; .... } //① getNewDevice之前已分析過了,這次再看 uint32_t newDevice =getNewDevice(mHardwareOutput, false); //②更新各種策略使用的設備 updateDeviceForStrategy(); //③設置新的輸出設備 setOutputDevice(mHardwareOutput,newDevice); ...... } ~~~ 這里面有三個比較重要的函數,前面也已提過,現將其再進行一次較深入的分析,旨在加深讀者對它的理解。 (3)getNewDevice 來看代碼,如下所示: **AudioPolicyManagerBase.cpp** ~~~ uint32_tAudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache) { //注意我們傳入的參數,output為mHardwardOutput,fromCache為false uint32_tdevice = 0; //根據output找到對應的AudioOutputDescriptor,這個對象保存了一些信息 AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); if(mPhoneState == AudioSystem::MODE_IN_CALL || outputDesc->isUsedByStrategy(STRATEGY_PHONE)) { device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); } elseif (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) { device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); } elseif (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) { //應用場景是正在聽歌,所以會走這個分支 device= getDeviceForStrategy(STRATEGY_MEDIA, fromCache); } elseif (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) { device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); } return device; } ~~~ 策略是怎么和設備聯系起來的呢?秘密就在getDeviceForStrategy中,來看: **AudioPolicyManagerBase.cpp** ~~~ uint32_tAudioPolicyManagerBase::getDeviceForStrategy( routing_strategystrategy, bool fromCache) { uint32_t device = 0; if (fromCache){//如果為true,則直接取之前的舊值 return mDeviceForStrategy[strategy]; } //如果fromCache為false,則需要重新計算策略所對應的設備 switch(strategy) { caseSTRATEGY_DTMF://先處理DTMF策略的情況 if(mPhoneState != AudioSystem::MODE_IN_CALL) { //如果不處于電話狀態,則DTMF的策略和MEDIA策略對應同一個設備 device = getDeviceForStrategy(STRATEGY_MEDIA, false); break; } //如果處于電話狀態,則DTMF策略和PHONE策略用同一個設備 caseSTRATEGY_PHONE: //是PHONE策略的時候,先要考慮是不是用戶強制使用了某個設備,例如強制使用揚聲器 switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { ...... case AudioSystem::FORCE_SPEAKER: ...... //如果沒有藍牙,則選擇揚聲器 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; break; } break; caseSTRATEGY_SONIFICATION://SONIFICATION策略 if(mPhoneState == AudioSystem::MODE_IN_CALL) { /* 如果處于來電狀態,則和PHONE策略用同一個設備。例如通話過程中我們強制使用 揚聲器,那么這個時候按撥號鍵,則按鍵聲也會從揚聲器出來 */ device = getDeviceForStrategy(STRATEGY_PHONE, false); break; } device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; //如果不處于電話狀態,則SONIFICATION和MEDIA策略用同一個設備 case STRATEGY_MEDIA: { //AUX_DIGITAL值為0x400,耳機不滿足該條件 uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; if(device2 == 0) { //也不滿足WIRED_HEADPHONE條件 device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; } if(device2 == 0) { //滿足這個條件,所以device2為0x4,WIRED_HEADSET device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; } if(device2 == 0) { device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; } device |= device2; //最終device為0x4,WIRED_HEADSET }break; default: break; } returndevice; } ~~~ getDeviceForStrategy是一個比較復雜的函數。它的復雜,在于選取設備時,需考慮很多情況。簡單的分析僅能和讀者一起領略一下它的風采,在實際工作中反復琢磨,或許才能掌握其中的奧妙。 好,getNewDevice將返回耳機的設備號0x4。下一個函數是updateDeviceForStrategy。這個函數和getNewDevice沒有什么關系,因為它沒用到getNewDevice的返回值。 (4)updateDeviceForStrategy 同樣是來看相應的代碼,如下所示: **AudioPolicyManagerBase.cpp** ~~~ voidAudioPolicyManagerBase::updateDeviceForStrategy() { for(int i = 0; i < NUM_STRATEGIES; i++) { //重新計算每種策略使用的設備,并保存到mDeviceForStrategy中,起到了cache的作用 mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i,false); } } ~~~ updateDeviceForStrategy會重新計算每種策略對應的設備。 另外,如果updateDeviceForStrategy和getNewDevice互換位置,就會節省很多不必要的調用。如: ~~~ updateDevicdForStrategy();//先更新策略 //使用cache中的設備,節省一次重新計算 uint32_t newDevice =getNewDevice(mHardwareOutput, true); ~~~ OK,不必討論這位碼農的功過了,現在看最后一個函數setOutputDevice。它會對新選出來的設備做如何處理呢? (5)setOutputDevice 繼續看setOutputDevice的代碼,如下所示: **AudioPolicyManagerBase.cpp** ~~~ void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_toutput, uint32_t device,bool force, int delayMs) { ...... //把這個請求要發送到output對應的AF工作線程中 AudioParameterparam = AudioParameter(); //參數是key/vlaue鍵值對的格式 param.addInt(String8(AudioParameter::keyRouting),(int)device); //mpClientInterface是AP對象,由它處理 mpClientInterface->setParameters(mHardwareOutput, param.toString(),delayMs); //設置音量,不做討論,讀者可自行分析 applyStreamVolumes(output, device, delayMs); } ~~~ setParameters最終會調用APS的setParameters,代碼如下所示: **AudioPolicyService.cpp** ~~~ voidAudioPolicyService::setParameters(audio_io_handle_t ioHandle, constString8& keyValuePairs, int delayMs) { //把這個請求加入到AudioCommandThread處理 mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs); } ~~~ AudioPolicyService創建時會同時創建兩個線程,其中一個用于處理各種請求。現在看看它是怎么做的。 2. AudioCommandThread AudioCommandThread有一個請求處理隊列,AP負責往該隊列中提交請求,而AudioCommandThread在它的線程函數threadLoop中處理這些命令。請直接看命令是如何處理的。 * * * * * **說明**:這種通過一個隊列來協調兩個線程的方法,在多線程編程中非常常見,它也屬于生產者/消費者模型。 * * * * * (1)AudioCommandThread中的處理 **AudioPolicyService.cpp** ~~~ boolAudioPolicyService::AudioCommandThread::threadLoop() { nsecs_twaitTime = INT64_MAX; mLock.lock(); while(!exitPending()) { while(!mAudioCommands.isEmpty()) { nsecs_t curTime = systemTime(); if (mAudioCommands[0]->mTime <= curTime) { AudioCommand *command = mAudioCommands[0]; mAudioCommands.removeAt(0); mLastCommand = *command; switch (command->mCommand) { case START_TONE: ...... case STOP_TONE: ...... //TONE處理 mLock.lock(); }break; case SET_VOLUME: { //設置音量 delete data; }break; case SET_PARAMETERS: { //處理路由設置請求 ParametersData *data =(ParametersData *)command->mParam; //轉到AudioSystem處理,mIO的值為mHardwareOutput command->mStatus =AudioSystem::setParameters( data->mIO, data->mKeyValuePairs); if(command->mWaitStatus) { command->mCond.signal(); mWaitWorkCV.wait(mLock); } delete data; }break; ...... default: } } ~~~ Audio系統真是非常繞!先看AudioSystem的setParameters。 (2)AudioSystem的setParameters AudioSystem將設置請求轉移給AudioFlinger處理,代碼如下所示: **AudioSystem.cpp** ~~~ status_tAudioSystem::setParameters(audio_io_handle_t ioHandle, constString8& keyValuePairs) { constsp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); //果然是交給AF處理,ioHandle看來一定就是工作線程索引號了 returnaf->setParameters(ioHandle, keyValuePairs); } ~~~ 離真相越來越近了,接著看代碼,如下所示: **AudioFlinger.cpp** ~~~ status_t AudioFlinger::setParameters(intioHandle, constString8& keyValuePairs) { status_t result; // ioHandle == 0 表示和混音線程無關,需要直接設置到HAL對象中。 if(ioHandle == 0) { AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; //調用AudioHardwareInterface的參數設置接口 result = mAudioHardware->setParameters(keyValuePairs); mHardwareStatus = AUDIO_HW_IDLE; return result; } sp<ThreadBase> thread; { Mutex::Autolock _l(mLock); //根據索引號找到對應混音線程。 thread = checkPlaybackThread_l(ioHandle); } //我們只有一個MixerThread,交給它處理,這又是一個命令處理隊列 result = thread->setParameters(keyValuePairs); returnresult; } returnBAD_VALUE; } ~~~ 好了,最終的請求處理在MixerThread的線程函數中,來看: (3)MixerThread最終處理 代碼如下所示: **AudioFlinger.cpp** ~~~ bool AudioFlinger::MixerThread::threadLoop() { .... while(!exitPending()) { processConfigEvents(); mixerStatus = MIXER_IDLE; {// scope for mLock Mutex::Autolock _l(mLock); // checkForNewParameters_l最有嫌疑 if (checkForNewParameters_l()) { ... } ......//其他處理 } ~~~ **AudioFlinger.cpp** ~~~ boolAudioFlinger::MixerThread::checkForNewParameters_l() { boolreconfig = false; while(!mNewParameters.isEmpty()) { status_t status = NO_ERROR; String8 keyValuePair = mNewParameters[0]; AudioParameter param = AudioParameter(keyValuePair); int value; ...... //路由設置需要硬件參與,所以直接交給代表音頻輸出設備的HAL對象處理 status = mOutput->setParameters(keyValuePair); return reconfig; } ~~~ 至此,路由設置所經歷的一切軌跡,我們都已清晰地看到了,可總還有點意猶未盡的感覺,HAL的setParameters到底是怎么工作的呢?不妨再來看一個實際的HAL對象處理例子。 (4)真實設備的處理 這個實際的Hardware,位于hardware/msm7k/libaudio-qsd8k的Hardware.cpp中,它提供了一個實際的音頻處理例子,這個Hardware針對的是高通公司的硬件。直接看它是怎么處理音頻輸出對象setParameters的,代碼如下所示: **AudioHardware.cppAudioStreamOutMSM72xx::setParameters()** ~~~ status_tAudioHardware::AudioStreamOutMSM72xx::setParameters( const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); String8 key = String8(AudioParameter::keyRouting); status_tstatus = NO_ERROR; intdevice; if(param.getInt(key, device) == NO_ERROR) { mDevices = device; //調用doRouting,mHardware就是AudioHardware對象 status = mHardware->doRouting(); param.remove(key); } ...... returnstatus; } ~~~ **AudioHardware.cpp** ~~~ status_t AudioHardware::doRouting() { Mutex::Autolock lock(mLock); uint32_t outputDevices = mOutput->devices(); status_t ret = NO_ERROR; intsndDevice = -1; ...... //做一些判斷,最終由doAudioRouteOrMute處理 if((vr_mode_change) || (sndDevice != -1 && sndDevice != mCurSndDevice)) { ret = doAudioRouteOrMute(sndDevice); mCurSndDevice = sndDevice; } returnret; } ~~~ **AudioHardware.cpp** ~~~ status_t AudioHardware::doAudioRouteOrMute(uint32_tdevice) { uint32_t rx_acdb_id = 0; uint32_t tx_acdb_id = 0; //只看看就行,對應硬件相關的代碼,咱們就是打打醬油 returndo_route_audio_dev_ctrl(device, mMode== AudioSystem::MODE_IN_CALL, rx_acdb_id, tx_acdb_id); } ~~~ **AudioHardware.cpp** ~~~ static status_t do_route_audio_dev_ctrl(uint32_tdevice, bool inCall, uint32_t rx_acdb_id, uint32_t tx_acdb_id) { uint32_t out_device = 0, mic_device = 0; uint32_t path[2]; int fd= 0; //打開音頻控制設備 fd =open("/dev/msm_audio_ctl", O_RDWR); path[0]= out_device; path[1]= rx_acdb_id; //通過ioctl切換設備,一般系統調用都是返回-1表示出錯,這里返回0表示出錯 if(ioctl(fd, AUDIO_SWITCH_DEVICE, &path)) { close(fd); return -1; } ...... } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看