Android音频系统之AudioTrack(二)
1.1.1 AudioPolicyService的路由实现
我们在AudioPolicyService小节曾将其比作是一个“路由器”,不过还没有深入解析它是如何完成路由选择的。这部分的功能与使用者——AudioTrack有很大关联,所以我们特别将它的实现原理剖析放在这里,以使读者可以综合起来理解。
路由器功能由如下几个部分组成:
l 与发送方(AudioTrack)的接口
就好像路由器首先要接收到一个IP数据包,它才会去做路由处理,否则AudioPolicyService就成了“无源之水”了
l 与接收方(AudioFlinger)的接口
道理和上面是类似的,AudioPolicyService内部拥有当前系统中所有音频设备的信息,就好比一个路由器也需要预先知道它有多少个节点,才可能把音频数据发送到正确的终点一样
l 路由路径的选择策略
路径选择策略是AudioPolicyService的重点。和传统的路由器不同,它的路径选择算法并不是固定的,而是通过灵活的方式先产生一个策略制定者,然后再由它来生成具体的策略
大家应该还记得前面AudioTrack小节中,我们调用了AudioSystem::getOutput,即:
{…
audio_io_handle_t output =AudioSystem::getOutput(streamType, sampleRate, format, channelMask, flags);
…}
AudioSystem只是一个中介,其中的实现还是由AudioPolicyService完成的:
{
constsp<IAudioPolicyService>& aps =AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
returnaps->getOutput(stream, samplingRate, format, channels, flags);
}
显然是直接调用了AudioPolicyService的服务接口:
{ …
Mutex::Autolock _l(mLock);
return mpAudioPolicy->get_output(mpAudioPolicy,stream, samplingRate, format, channels, flags);
}
变量mpAudioPolicy便是由策略制定者“生产”出来的Policy。在原生态的实现中它代表的是legacy_audio_policy::policy@Audio_policy_hal.cpp,因而上面实际上调用的是如下函数:
{
struct legacy_audio_policy*lap = to_lap(pol);
returnlap->apm->getOutput((AudioSystem::stream_type)stream, sampling_rate,(int) format, channels,
(AudioSystem::output_flags)flags);
}
也就是说,前面的apm->getOutput的接口实现最终是落在getOutput @ AudioPolicyManagerBase(AudioPolicyManagerDefault继承自 AudioPolicyManagerBase,而后者又继承自AudioPolicyInterface)。
我们先来看下AudioPolicyManagerBase的getOutput实现。
audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_typestream,
uint32_t samplingRate, uint32_t format,
uint32_t channelMask, AudioSystem::output_flags flags)
{
audio_io_handle_t output = 0;
uint32_t latency = 0;
/*Step 1. 获取stream类型对应的Strategy*/
routing_strategy strategy= getStrategy((AudioSystem::stream_type)stream);
audio_devices_t device = getDeviceForStrategy(strategy, false/*fromCache*/);
…
/*Step 2. 应用策略,判断哪些Output符合用户传入的Stream类型*/
SortedVector<audio_io_handle_t>outputs = getOutputsForDevice(device);
/*Step 3. 选择一个最适合的Output*/
output =selectOutput(outputs, flags);
return output;
}
我们将这个函数分为三个步骤。
Step1@AudioPolicyManagerBase::getOutput. 每种Stream类型都有对应的strategy,比如AudioSystem::TTS 和AudioSystem::MUSIC对应的是STRATEGY_MEDIA,AudioSystem::NOTIFICATION对应的是 STRATEGY_SONIFICATION_RESPECTFUL。具体的对应关系如下表所示:
表格 13?5 Stream类型与Strategy对照表
STREAM_TYPE
|
STRATEGY
|
VOICE_CALL |
STRATEGY_PHONE |
BLUETOOTH_SCO |
|
RING |
STRATEGY_SONIFICATION |
ALARM |
|
NOTIFICATION |
STRATEGY_SONIFICATION_RESPECTFUL |
DTMF |
STRATEGY_DTMF |
SYSTEM |
STRATEGY_MEDIA |
TTS |
|
MUSIC |
|
ENFORCED_AUDIBLE |
STRATEGY_ENFORCED_AUDIBLE |
不同的Stream类型有可能会被划归同一个Strategy,比如TTS、MUSIC及SYSTEM类型的音频,它们在路由策略上都遵循STRATEGY_MEDIA。当然我们也可以通过重载getStrategy来按自己的要求划分Strategy。
当找到某Stream类型对应的Strategy后,接下来getDeviceForStrategy进一步为这一Strategy查找最佳匹配的音频设备(以STRATEGY_MEDIA为例):
{
uint32_t device = 0;
…
switch(strategy) {
case STRATEGY_MEDIA: {
uint32_t device2 = 0;
if (mHasA2dp&& (mForceUse[AudioSystem::FOR_MEDIA] !=
AudioSystem::FORCE_NO_BT_A2DP)&& (getA2dpOutput() != 0) && !mA2dpSuspended) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
}
…
device |= device2;
if (device) break;
device =mDefaultOutputDevice;
if (device == 0) {
ALOGE("getDeviceForStrategy() no device found forSTRATEGY_MEDIA");
}
} break;
…
上面的代码看上去很长,但逻辑比较简单——按照一定的优先级来匹配系统中已经存在的音频设备。这个优先级的设定因Strategy不同而有所差异。在STRATEGY_MEDIA这种情况下,其优先级如下所示:
² 在有蓝牙A2dp的平台上,且设备可以正常打开,没有挂起,当前也没有强制不使用A2dp,那么通过匹配mAvailableOutputDevices来寻找合适的A2dp设备,比如A2dp_headphone、A2dp_Speaker
² 要注意的是,只有在上一步匹配失败(即找不到合适的设备,变量device2为0)的情况下,才会继续执行下一优先级的判断。这里处于第二等级的是wired headphone
² 继续寻找是否有wired headset
² 寻找是否有usb accessory
² 寻找是否有usb device
等等。。。
正常情况下getDeviceForStrategy都能获得符合要求的device。我们再回到前面的getOutput,看下接下来步骤的执行。
Step2@AudioPolicyManagerBase::getOutput
{
SortedVector<audio_io_handle_t> outputs;
for (size_t i = 0; i <mOutputs.size(); i++) {
if ((device &mOutputs.valueAt(i)->supportedDevices()) == device) {
outputs.add(mOutputs.keyAt(i));
}
}
return outputs;
}
这个函数用于获得所有支持device设备的Output,并添加到outputs中。Output是AudioFlinger::openOutput 得到的结果,AudioPolicyService会把它们存储到mOutputs键值对中。因为每个Output通常都支持若干种音频设备,不同的 Output支持的音频设备类型也是不限的,所以系统中很可能存在多个支持device的Output。
Step 3@AudioPolicyManagerBase::getOutput. 到目前为止,符合要求的Output可能不止一个,所以要选择一个最适合的。
AudioSystem::output_flags flags)
{
/*Step 1. 处理一些特殊情况*/
if (outputs.size() == 0) {
return 0;
}
if (outputs.size() == 1) {
return outputs[0];
}
先处理一些特殊情况,比如没有任何output存在的情况下只能返回空;同样的如果只有一个output的情况也没得选择,直接返回该output。
int maxCommonFlags = 0;
audio_io_handle_toutputFlags = 0;
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i <outputs.size(); i++) {
AudioOutputDescriptor*outputDesc = mOutputs.valueFor(outputs[i]);
if(!outputDesc->isDuplicated()) {
int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
if (commonFlags > maxCommonFlags) {
outputFlags =outputs[i];
maxCommonFlags= commonFlags;
ALOGV("selectOutput() commonFlags foroutput %d, %04x", outputs[i], commonFlags);
}
if(outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
outputPrimary= outputs[i];
}
}
}
这个循环是整个函数的核心,我们来分析下它的决策准绳是什么。大家只要仔细看下循环中的判断语句,就可以发现它实际上是在寻找最大值maxCommonFlags,所以问题就转化为,什么东西的最大值?
int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
上面这句代码用通俗的话来讲,就是计算outputDesc->mProfile->mFlags与入参flags的相似度有多大。可选的Flags如下所示:
AUDIO_OUTPUT_FLAG_NONE = 0x0,
AUDIO_OUTPUT_FLAG_DIRECT = 0x1, //output直接把track导向一个output stream,没有混音器
AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, //primary output,它是唯一的并且必需存在
AUDIO_OUTPUT_FLAG_FAST = 0x4, //支持fasttracks的output
AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 //使用deepaudio buffer的output
音频系统中名为output_flags的数据类型非常多,不过仔细回溯的话,可以发现这个flags是在AudioTrack的set函数中指定的。另外,如果在查找过程中发现primaryoutput,则用outputPrimary表示,这在后面会用到。
if (outputFlags != 0) {
return outputFlags;
}
if (outputPrimary != 0) {
return outputPrimary;
}
return outputs[0];
}
优先级的排列很简单,即:
² Flags与要求相似度高的output
² Primaryoutput
² 如果上面两种都找不到,则默认返回第一个output
这样子AudioPolicyService就完成了整个路由路径的选择,AudioTrack则是通过AudioSystem::getOutput间接调用到AudioPolicyService的这一功能。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。