本文主要介绍android上音频输出设备切换的代码流程
(此文部分内容参考自邓凡达老师的博客。感谢邓老师讲解)
上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setForceUse
接下来就是调用frameworks/av/services/audioflinger/AudioPolicyService.cpp的setForceUse()函数了;
mpAudioPolicy->set_force_use实际上调用的是audio_policy_hal.cpp里面的ap_set_force_use。
继而调用AudioPolicyManagerBase::setForceUse
继续看setOutputDevice的代码,如下所示:
[-->AudioPolicyManagerBase.cpp]
[-->AudioPolicyService.cpp]
AudioPolicyService创建时会同时创建两个线程,其中一个用于处理各种请求。现在看看它是怎么做的。
AudioCommandThread有一个请求处理队列,AP负责往该队列中提交请求,而AudioCommandThread在它的线程函数threadLoop中处理这些命令。请直接看命令是如何处理的。
说明:这种通过一个队列来协调两个线程的方法,在多线程编程中非常常见,它也属于生产者/消费者模型。
AudioCommandThread中的处理
[-->AudioPolicyService.cpp]
先看AudioSystem的setParameters。
AudioSystem将设置请求转移给AudioFlinger处理,代码如下所示:
[-->AudioFlinger.cpp]
好了,最终的请求处理在MixerThread,或者DirectPlaybackThread的线程函数中,来看:
例如MixerThread最终处理代码如下所示:
[
至此,路由设置最终通过HAL的setParameters来实现。
在某款android DTV的具体HAL实现上,
智能电视上只有三路音频通道:板载speaker,SPDIF,蓝牙,
切换到蓝牙时,走A2DP的path,这里没有深入研究。不做赘述。
切换到板载speaker或者SPDIF时,这两者在同一个HAL里。
HAL层切换的时候,根据不同的setParameters参数,打开不同的/dev/snd/pcm设备写就可以了。
(此文部分内容参考自邓凡达老师的博客。感谢邓老师讲解)
上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setForceUse
- status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config)
- {
- const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return PERMISSION_DENIED;
- return aps->setForceUse(usage, config);
- }
接下来就是调用frameworks/av/services/audioflinger/AudioPolicyService.cpp的setForceUse()函数了;
- status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
- audio_policy_forced_cfg_t config)
- {
- Mutex::Autolock _l(mLock);
- mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);
- return NO_ERROR;
- }
mpAudioPolicy->set_force_use实际上调用的是audio_policy_hal.cpp里面的ap_set_force_use。
- static void ap_set_force_use(struct audio_policy *pol,
- audio_policy_force_use_t usage,
- audio_policy_forced_cfg_t config)
- {
- struct legacy_audio_policy *lap = to_lap(pol);
- lap->apm->setForceUse((AudioSystem::force_use)usage,
- (AudioSystem::forced_config)config);
- }
继而调用AudioPolicyManagerBase::setForceUse
- void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
- {
- ....
- checkA2dpSuspend();
- checkOutputForAllStrategies();
- updateDevicesAndOutputs(); //各种内部状态的更新
- for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t output = mOutputs.keyAt(i);
- audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/);
- setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); //继续往下调用
- if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
- applyStreamVolumes(output, newDevice, 0, true);
- }
- }
- ....
- }
继续看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);
- }
[-->AudioPolicyService.cpp]
- voidAudioPolicyService::setParameters(audio_io_handle_t ioHandle,
- constString8& keyValuePairs, int delayMs)
- {
- //把这个请求加入到AudioCommandThread处理
- mAudioCommandThread->parametersCommand((int)ioHandle,
- keyValuePairs, delayMs);
- }
AudioPolicyService创建时会同时创建两个线程,其中一个用于处理各种请求。现在看看它是怎么做的。
AudioCommandThread有一个请求处理队列,AP负责往该队列中提交请求,而AudioCommandThread在它的线程函数threadLoop中处理这些命令。请直接看命令是如何处理的。
说明:这种通过一个队列来协调两个线程的方法,在多线程编程中非常常见,它也属于生产者/消费者模型。
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:
- }
- }
先看AudioSystem的setParameters。
AudioSystem将设置请求转移给AudioFlinger处理,代码如下所示:
- [-->AudioSystem.cpp]
- status_t AudioSystem::setParameters(audio_io_handle_t ioHandle,
- constString8& keyValuePairs)
- {
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- //果然是交给AF处理,ioHandle看来一定就是工作线程索引号了
- return af->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);
- }
- result = thread->setParameters(keyValuePairs);
- return result;
- }
- return BAD_VALUE;
- }
好了,最终的请求处理在MixerThread,或者DirectPlaybackThread的线程函数中,来看:
例如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来实现。
在某款android DTV的具体HAL实现上,
智能电视上只有三路音频通道:板载speaker,SPDIF,蓝牙,
切换到蓝牙时,走A2DP的path,这里没有深入研究。不做赘述。
切换到板载speaker或者SPDIF时,这两者在同一个HAL里。
HAL层切换的时候,根据不同的setParameters参数,打开不同的/dev/snd/pcm设备写就可以了。