Android 在O之后增加了CarAudio,增加了多音区,增加了动态路由,而对于Audio的三大块AudioTrack、AudioFlinger和AudioPolicy。CarAudio主要解决了车载上的AudioPolicy策略。我们之前分析了car_audio_configuration.xml的解析,以及解析后如何构建路由策略和多音区的AudioFocus,今天继续分析。解析后的路由策略是如何注册到AudioPlolicy中,以及如何应用在我们系统中的。
在整个CarAudio启动时,核心初始化的就是setupDynamicRouting()函数,我们七七八八也分析的差不多了,最后分析下解析完的xml,做好的路由策略是如何注册下去的,还是先看下时序图,首先是构建AudioPolcy的时序:
我们直接进CarAudioDynamicRouting里看下:
static void setupAudioDynamicRouting(AudioPolicy.Builder builder,
SparseArray<CarAudioZone> carAudioZones) {
for (int i = 0; i < carAudioZones.size(); i++) {
CarAudioZone zone = carAudioZones.valueAt(i);
for (CarVolumeGroup group : zone.getVolumeGroups()) {
setupAudioDynamicRoutingForGroup(group, builder);
}
}
}
这个函数没啥说的,比较简单,我们举例来说明下吧,在Android R- CarAudioService之registerAudioPolicy动态注册(一)中,我们知道有carAudioZones有3个,这里以primary zone举例,他有四个group。继续看下setupAudioDynamicRoutingForGroup
private static void setupAudioDynamicRoutingForGroup(CarVolumeGroup group,
AudioPolicy.Builder builder) {
// Note that one can not register audio mix for same bus more than once.
for (String address : group.getAddresses()) {
boolean hasContext = false;
CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForAddress(address);
AudioFormat mixFormat = new AudioFormat.Builder()
.setSampleRate(info.getSampleRate())
.setEncoding(info.getEncodingFormat())
.setChannelMask(info.getChannelCount())
.build();
AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
for (int carAudioContext : group.getContextsForAddress(address)) {
hasContext = true;
int[] usages = CarAudioContext.getUsagesForContext(carAudioContext);
for (int usage : usages) {
AudioAttributes attributes = buildAttributesWithUsage(usage);
mixingRuleBuilder.addRule(attributes,
AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
}
if (Log.isLoggable(CarLog.TAG_AUDIO, Log.DEBUG)) {
Log.d(CarLog.TAG_AUDIO, String.format(
"Address: %s AudioContext: %s sampleRate: %d channels: %d usages: %s",
address, carAudioContext, info.getSampleRate(), info.getChannelCount(),
Arrays.toString(usages)));
}
}
if (hasContext) {
// It's a valid case that an audio output address is defined in
// audio_policy_configuration and no context is assigned to it.
// In such case, do not build a policy mix with zero rules.
AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
.setFormat(mixFormat)
.setDevice(info.getAudioDeviceInfo())
.setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
.build();
builder.addMix(audioMix);
}
}
}
代码比较长,但很重要,先看第一个for循环即 (String address : group.getAddresses()) ,我们知道每个group都会至少有一个bus,每个bus都会有一个addrss。比如还是在Android R- CarAudioService之registerAudioPolicy动态注册(一)中我们的primary zone中group0中有bus0、bus3、bus6和bus7,group1中有bus1和bus2,group2中有bus4,group3中有bus5.这里取出每个bus中一些配置信息,这些bus的信息是来自audio_policy_configuration.xml中的,截取一段bus0的如下:
<devicePorts>
<devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
address="bus0_media_out">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
gains>
devicePort>
然后构建AudioFormat的SampleRate、Encoding、ChannelMask,如上图就是48000、AUDIO_FORMAT_PCM_16_BIT、和AUDIO_CHANNEL_OUT_STEREO。
接下来又是一个for循环,因为每个bus下都至少有一个carAudioContext ,例如primary zone中的bus0下有个carAudioContext Music,而rear seat zone1和 rear seat zone2中每个bus下都是很多context的。这里的第二个for循环就是遍历每个bus下的carAudioContext ,为什么要遍历carAudioContext呢?其实最主要的就是找AudioAttributes的Usage,我们在Android8.0之前的版本AudioPolicyManager的路由策略都是根据播放AudioTrack或者MediaPlayer的StreamType来处理的。但是CarAudio的动态路由是根据Usage来处理的。还是看下在Android R- CarAudioService之registerAudioPolicy动态注册(一)中,每个carAudioContext下都有至少1个usage,这里就是要找出所有的usage。然后加到AudioMixingRule中,addRule的过程就不说了,他们最终都被放到AudioMixMatchCriterion集合中了,AudioMixMatchCriterion有3个属性其中Rule是RULE_MATCH_ATTRIBUTE_USAGE,IntProp是 Integer.MIN_VALUE,以及传入的AudioAttributes,还有他们的mTargetMixType 是AudioMix.MIX_TYPE_PLAYERS。
最后把这个 AudioMixingRule添加到AudioMix中,具体代码也不细说了,再记住一个属性mRouteFlags是AudioMix.ROUTE_FLAG_RENDER 。这些后面会用到。
到这里我们来总结下,解析完成XMl后便进入到 CarAudioDynamicRouting中来构建动态路由策略,大体步骤如下:
1.循环处理Zones下的每个zone
1.1循环处理zone下的每个volumegroup
1.1.1循环处理volumegroup下的每个bus,同时每个bus对应创建了一个AudioMixingRule
1.1.1.1循环处理bus下的每个caraudiocontext
1.1.1.1.1循环处理caraudiocontext下的每个usage
1.1.1.1.1.1将bus下的每个caraudiocontext以及每个caraudiocontext下usage全部加入到AudioMixingRule中,感觉有点绕,其实我也没有get到谷歌搞caraudiocontext的用意,感觉很啰嗦,这个简单说就是把bus下所有的usage全部加到AudioMixingRule中。
1.1.1.2将每个bus的device信息加上AudioMixingRule 然后创建一个AudioMix,这个AudioMix化繁入简,说白了就是一个device对应了一群usage,多少有点路由的概念了。
1.1.1.3最后将AudioMix加到AudioPolicy中。
由于谷歌把CarAudioVolume、CarAudioZone、以及CarAudioFocus都融合到一起了,所以这块看起来有点乱,尤其一个接一个的for循环。
归根结底就是将bus和usage对应上(1个bus对应一群usage),不同的bus可以有相同的usage,但前提相同usage对应的bus一定是不同zone的。这个后面说同一个应用如何播放到不同音区上时再说。
所以我们在做定制路由策略的时候,一定要考虑好,哪些Usage放到同一个bus上。哪些bus放到同一个group中。
下一篇继续说如何将添加到AudioPolicy中的路由策略注册到FW的AudioPolicyManager中的。