Android支持多种设备的的输出。一台正常的机子,本身就自带话筒,扬声器,麦克风等多个声音输入输出设备,再加上五花八门的外置设备(通过耳机,蓝牙,wifi等方式连接),使声音的输出更具多样性。Android支持如此多的设备连接,那么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的具体情况:
由于usecase只是目前高通hal层独有的定义,所以本文不会花太多时间和精力去探讨usecase的相关设置和内容。目前来说,对这个有一定的认知就可。
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);
我们知道,在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) { //…… // create the native AudioTrack object sp<AudioTrack> lpTrack = new AudioTrack(); //…… // initialize the native AudioTrack object 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; } // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field // of the Java object (in mNativeTrackInJavaObj) setAudioTrack(env, thiz, lpTrack); //…… }
接下来我们来看看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(); // see comment in AudioTrack.h 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的性质,Java层在创建完成AudioTrack对象直接调用play()和write()操作,那么其实从另一方面我们可以猜想,在Java层创建完成AudioTrack之后,系统已经设置好输出的设备等等操作,只等调用play()和write方法进行播放。所以为了验证我们的猜想,接下来针对AudioFlinger&AudioSystem的相关具体分析验证。
回到上面的内容,我们可以看到,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); //根据strategy获取device routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); 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); //……
从audio_attributes_t的结构我们可以看出,audio_attributes_t保存着需要输出音频的应用的相关配置信息。
然后,根据刚刚的代码,我们来了解一下strategy的获取:uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { // flags to strategy mapping 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; } // usage to strategy mapping 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) { // Routing // see if we have an explicit route // scan the whole RouteMap, for each entry, convert the stream type to a strategy // (getStrategy(stream)). // if the strategy from the stream type in the RouteMap is the same as the argument above, // and activity count is non-zero // the device = the device from the descriptor in the RouteMap, and exit. 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)) { // when in call, get the device for Phone strategy device = getDeviceForStrategy(STRATEGY_PHONE); break; } if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) 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) 最后按照优先级匹配设备。
然后就这样,选择设备的流程就此结束。简单来说,选择设备的流程,主要是几个参数一步一步去确定然后最后确定合适的设备。具体选择设备的简单流程如图:
针对音频通路的切换,我们就简单聊到这里。谢谢。
原文:http://blog.csdn.net/u012440406/article/details/54883220