组合音频设备路由

组合音频设备路由功能增加了对同时将音频流式传输到多个音频设备的支持。借助此功能,特权应用可以通过系统 API 为特定策略选择多个首选设备。应用可以使用此功能提供的公共 API,更精确地发现音频设备的功能。在 Android 11 及更低版本中,音频框架实现对同时连接同一类型的多个音频设备(例如 2 个蓝牙 A2DP 耳机)支持有限。此外,默认的音频路由规则也不允许用户为特定用例选择同一类型的多个设备。

从 Android 12 开始,我们移除了这些限制,以便支持一些新用例,例如音频广播、向一组 BLE 音频耳机发送多播消息,或同时使用多个 USB 声卡。

本文将介绍如何实现对将音频流式传输到多个音频设备的支持,以及如何验证此功能的实现。

支持将音频流式传输到多个音频设备

Android 12 中有两组支持此功能的 API:

  • 系统 API,用于处理一项策略的多个首选设备。
  • HIDL 接口,由供应商作为音频 HAL 的一部分实现,用于报告设备功能。

以下几部分详细介绍了其中的每个 API。

处理一项策略的多个首选设备

为了更好地支持同时将音频流式传输到多个音频设备,音频政策管理中心提供了一些系统 API。借助这些系统 API,就能为特定策略设置、获取和移除多个首选设备。在 Android 12 之前,只支持将此功能用于单个设备。

音频政策管理中心引入了活跃媒体设备的概念来描述最有可能被选中用于媒体播放的设备。在连接可拆卸设备时,可能必须打开可路由到此设备的音频 HAL 输出流并探测支持的属性。

打开输出流时,必须指定音频设备。活跃媒体设备就是在这种情况下打开输出流时使用的设备。

活跃媒体设备的选择可能因连接或断开连接的实际设备而发生变化。音频政策管理中心使用以下一系列规则来选择活跃媒体设备:

  1. 如果媒体的所有首选设备都可用,就将这些设备全部选为活跃设备。
  2. 否则,选择上次连接的可移动设备。
  3. 如果没有连接任何可移动设备,就应用用于选择输出设备的默认音频政策规则来选择活跃设备。

输出流必须满足以下条件才能重新打开并路由到活跃设备,以便挑选用于播放的最佳配置:

  • 输出流必须支持活跃设备。
  • 输出流必须支持动态配置文件。
  • 输出流当前不得路由到活跃设备。

为了应用新的设备选择,如果输出流处于空闲状态,音频政策管理中心就会在设备连接时关闭输出流然后将其重新打开,否则就延迟到输出流被置于待机状态时再关闭后重新打开输出流。

音频政策管理中心提供下列系统 API(如 AudioManager.java 中所定义):

  • setPreferredDeviceForStrategy

    为特定策略设置音频路由的一个首选设备。请注意,设备可能在设置首选设备时不可用,但一旦变得可用就会使用它。

  • removePreferredDeviceForStrategy

    移除之前使用 setPreferredDeviceForStrategysetPreferredDevicesForStrategy 设置的首选音频设备。

  • getPreferredDeviceForStrategy

    返回之前使用 setPreferredDeviceForStrategysetPreferredDevicesForStrategy 为某一项音频策略设置的一个首选设备。

  • setPreferredDevicesForStrategy

    为特定策略设置多个首选设备。

  • getPreferredDevicesForStrategy

    返回之前使用 setPreferredDeviceForStrategysetPreferredDevicesForStrategy 为某一项音频策略设置的多个首选设备。

  • OnPreferredDevicesForStrategyChangedListener

    定义一个接口,用于通知为特定音频策略设置的首选音频设备的更改。

  • addOnPreferredDevicesForStrategyChangedListener

    添加一个监听器,以便在策略首选音频设备更改时收到通知。

  • removeOnPreferredDevicesForStrategyChangedListener

    移除之前添加的策略首选音频设备更改监听器。

报告设备功能

供应商会实现支持报告设备功能的 API,作为音频 HAL 实现的一部分。本部分将介绍用于报告设备功能的数据类型和方法,并列出音频 HIDL HAL V7 中为了支持多个设备而做出的一些变更。

数据类型

在音频 HIDL HAL V7 中,使用 AudioProfileAudioTransport 结构报告设备功能。AudioTransport 结构使用 AudioProfile(适用于已知音频格式)或者原始硬件描述符(适用于平台未知的格式)描述音频端口的功能。AudioProfile 结构包含音频格式、配置文件支持的采样率和通道掩码列表,如 types.hal 中的以下代码块所示:

/**
* Configurations supported for a certain audio format.
*/
struct AudioProfile {
   AudioFormat format;
   /** List of the sample rates (in Hz) supported by the profile. */
   vec<uint32_t> sampleRates;
   /** List of channel masks supported by the profile. */
   vec<AudioChannelMask> channelMasks;
};

在音频 HIDL HAL V7 中,使用 AudioTransportAudioProfile 结构定义 AudioPort 数据类型,以描述设备的功能。

音频 HAL 方法

音频政策管理中心使用以下方法来查询设备的功能:

  • getParameters: 用于检索供应商特定参数值(如支持的音频格式及其各自的采样率)的通用方法。
  • getAudioPort: 返回特定音频端口支持的属性(如采样率、格式、通道掩码、增益控制器)的列表。

IDevice.hal 中的以下代码显示了 getAudioPort 方法的接口:

   /**
    * Returns the list of supported attributes for a given audio port.
    *
    * As input, 'port' contains the information (type, role, address etc...)
    * needed by the HAL to identify the port.
    *
    * As output, 'resultPort' contains possible attributes (sampling rates,
    * formats, channel masks, gain controllers...) for this port.
    *
    * @param port port identifier.
    * @return retval operation completion status.
    * @return resultPort port descriptor with all parameters filled up.
    */
   getAudioPort(AudioPort port)
           generates (Result retval, AudioPort resultPort);

旧版 API 的变更

为了支持多个音频配置文件,3.2 版的旧版 API 添加了一个名为 audio_port_v7 的新结构。如需了解详情,请参阅源代码

由于添加了 audio_port_v7,为了使用 audio_port_v7 结构来查询设备的功能,3.2 版的旧版 API 新增了一个名为 get_audio_port_v7 的 API。

audio.h 中的以下代码显示了 get_audio_port_v7 API 的定义:

/**
 * Fills the list of supported attributes for a given audio port.
 * As input, "port" contains the information (type, role, address etc...)
 * needed by the HAL to identify the port.
 * As output, "port" contains possible attributes (sampling rates,
 * formats, channel masks, gain controllers...) for this port. The
 * possible attributes are saved as audio profiles, which contains audio
 * format and the supported sampling rates and channel masks.
 */
 int (*get_audio_port_v7)(struct audio_hw_device *dev,
                          struct audio_port_v7 *port);

当旧版 API 版本低于 3.2 且 HIDL HAL 版本为 7 或更高版本时,必须将来自旧版 get_audio_port API 的数据填充到新的 AudioPort 格式中。在这种情况下,系统会假定所有返回的格式都支持通过 get_audio_port 报告的所有采样率和通道掩码,因而能够从 get_audio_port 值直接映射到新的 AudioPort 结构。

API 实现示例

本部分将介绍一些测试套件,其中包含的方法使用了前面部分介绍的 API。如需查看这些 API 的实现方式和使用方式示例,请参阅这些方法。

如需查看系统 API setPreferredDevicesForStrategygetPreferredDevicesForStrategyremovePreferredDeviceForStrategyOnPreferredDevicesForStrategyChangedListener 的使用示例,请参阅位于 GTS 中的 PreferredDeviceRoutingTest 方法。

如需查看正在使用的 AudioDeviceInfo 中的新结构示例,请参阅位于 CTS 中的 AudioManagerTest#testGetDevices 方法。

get_audio_port_v7 实现的示例位于 audio_hal.c 中,展示了如何查询多个设备的功能。

验证

本部分将介绍音频管理器的 CTS 和 GTS(Google 移动服务测试套件)验证。

CTS 测试

CTS 测试位于 android.media.cts.AudioManagerTest 中。

下面列出了可用的音频管理器测试:

  • AudioManagerTest#testGetDevices

    验证音频设备的确切功能。此测试还会验证 AudioDeviceInfo 结构中返回的音频配置文件是否保留了来自旧扁平化数组格式但目前采用新 AudioProfile 格式的内容。

  • AudioManagerTest#testPreferredDevicesForStrategyAudioManagerTest#testPreferredDeviceForCapturePreset

    验证与策略首选设备和捕获预设首选设备相关的 API 测试是否成功完成。

GTS 测试

GTS 测试位于 com.google.android.gts.audioservice.AudioServiceHostTest 中。

如需验证用于策略首选设备和捕获预设首选设备的 API 能否正常工作,请运行 AudioServiceHostTest#testPreferredDeviceRoutingAudioServiceHostTest#testPreferredDeviceRoutingForCapturePreset 测试。