当前位置:   article > 正文

audio的输出设备切换分析_audiopolicyintefaceimpl: setforceuse()

audiopolicyintefaceimpl: setforceuse()


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


上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setForceUse
  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()函数了;
  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。
  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
  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. }


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

[-->AudioPolicyManagerBase.cpp]
  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]
  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]
  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处理,代码如下所示:
  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]
  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最终处理代码如下所示:
[
  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. }


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


至此,路由设置最终通过HAL的setParameters来实现。
在某款android DTV的具体HAL实现上,
智能电视上只有三路音频通道:板载speaker,SPDIF,蓝牙,
切换到蓝牙时,走A2DP的path,这里没有深入研究。不做赘述。
切换到板载speaker或者SPDIF时,这两者在同一个HAL里。
HAL层切换的时候,根据不同的setParameters参数,打开不同的/dev/snd/pcm设备写就可以了。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/499821
推荐阅读
  

闽ICP备14008679号