在车机上,可能会遇到这种需求:
- 铃声和导航只在主驾扬声器播放。其它声音在所有扬声器播放。
- 对播放的音乐添加音效,但导航音或其它声音不添加音效。
这类需求如果在手机平台上实现,会比较复杂,修改的内容也会比较多。手机由于只有一个主扬声器,所以不管是什么类型的声音都从一个地方出来,因为大部分情况也不需要这样处理。
但如果是在车机平台,车机拥有更多的扬声器,需求就非常合理,事实上Android已经考虑到这种情况,只需要修改配置及少量的代码就可以实现。
1. 使用动态路由
通过overlay将audioUseDynamicRouting设置为true。默认为false,使用的还是手机的那一套audio_routing 配置方法。
<bool name="audioUseDynamicRouting">true</bool>
改为动态路由后,audio_policy_configuration.xml的配置方法略有不同,主要是devicePort,type将统一采用AUDIO_DEVICE_OUT_BUS,并且需要指明address,后续区分不同的音频流也是根据address来的。
2. 配置audio_policy_configuration.xml
附上一个audio_policy_configuration.xml示例
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<audioPolicyConfiguration version="7.0" xmlns:xi="http://www.w3.org/2001/XInclude">
<modules>
<module name="primary" halVersion="2.0">
<attachedDevices>
<item>Speaker</item>
<item>Built-In Mic</item>
<item>bus0_media_out</item>
<item>bus1_navigation_out</item>
<item>bus2_voice_command_out</item>
<item>bus3_call_ring_out</item>
<item>bus4_call_out</item>
<item>bus5_alarm_out</item>
<item>bus6_notification_out</item>
<item>bus7_system_sound_out</item>
</attachedDevices>
<defaultOutputDevice>bus0_media_out</defaultOutputDevice>
<mixPorts>
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus0_media_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus1_navigation_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus2_voice_command_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus3_call_ring_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus4_call_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus5_alarm_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus6_notification_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mix_bus7_system_sound_out" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="primary input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus0_media_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" 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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus1_navigation_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus1_navigation_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus2_voice_command_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus2_voice_command_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus3_call_ring_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus3_call_ring_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus4_call_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus4_call_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus5_alarm_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus5_alarm_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus6_notification_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus6_notification_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="bus7_system_sound_out" type="AUDIO_DEVICE_OUT_BUS" role="sink" address="bus7_system_sound_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="0" maxValueMB="49" defaultValueMB="25" stepValueMB="1"/>
</gains>
</devicePort>
<devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</devicePort>
<devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000"
channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000"
channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="BT SCO Car Kit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000"
channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
</devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
<devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
<devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/>
</devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
sources="primary output"/>
<route type="mix" sink="bus0_media_out"
sources="mix_bus0_media_out"/>
<route type="mix" sink="bus1_navigation_out"
sources="mix_bus1_navigation_out"/>
<route type="mix" sink="bus2_voice_command_out"
sources="mix_bus2_voice_command_out"/>
<route type="mix" sink="bus3_call_ring_out"
sources="mix_bus3_call_ring_out"/>
<route type="mix" sink="bus4_call_out"
sources="mix_bus4_call_out"/>
<route type="mix" sink="bus5_alarm_out"
sources="mix_bus5_alarm_out"/>
<route type="mix" sink="bus6_notification_out"
sources="mix_bus6_notification_out"/>
<route type="mix" sink="bus7_system_sound_out"
sources="mix_bus7_system_sound_out"/>
<route type="mix" sink="Wired Headset"
sources="primary output"/>
<route type="mix" sink="Wired Headphones"
sources="primary output"/>
<route type="mix" sink="BT SCO"
sources="primary output"/>
<route type="mix" sink="BT SCO Headset"
sources="primary output"/>
<route type="mix" sink="BT SCO Car Kit"
sources="primary output"/>
<route type="mix" sink="primary input"
sources="Built-In Mic,Wired Headset Mic,BT SCO Headset Mic"/>
</routes>
</module>
<xi:include href="a2dp_in_audio_policy_configuration_7_0.xml"/>
<xi:include href="usb_audio_policy_configuration.xml"/>
<xi:include href="r_submix_audio_policy_configuration.xml"/>
<xi:include href="bluetooth_audio_policy_configuration_7_0.xml"/>
</modules>
<xi:include href="audio_policy_volumes.xml"/>
<xi:include href="default_volume_tables.xml"/>
</audioPolicyConfiguration>
1. 先为每种类型的音频流声明一个播放设备(devicePort),注意需要同时声明gain,car service启动时会解析gain,否则car service无法正常启动。
2. 为该设备声明一个可接收的流(mixPort)。
3. 将流和设备连接起来。
3. 配置car_audio_configuration.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Defines the audio configuration in a car, including
- Audio zones
- Context to audio bus mappings
- Volume groups
in the car environment.
-->
<carAudioConfiguration version="2">
<zones>
<zone name="primary zone" isPrimary="true" occupantZoneId="0">
<volumeGroups>
<group>
<device address="bus0_media_out">
<context context="music"/>
<context context="announcement"/>
</device>
<device address="bus6_notification_out">
<context context="notification"/>
</device>
</group>
<group>
<device address="bus1_navigation_out">
<context context="navigation"/>
</device>
<device address="bus2_voice_command_out">
<context context="voice_command"/>
</device>
</group>
<group>
<device address="bus4_call_out">
<context context="call"/>
</device>
<device address="bus3_call_ring_out">
<context context="call_ring"/>
</device>
</group>
<group>
<device address="bus5_alarm_out">
<context context="alarm"/>
</device>
<device address="bus7_system_sound_out">
<context context="system_sound"/>
<context context="emergency"/>
<context context="safety"/>
<context context="vehicle_status"/>
</device>
</group>
</volumeGroups>
</zone>
</zones>
</carAudioConfiguration>
对应到系统设置,可以看到有4个音量调试Bar,分别对应上面的volumeGroup。
4. 如何使用address
address会通过audioflinger传递给hal
Device.cpp
std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamImpl(int32_t ioHandle,
const DeviceAddress& device,
const AudioConfig& config,
const AudioOutputFlags& flags,
AudioConfig* suggestedConfig) {
audio_config_t halConfig;
if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
return {Result::INVALID_ARGUMENTS, nullptr};
}
audio_stream_out_t* halStream;
audio_devices_t halDevice;
char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
return {Result::INVALID_ARGUMENTS, nullptr};
}
audio_output_flags_t halFlags;
if (CoreUtils::audioOutputFlagsToHal(flags, &halFlags) != NO_ERROR) {
return {Result::INVALID_ARGUMENTS, nullptr};
}
ALOGE("CCDEBUG open_output_stream handle: %d devices: %x flags: %#x "
"srate: %d format %#x channels %x address %s",
ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
halConfig.channel_mask, halDeviceAddress);
int status = mDevice->open_output_stream(mDevice, ioHandle, halDevice, halFlags, &halConfig,
&halStream, halDeviceAddress);
ALOGV("open_output_stream status %d stream %p", status, halStream);
sp<IStreamOut> streamOut;
if (status == OK) {
streamOut = new StreamOut(this, halStream, halDeviceAddress);
++mOpenedStreamsCount;
}
status_t convertStatus =
HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig);
ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
}
这里的halDeviceAddress就是audio_policy_configuration.xml中定义的address了。
将halDeviceAddress进一步传递给StreamOut,然后再传给WriteThread,这样从data message queue中读取的pcm数据就可以和address绑定起来了。
下面是分别点击四个音量调的日志,可以看到不同类型的音频输出已经可以区分了,并且他们分别运行在不同的线程中,说明。这样一来,就可以单独对它们做进一步处理了。
07-18 10:44:40.069 260 2722 I StreamOutHAL: CCDEBUG doWrite address bus0_media_out
07-18 10:44:55.798 260 2738 I StreamOutHAL: CCDEBUG doWrite address bus1_navigation_out
07-18 10:45:09.696 260 2753 I StreamOutHAL: CCDEBUG doWrite address bus4_call_out
07-18 10:45:21.261 260 2768 I StreamOutHAL: CCDEBUG doWrite address bus5_alarm_out