audio的输出设备切换分析

本文主要介绍android上音频输出设备切换的代码流程
(此文部分内容参考自邓凡达老师的博客。感谢邓老师讲解)


上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setForceUse
[cpp]  view plain  copy
  1. status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)  
  2. {  
  3.     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();  
  4.     if (aps == 0) return PERMISSION_DENIED;  
  5.     return aps->setForceUse(usage, config);  
  6. }  



接下来就是调用frameworks/av/services/audioflinger/AudioPolicyService.cpp的setForceUse()函数了;
[cpp]  view plain  copy
  1. status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,  
  2.                                          audio_policy_forced_cfg_t config)  
  3. {  
  4.     Mutex::Autolock _l(mLock);  
  5.     mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);  
  6.     return NO_ERROR;  
  7. }  



AP章节中,我们介绍过,mpAudioPolicy实际上是在AudioServicePolicy.cpp的构造函数中被赋值,可以理解成是指向了AP HAL层的handle
mpAudioPolicy->set_force_use实际上调用的是audio_policy_hal.cpp里面的ap_set_force_use。
[cpp]  view plain  copy
  1. static void ap_set_force_use(struct audio_policy *pol,  
  2.                           audio_policy_force_use_t usage,  
  3.                           audio_policy_forced_cfg_t config)  
  4. {  
  5.     struct legacy_audio_policy *lap = to_lap(pol);  
  6.     lap->apm->setForceUse((AudioSystem::force_use)usage,  
  7.                           (AudioSystem::forced_config)config);  
  8. }  


继而调用AudioPolicyManagerBase::setForceUse
[cpp]  view plain  copy
  1. void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)  
  2. {  
  3. ....  
  4.     checkA2dpSuspend();  
  5.     checkOutputForAllStrategies();  
  6.     updateDevicesAndOutputs(); //各种内部状态的更新  
  7.     for (size_t i = 0; i < mOutputs.size(); i++) {  
  8.         audio_io_handle_t output = mOutputs.keyAt(i);  
  9.         audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/);  
  10.         setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));  //继续往下调用  
  11.         if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {  
  12.             applyStreamVolumes(output, newDevice, 0, true);  
  13.         }  
  14.     }  
  15. ....  
  16.    
  17. }  


继续看setOutputDevice的代码,如下所示:

[-->AudioPolicyManagerBase.cpp]
[cpp]  view plain  copy
  1. void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_toutput,  uint32_t device,bool force, int delayMs)  
  2. {  
  3.     ......  
  4.    //把这个请求要发送到output对应的AF工作线程中  
  5.      AudioParameterparam = AudioParameter();  
  6.    //参数是key/vlaue键值对的格式  
  7.     param.addInt(String8(AudioParameter::keyRouting),(int)device);  
  8.    //mpClientInterface是AP对象,由它处理  
  9.     mpClientInterface->setParameters(mHardwareOutput, param.toString(),delayMs);  
  10.    //设置音量,不做讨论,读者可自行分析  
  11.    applyStreamVolumes(output, device, delayMs);  
  12.  }  



setParameters最终会调用APS的setParameters,代码如下所示:
[-->AudioPolicyService.cpp]
[cpp]  view plain  copy
  1. voidAudioPolicyService::setParameters(audio_io_handle_t ioHandle,  
  2.                               constString8& keyValuePairs, int delayMs)  
  3. {  
  4.     //把这个请求加入到AudioCommandThread处理  
  5.     mAudioCommandThread->parametersCommand((int)ioHandle,  
  6.                                                  keyValuePairs, delayMs);  
  7. }  



AudioPolicyService创建时会同时创建两个线程,其中一个用于处理各种请求。现在看看它是怎么做的。
AudioCommandThread有一个请求处理队列,AP负责往该队列中提交请求,而AudioCommandThread在它的线程函数threadLoop中处理这些命令。请直接看命令是如何处理的。
说明:这种通过一个队列来协调两个线程的方法,在多线程编程中非常常见,它也属于生产者/消费者模型。


AudioCommandThread中的处理
[-->AudioPolicyService.cpp]
[cpp]  view plain  copy
  1. boolAudioPolicyService::AudioCommandThread::threadLoop()  
  2. {  
  3.     nsecs_twaitTime = INT64_MAX;  
  4.    mLock.lock();  
  5.     while(!exitPending())  
  6.     {  
  7.        while(!mAudioCommands.isEmpty()) {  
  8.            nsecs_t curTime = systemTime();  
  9.            if (mAudioCommands[0]->mTime <= curTime) {  
  10.                AudioCommand *command = mAudioCommands[0];  
  11.                mAudioCommands.removeAt(0);  
  12.                mLastCommand = *command;  
  13.                switch (command->mCommand) {  
  14.                case START_TONE:  
  15.                    ......  
  16.                case STOP_TONE:  
  17.                    ......  //TONE处理  
  18.                     mLock.lock();  
  19.                     }break;  
  20.                case SET_VOLUME: {  
  21.                     //设置音量  
  22.                     delete data;  
  23.                     }break;  
  24.                case SET_PARAMETERS: {  
  25.                     //处理路由设置请求  
  26.                      ParametersData *data =(ParametersData *)command->mParam;  
  27.                     //转到AudioSystem处理,mIO的值为mHardwareOutput  
  28.                      command->mStatus =AudioSystem::setParameters(  
  29.                                           data->mIO,  
  30.                                          data->mKeyValuePairs);  
  31.                      if(command->mWaitStatus) {  
  32.                         command->mCond.signal();  
  33.                         mWaitWorkCV.wait(mLock);  
  34.                      }  
  35.                      delete data;  
  36.                      }break;  
  37.                ......  
  38.                default:  
  39.                }  
  40. }  

先看AudioSystem的setParameters。
AudioSystem将设置请求转移给AudioFlinger处理,代码如下所示:
[cpp]  view plain  copy
  1. [-->AudioSystem.cpp]  
  2. status_t AudioSystem::setParameters(audio_io_handle_t ioHandle,  
  3.                                           constString8& keyValuePairs)  
  4. {  
  5.      const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();  
  6.     //果然是交给AF处理,ioHandle看来一定就是工作线程索引号了  
  7.     return af->setParameters(ioHandle, keyValuePairs);  
  8. }  



离真相越来越近了,接着看代码,如下所示:
[-->AudioFlinger.cpp]
[cpp]  view plain  copy
  1. status_t AudioFlinger::setParameters(intioHandle,  constString8& keyValuePairs)  
  2. {  
  3.    status_t result;  
  4.     // ioHandle == 0 表示和混音线程无关,需要直接设置到HAL对象中。  
  5.     if(ioHandle == 0) {  
  6.        AutoMutex lock(mHardwareLock);  
  7.        mHardwareStatus = AUDIO_SET_PARAMETER;  
  8.        //调用AudioHardwareInterface的参数设置接口  
  9.        result = mAudioHardware->setParameters(keyValuePairs);  
  10.        mHardwareStatus = AUDIO_HW_IDLE;  
  11.        return result;  
  12.     }  
  13.     sp<ThreadBase> thread;  
  14.     {  
  15.        Mutex::Autolock _l(mLock);  
  16.        //根据索引号找到对应混音线程。  
  17.        thread = checkPlaybackThread_l(ioHandle);  
  18.       }  
  19.      result = thread->setParameters(keyValuePairs);  
  20.       return result;  
  21.     }  
  22.     return BAD_VALUE;  
  23. }  


好了,最终的请求处理在MixerThread,或者DirectPlaybackThread的线程函数中,来看:
例如MixerThread最终处理代码如下所示:
[
[cpp]  view plain  copy
  1. -->AudioFlinger.cpp]  
  2. bool AudioFlinger::MixerThread::threadLoop()  
  3. {  
  4.     ....  
  5.     while(!exitPending())  
  6.     {  
  7.        processConfigEvents();  
  8.        mixerStatus = MIXER_IDLE;  
  9.         {// scope for mLock  
  10.            Mutex::Autolock _l(mLock);  
  11.           // checkForNewParameters_l最有嫌疑  
  12.            if (checkForNewParameters_l()) {  
  13.                 ...  
  14.            }  
  15.       ......//其他处理  
  16. }  


[cpp]  view plain  copy
  1. [-->AudioFlinger.cpp]  
  2. boolAudioFlinger::MixerThread::checkForNewParameters_l()  
  3. {  
  4.   
  5.   
  6.     boolreconfig = false;  
  7.     while(!mNewParameters.isEmpty()) {  
  8.        status_t status = NO_ERROR;  
  9.        String8 keyValuePair = mNewParameters[0];  
  10.        AudioParameter param = AudioParameter(keyValuePair);  
  11.        int value;  
  12.        ......  
  13.       //路由设置需要硬件参与,所以直接交给代表音频输出设备的HAL对象处理  
  14.       status = mOutput->setParameters(keyValuePair);     
  15.       return reconfig;  
  16. }  


至此,路由设置最终通过HAL的setParameters来实现。
在某款android DTV的具体HAL实现上,
智能电视上只有三路音频通道:板载speaker,SPDIF,蓝牙,
切换到蓝牙时,走A2DP的path,这里没有深入研究。不做赘述。
切换到板载speaker或者SPDIF时,这两者在同一个HAL里。
HAL层切换的时候,根据不同的setParameters参数,打开不同的/dev/snd/pcm设备写就可以了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值