Android音频通路的切换

 

Android音频通路的切换(转)

转自大神博客

 

Android支持多种设备的的输出。一台正常的机子,本身就自带话筒,扬声器,麦克风等多个声音输入输出设备,再加上五花八门的外置设备(通过耳机,蓝牙,wifi等方式连接),使声音的输出更具多样性。Android支持如此多的设备连接,那么android内部是怎样对设备的输出输出进行控制的呢?这一次我们主要来看看音频通路的切换。

 

音频流、设备、音频策略

 

要想知道Andorid是怎样对设备的输出输出进行控制的,我们首先来了解一些音频相关的基本知识: stream_type、content_type、devices、routing_strategy。
stream_type:音频流的类型。在当前系统中,Android(6.0)一共定义了11种stream_type以供开发者使用。Android上层开发要想要发出声音,都必须先确定当前当前的音频类型。
content_type:具体输出类型。虽然当前一共有11种stream_type,但一旦进入到Attribute,Android就只将其整理成几种类型。这才是实际的类型。
device:音频输入输出设备。Android定义了多种设备输入输出设备(具体物理设备可能还是那几个,但是输出场景不尽相同)。
routing_strategy:音频路由策略。默认情况下,Android是根据路由策略去选择设备负责输出输入音频的。

 

stream_type在android中Java层与c++层均有定义。并且对应的值是保持一致的。

 

device与stream_type一样,在java层和C++层均有定义,并且会根据使用的情况不同蕴含多个定义:

 

相对stream_type,routing_strategy只是一个定义在RountingStrategy.h的一个简单的枚举结构体:



usecase只是qcom内部定义的一个数据结构,位于hal层,用作处理处理内部声卡逻辑和输出方案。输出方案与声卡中的mixer_path_xxx.xml相联。而mixer_path等相关文件,才是具体的音频输出方案。



 

我们通过查看当前的声卡情况确定了当前具体的mixer_path文件——mixer_path_skue.xml。xml文件内部就是我们预定义的usecase的具体情况:


在mixer_path类文件中,一个标准的path就如上面的红框那样。有名字,有一定的参数。另外,一个patch之中,还可以嵌套另外一个patch。

由于usecase只是目前高通hal层独有的定义,所以本文不会花太多时间和精力去探讨usecase的相关设置和内容。目前来说,对这个有一定的认知就可。

 

AudioPolicy和AudioPolicyService

在了解完Audio一些基本的定义设定之后,我们来看一下Android的Audio整体架构。
Audio内部系统从上到下包含各方面的东西。对于声音输出的设备的选择与切换,我们主要需要关注2个地方。第一处,是外接设备如耳机,蓝牙设备等连接的通知。第二处就是Audio系统中核心的AudioFinger与AudioPolicyService的处理内容。
AudioFinger是Audio系统的工作引擎,管理者系统中输入输出音频流,并承担音频数据混音,以及读写Audio硬件等工作以实现数据的输入输出功能。AudioPolicyService是Audio系统策略控制中心,具体负责掌管系统中声音设备的选择和切换,音量控制等功能。
AudioFinger与AudioPolicyService的类图关系:



在AudioFlinger和AudioPolicyService的运作中其实包含着很多类,但同时,我们也可以发现,在Audio系统中, AudioFinger与AudioPolicyService是紧密相连的。总得来说,AudioFinger与AudioPolicyService是Audio系统的核心。所以下面我们很多内容的主角,都是他们2个。
基本的声音输出调用
发出声音是Android机器的一个最基本的功能。但是,Android是怎么发出声音的呢?就算不连接外设,Android最基本还有听筒和扬声器2个设备。那么,Android内部,是怎么控制他们2个发出声音的呢?下面我们来具体看 一下Android一般情况下发出声音时选择设备的过程。
我们要想分析Android中的声音输出,当然是先通过播放音频去一步一步了解Android是怎恶魔输出声音的。下面我们以一个最简单的AudioTrack播放音频为例,来看下Android的发生过程。
一个简单的AudioTrack播放的例子如下:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 11025/2,   
  2.                                 AudioFormat.CHANNEL_CONFIGURATION_MONO,   
  3.                                 AudioFormat.ENCODING_PCM_16BIT,  
  4.                                 audioLength, AudioTrack.MODE_STREAM);  
  5. audioTrack.play();  
  6. audioTrack.write(audioData, 0, sizeInBytes);  



AudioTrack在接收参数创建的时候,就会将设置的steamtype保存在对应的AudioAttributes当中(AudioAttributes是一个描述关于音频流的信息的属性集合的类)。
 

我们知道,在android系统中,系统封装的对象是一层一层往下调用的。所以,在我们创建了一个java的AudioTrack对象的时候,其实在同时,在C++当中,我们已经做了很多操作了。下面我们来看一下,AudioTrack对象创建时,主要做了什么:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static jint  
  2. android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,  
  3.         jobject jaa,  
  4.         jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,  
  5.         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {  
  6.   
  7.     //……  
  8.     // create the native AudioTrack object  
  9.     sp<AudioTrack> lpTrack = new AudioTrack();  
  10.   
  11.     //……  
  12.     // initialize the native AudioTrack object  
  13.     status_t status = NO_ERROR;  
  14.     switch (memoryMode) {  
  15.     case MODE_STREAM:  
  16.   
  17.         status = lpTrack->set(  
  18.                 AUDIO_STREAM_DEFAULT,  
  19.                 sampleRateInHertz,  
  20.                 format,  
  21.                 nativeChannelMask,  
  22.                 frameCount,  
  23.                 AUDIO_OUTPUT_FLAG_NONE,  
  24.                 audioCallback, &(lpJniStorage->mCallbackData),  
  25.                 0,  
  26.                 0,  
  27.                 true,  
  28.                 sessionId,  
  29.                 AudioTrack::TRANSFER_SYNC,  
  30.                 NULL,                           
  31.                 -1, -1,                         
  32.                 paa);  
  33.         break;  
  34.     //……  
  35.     if (status != NO_ERROR) {  
  36.         ALOGE("Error %d initializing AudioTrack", status);  
  37.         goto native_init_failure;  
  38.     }  
  39.     // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field  
  40.     // of the Java object (in mNativeTrackInJavaObj)  
  41.     setAudioTrack(env, thiz, lpTrack);  
  42.    //……  
  43.   
  44. }  



 

从上面的代码可以看出,在创建java层的AudioTrack对象时,对应的jni也创建出一个C++的AudioTrack对象,并且传入了部分参数和调用了其方法。

接下来我们来看看C++的AudioTrack对象的构造方法:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. AudioTrack::AudioTrack()  
  2.     : mStatus(NO_INIT),  
  3.       mIsTimed(false),  
  4.       mPreviousPriority(ANDROID_PRIORITY_NORMAL),  
  5.       mPreviousSchedulingGroup(SP_DEFAULT),  
  6.       mPausedPosition(0),  
  7.       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),  
  8.       mPlaybackRateSet(false)  
  9. {  
  10.     mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;  
  11.     mAttributes.usage = AUDIO_USAGE_UNKNOWN;  
  12.     mAttributes.flags = 0x0;  
  13.     strcpy(mAttributes.tags, "");  
  14. }  

 

我们可以看到,AudioTrack的无参构造方法只是进行了一些参数的初始化,那么,具体是AudioTrack初始化是进行在哪里呢?

我们再回到上面,发现jni层在创建完AudioTrack对象后,根据memoryMode的不同而进行了不同的AudioTrack->set()操作,只是因为AudioTrack提供2种不同的输出方式(对内存的影响和要求不同)。我来看看看set中主要的操作:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. status_t AudioTrack::set(…){  
  2.     //……  
  3.     status_t status = createTrack_l();  
  4.     if (status != NO_ERROR) {  
  5.         if (mAudioTrackThread != 0) {  
  6.             mAudioTrackThread->requestExit();   // see comment in AudioTrack.h  
  7.             mAudioTrackThread->requestExitAndWait();  
  8.             mAudioTrackThread.clear();  
  9.         }  
  10.         return status;  
  11.    //……  
  12.     }  

 

在AudioTrack的set()中,除了部分的参数判断和设置之外,我们可以看到,他调用了自身的createTrack_l()进行了进一步的设置。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. status_t AudioTrack::createTrack_l()  
  2. {  
  3.     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();  
  4.     if (audioFlinger == 0) {  
  5.         ALOGE("Could not get audioflinger");  
  6.         return NO_INIT;  
  7.     }  
  8.   
  9.     audio_io_handle_t output;  
  10.     audio_stream_type_t streamType = mStreamType;  
  11.     audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;  
  12.     //……  
  13.     audio_offload_info_t tOffloadInfo = AUDIO_INFO_INITIALIZER;  
  14.     if (mPlaybackRateSet == true && mOffloadInfo == NULL && mFormat == AUDIO_FORMAT_PCM_16_BIT) {  
  15.         mOffloadInfo = &tOffloadInfo;  
  16.     }  
  17.     status_t status = AudioSystem::getOutputForAttr(attr, &output,  
  18.                                            (audio_session_t)mSessionId, &streamType, mClientUid,  
  19.                                            mSampleRate, mFormat, mChannelMask,  
  20.                                            mFlags, mSelectedDeviceId, mOffloadInfo);  
  21.   
  22.     //……  
  23.     IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;  
  24.     //……  
  25.     sp<IAudioTrack> track = audioFlinger->createTrack(streamType,  
  26.                                                       mSampleRate,  
  27.                                                       mFormat,  
  28.                                                       mChannelMask,  
  29.                                                       &temp,  
  30.                                                       &trackFlags,  
  31.                                                       mSharedBuffer,  
  32.                                                       output,  
  33.                                                       tid,  
  34.                                                       &mSessionId,  
  35.                                                       mClientUid,  
  36.                                                       &status);  
  37.    //……  



 

上面的代码可以看出,AudioTrack从这里开始,与AudioFlinger等进行大量的交互:获取句柄,获取输出,创建IAudioTrack指针对象等等。所以接下来,就是AudioFlinger的相关内容了。在这里,我们先简单总结下AudioTrack的创建过程:

根据AudioTrack的性质,Java层在创建完成AudioTrack对象直接调用play()和write()操作,那么其实从另一方面我们可以猜想,在Java层创建完成AudioTrack之后,系统已经设置好输出的设备等等操作,只等调用play()和write方法进行播放。所以为了验证我们的猜想,接下来针对AudioFlinger&AudioSystem的相关具体分析验证。

 

AudioFlinger&AudioPolicyService的控制过程

回到上面的内容,我们可以看到,AudioTrack在调用createTrack_l()的方法的时候,开始通过AudioSystem获取output。所以下面我们来看看AudioSystem的getOutputForAttr():

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. status_t AudioSystem::getOutputForAttr()  
  2. {  
  3.     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();  
  4.     if (aps == 0) return NO_INIT;  
  5.     return aps->getOutputForAttr(attr, output, session, stream, uid,  
  6.                                  samplingRate, format, channelMask,  
  7.                                  flags, selectedDeviceId, offloadInfo);  
  8. }  

 

从上面我们可以看到,AudioSystem只是作为一个过渡,然后通过获取AudioPolicyService的句柄去getOutputForAttr()。我们继续跟踪AudioPolicyService的情况,会发现其实他只是在AudioPolicyService中也只是作为一个过渡,真正进行getOutputForAttr()的,在AudioPolicyManager之中。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. status_t AudioPolicyManager::getOutputForAttr()  
  2. {  
  3.     //……  
  4.     *stream = streamTypefromAttributesInt(&attributes);  
  5.     sp<DeviceDescriptor> deviceDesc;  
  6.     for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {  
  7.         if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {  
  8.             deviceDesc = mAvailableOutputDevices[i];  
  9.             break;  
  10.         }  
  11.     }  
  12.     mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);  
  13.       
  14.     //根据strategy获取device  
  15.     routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);  
  16.     audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);  
  17.   
  18.     if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {  
  19.         flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);  
  20.     }  
  21.   
  22.     *output = getOutputForDevice(device, session, *stream,  
  23.                                  samplingRate, format, channelMask,  
  24.                                  flags, offloadInfo);  
  25.     //……  

 

 

在AudioPolicyManager的getOutputForAttr()中,我们可以发现关键点在strategy的获取与device的获取当中。而在这当中,关键的参数恰恰是在先前从java层一步一步封装的过来的attributes。我们先来简单看一下attributes这个参数的数据结构:
 

 

从audio_attributes_t的结构我们可以看出,audio_attributes_t保存着需要输出音频的应用的相关配置信息。

然后,根据刚刚的代码,我们来了解一下strategy的获取:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {  
  2.     // flags to strategy mapping  
  3.     if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {  
  4.         return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;  
  5.     }  
  6.     if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {  
  7.         return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;  
  8.     }  
  9.     // usage to strategy mapping  
  10.     return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage));  

 

 

虽然在这里,会先对flags参数进行比较,但是,在实际上flags大部分时候都是0。所以最后,都是根据“mEngine->getStrategyForUsage(attr->usage)”去选择StrategyForUsage。当然,再到下一步就到了就是switch和case的过程,这里就不继续展开了。

在获取到strategy之后,我们来看看Audio接着是怎么来确定device的。

先继续看AudioPolicyManager的getDeviceForStrategy():

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,  
  2.                                                          bool fromCache)  
  3. {  
  4.     // Routing  
  5.     // see if we have an explicit route  
  6.     // scan the whole RouteMap, for each entry, convert the stream type to a strategy  
  7.     // (getStrategy(stream)).  
  8.     // if the strategy from the stream type in the RouteMap is the same as the argument above,  
  9.     // and activity count is non-zero  
  10.     // the device = the device from the descriptor in the RouteMap, and exit.  
  11.     for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {  
  12.         sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);  
  13.         routing_strategy strat = getStrategy(route->mStreamType);  
  14.         bool strategyMatch = (strat == strategy) ||  
  15.                              ((strategy == STRATEGY_ACCESSIBILITY) &&  
  16.                               ((mEngine->getStrategyForUsage(  
  17.                                       AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY) == strat) ||  
  18.                                (strat == STRATEGY_MEDIA)));  
  19.         if (strategyMatch && route->isActive()) {  
  20.             return route->mDeviceDescriptor->type();  
  21.         }  
  22.     }  
  23.     if (fromCache) {  
  24.         ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",  
  25.               strategy, mDeviceForStrategy[strategy]);  
  26.         return mDeviceForStrategy[strategy];  
  27.     }  
  28.     return mEngine->getDeviceForStrategy(strategy);  
  29. }  

 

调用AudioPolicyManager的getDeviceForStrategy()的时候,一般会先查下一下当前的RouteMap,看看有没有匹配的情况的。但由于我们新申请一个output的时候,传入的参数是false,所以这个时候,是会直接通过mEngine去直接获取device。

而在mEngine中,getDeviceForStrategy()又是一堆的选择判断,然后返回设备:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const  
  2. {  
  3.     const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();  
  4.     const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();  
  5.   
  6.     const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();  
  7.   
  8.     uint32_t device = AUDIO_DEVICE_NONE;  
  9.     uint32_t availableOutputDevicesType = availableOutputDevices.types();  
  10.   
  11.     switch (strategy) {  
  12.     //……  
  13.     case STRATEGY_MEDIA: {  
  14.         uint32_t device2 = AUDIO_DEVICE_NONE;  
  15.   
  16.         if (isInCall() && (device == AUDIO_DEVICE_NONE)) {  
  17.             // when in call, get the device for Phone strategy  
  18.             device = getDeviceForStrategy(STRATEGY_PHONE);  
  19.             break;  
  20.         }  
  21.   
  22.         if (strategy != STRATEGY_SONIFICATION) {  
  23.             // no sonification on remote submix (e.g. WFD)  
  24.             if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) {  
  25.                 device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;  
  26.             }  
  27.         }  
  28.         if (isInCall() && (strategy == STRATEGY_MEDIA)) {  
  29.             device = getDeviceForStrategy(STRATEGY_PHONE);  
  30.             break;  
  31.         }  
  32.         if ((device2 == AUDIO_DEVICE_NONE) &&  
  33.                 (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&  
  34.                 (outputs.getA2dpOutput() != 0)) {  
  35.             device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;  
  36.             if (device2 == AUDIO_DEVICE_NONE) {  
  37.                 device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;  
  38.             }  
  39.            //……  
  40.     ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device);  
  41.     return device;  
  42. }  

我们就其中一个strategty(STRATEGY_MEDIA)来具体看看Audio系统的选择输出设备:

1)  首先我们会获取当前存在的设备集合availableOutputDevices

2)  然后根据传入的strategty类型进行匹配选择

3)  在选择之前会先检测是否处于特殊情况下(如通话中)

4)  最后按照优先级匹配设备。

然后就这样,选择设备的流程就此结束。简单来说,选择设备的流程,主要是几个参数一步一步去确定然后最后确定合适的设备。具体选择设备的简单流程如图:

posted on 2017-02-09 00:33  信假名如  阅读(12311)  评论(0编辑  收藏  举报

导航