Android音频通路的切换(转)java
转自大神博客android
Android支持多种设备的的输出。一台正常的机子,自己就自带话筒,扬声器,麦克风等多个声音输入输出设备,再加上五花八门的外置设备(经过耳机,蓝牙,wifi等方式链接),使声音的输出更具多样性。Android支持如此多的设备链接,那么android内部是怎样对设备的输出输出进行控制的呢?这一次咱们主要来看看音频通路的切换。c++
音频流、设备、音频策略
要想知道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++层均有定义。而且对应的值是保持一致的。session

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

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

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

咱们经过查看当前的声卡状况肯定了当前具体的mixer_path文件——mixer_path_skue.xml。xml文件内部就是咱们预约义的usecase的具体状况:ui

在mixer_path类文件中,一个标准的path就如上面的红框那样。有名字,有必定的参数。另外,一个patch之中,还能够嵌套另一个patch。this
因为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播放的例子以下:
- AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 11025/2,
- AudioFormat.CHANNEL_CONFIGURATION_MONO,
- AudioFormat.ENCODING_PCM_16BIT,
- audioLength, AudioTrack.MODE_STREAM);
- audioTrack.play();
- audioTrack.write(audioData, 0, sizeInBytes);
AudioTrack在接收参数建立的时候,就会将设置的steamtype保存在对应的AudioAttributes当中(AudioAttributes是一个描述关于音频流的信息的属性集合的类)。
咱们知道,在android系统中,系统封装的对象是一层一层往下调用的。因此,在咱们建立了一个java的AudioTrack对象的时候,其实在同时,在C++当中,咱们已经作了不少操做了。下面咱们来看一下,AudioTrack对象建立时,主要作了什么:
- static jint
- android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jaa,
- jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
- jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
-
-
-
- sp<AudioTrack> lpTrack = new AudioTrack();
-
-
-
- status_t status = NO_ERROR;
- switch (memoryMode) {
- case MODE_STREAM:
-
- status = lpTrack->set(
- AUDIO_STREAM_DEFAULT,
- sampleRateInHertz,
- format,
- nativeChannelMask,
- frameCount,
- AUDIO_OUTPUT_FLAG_NONE,
- audioCallback, &(lpJniStorage->mCallbackData),
- 0,
- 0,
- true,
- sessionId,
- AudioTrack::TRANSFER_SYNC,
- NULL,
- -1, -1,
- paa);
- break;
-
- if (status != NO_ERROR) {
- ALOGE("Error %d initializing AudioTrack", status);
- goto native_init_failure;
- }
-
-
- setAudioTrack(env, thiz, lpTrack);
-
-
- }
从上面的代码能够看出,在建立java层的AudioTrack对象时,对应的jni也建立出一个C++的AudioTrack对象,而且传入了部分参数和调用了其方法。
接下来咱们来看看C++的AudioTrack对象的构造方法:
- AudioTrack::AudioTrack()
- : mStatus(NO_INIT),
- mIsTimed(false),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mPlaybackRateSet(false)
- {
- mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
- mAttributes.usage = AUDIO_USAGE_UNKNOWN;
- mAttributes.flags = 0x0;
- strcpy(mAttributes.tags, "");
- }
咱们能够看到,AudioTrack的无参构造方法只是进行了一些参数的初始化,那么,具体是AudioTrack初始化是进行在哪里呢?
咱们再回到上面,发现jni层在建立完AudioTrack对象后,根据memoryMode的不一样而进行了不一样的AudioTrack->set()操做,只是由于AudioTrack提供2种不一样的输出方式(对内存的影响和要求不一样)。我来看看看set中主要的操做:
- status_t AudioTrack::set(…){
-
- status_t status = createTrack_l();
- if (status != NO_ERROR) {
- if (mAudioTrackThread != 0) {
- mAudioTrackThread->requestExit();
- mAudioTrackThread->requestExitAndWait();
- mAudioTrackThread.clear();
- }
- return status;
-
- }
在AudioTrack的set()中,除了部分的参数判断和设置以外,咱们能够看到,他调用了自身的createTrack_l()进行了进一步的设置。
- status_t AudioTrack::createTrack_l()
- {
- const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
- if (audioFlinger == 0) {
- ALOGE("Could not get audioflinger");
- return NO_INIT;
- }
-
- audio_io_handle_t output;
- audio_stream_type_t streamType = mStreamType;
- audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
-
- audio_offload_info_t tOffloadInfo = AUDIO_INFO_INITIALIZER;
- if (mPlaybackRateSet == true && mOffloadInfo == NULL && mFormat == AUDIO_FORMAT_PCM_16_BIT) {
- mOffloadInfo = &tOffloadInfo;
- }
- status_t status = AudioSystem::getOutputForAttr(attr, &output,
- (audio_session_t)mSessionId, &streamType, mClientUid,
- mSampleRate, mFormat, mChannelMask,
- mFlags, mSelectedDeviceId, mOffloadInfo);
-
-
- IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
-
- sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
- mSampleRate,
- mFormat,
- mChannelMask,
- &temp,
- &trackFlags,
- mSharedBuffer,
- output,
- tid,
- &mSessionId,
- mClientUid,
- &status);
-
上面的代码能够看出,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():
- status_t AudioSystem::getOutputForAttr()
- {
- const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- if (aps == 0) return NO_INIT;
- return aps->getOutputForAttr(attr, output, session, stream, uid,
- samplingRate, format, channelMask,
- flags, selectedDeviceId, offloadInfo);
- }
从上面咱们能够看到,AudioSystem只是做为一个过渡,而后经过获取AudioPolicyService的句柄去getOutputForAttr()。咱们继续跟踪AudioPolicyService的状况,会发现其实他只是在AudioPolicyService中也只是做为一个过渡,真正进行getOutputForAttr()的,在AudioPolicyManager之中。
- status_t AudioPolicyManager::getOutputForAttr()
- {
-
- *stream = streamTypefromAttributesInt(&attributes);
- sp<DeviceDescriptor> deviceDesc;
- for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
- if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {
- deviceDesc = mAvailableOutputDevices[i];
- break;
- }
- }
- mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
-
-
- routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
- audio_devices_t device = getDeviceForStrategy(strategy, false
-
- if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
- flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
- }
-
- *output = getOutputForDevice(device, session, *stream,
- samplingRate, format, channelMask,
- flags, offloadInfo);
-
在AudioPolicyManager的getOutputForAttr()中,咱们能够发现关键点在strategy的获取与device的获取当中。而在这当中,关键的参数偏偏是在先前从java层一步一步封装的过来的attributes。咱们先来简单看一下attributes这个参数的数据结构:

从audio_attributes_t的结构咱们能够看出,audio_attributes_t保存着须要输出音频的应用的相关配置信息。
而后,根据刚刚的代码,咱们来了解一下strategy的获取:
- uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
-
- if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
- return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
- }
- if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
- return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
- }
-
- return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage));
虽然在这里,会先对flags参数进行比较,可是,在实际上flags大部分时候都是0。因此最后,都是根据“mEngine->getStrategyForUsage(attr->usage)”去选择StrategyForUsage。固然,再到下一步就到了就是switch和case的过程,这里就不继续展开了。
在获取到strategy以后,咱们来看看Audio接着是怎么来肯定device的。
先继续看AudioPolicyManager的getDeviceForStrategy():
- audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
- bool fromCache)
- {
-
-
-
-
-
-
-
- for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
- sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
- routing_strategy strat = getStrategy(route->mStreamType);
- bool strategyMatch = (strat == strategy) ||
- ((strategy == STRATEGY_ACCESSIBILITY) &&
- ((mEngine->getStrategyForUsage(
- AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY) == strat) ||
- (strat == STRATEGY_MEDIA)));
- if (strategyMatch && route->isActive()) {
- return route->mDeviceDescriptor->type();
- }
- }
- if (fromCache) {
- ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
- strategy, mDeviceForStrategy[strategy]);
- return mDeviceForStrategy[strategy];
- }
- return mEngine->getDeviceForStrategy(strategy);
- }
调用AudioPolicyManager的getDeviceForStrategy()的时候,通常会先查下一下当前的RouteMap,看看有没有匹配的状况的。但因为咱们新申请一个output的时候,传入的参数是false,因此这个时候,是会直接经过mEngine去直接获取device。
而在mEngine中,getDeviceForStrategy()又是一堆的选择判断,而后返回设备:
- audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
- {
- const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
- const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
-
- const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
-
- uint32_t device = AUDIO_DEVICE_NONE;
- uint32_t availableOutputDevicesType = availableOutputDevices.types();
-
- switch (strategy) {
-
- case STRATEGY_MEDIA: {
- uint32_t device2 = AUDIO_DEVICE_NONE;
-
- if (isInCall() && (device == AUDIO_DEVICE_NONE)) {
-
- device = getDeviceForStrategy(STRATEGY_PHONE);
- break;
- }
-
- if (strategy != STRATEGY_SONIFICATION) {
-
- if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) {
- device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
- }
- }
- if (isInCall() && (strategy == STRATEGY_MEDIA)) {
- device = getDeviceForStrategy(STRATEGY_PHONE);
- break;
- }
- if ((device2 == AUDIO_DEVICE_NONE) &&
- (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
- (outputs.getA2dpOutput() != 0)) {
- device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
- if (device2 == AUDIO_DEVICE_NONE) {
- device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
- }
-
- ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
- return device;
- }
咱们就其中一个strategty(STRATEGY_MEDIA)来具体看看Audio系统的选择输出设备:
1) 首先咱们会获取当前存在的设备集合availableOutputDevices
2) 而后根据传入的strategty类型进行匹配选择
3) 在选择以前会先检测是否处于特殊状况下(如通话中)
4) 最后按照优先级匹配设备。
而后就这样,选择设备的流程就此结束。简单来讲,选择设备的流程,主要是几个参数一步一步去肯定而后最后肯定合适的设备。具体选择设备的简单流程如图:
