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


上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setForceUse
status_t AudioSystem::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config){    const sp& 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;}



AP章节中,我们介绍过,mpAudioPolicy实际上是在AudioServicePolicy.cpp的构造函数中被赋值,可以理解成是指向了AP HAL层的handle
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); }



setParameters最终会调用APS的setParameters,代码如下所示:
[-->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& 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 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设备写就可以了。

更多相关文章

  1. Android(安卓)NuPlayer播放框架
  2. Android(安卓)Handler的使用yu应该注意的问题
  3. Android(安卓)- HttpURLConnection 抛出异常
  4. 关于Service的使用
  5. android 使用InstanceState保存和恢复数据
  6. Android(安卓)4.0 ICS SystemUI浅析——StatusBar加载流程分析
  7. React Native安卓实现更新下载、安装
  8. Unable to decode stream: java.io.FileNotFoundException: ****
  9. Android中将View的内容保存为图像的方法

随机推荐

  1. Android中的ANR异常如何分析又该怎么去避
  2. Android设置TextView显示指定个数字符,超
  3. Android(安卓)Handler使用
  4. Android(安卓)动态库反汇编
  5. WebView高度自适应方案探究
  6. 【Android】实现全屏、无标题栏效果
  7. android eclipse 真机调试
  8. android 中管理短信
  9. android - 为响应度而设计 - 开发文档翻
  10. Android(安卓)Service生命周期及用法