AAudio 是 Android 8.0 版本中引入的音频 API。Android 8.1 版本增强了与支持 MMAP 的 HAL 和驱动程序结合使用时的延迟。本文档介绍了 Android 中支持 AAudio MMAP 功能所需的硬件抽象层 (HAL) 和驱动程序更改。
对 AAudio MMAP 的支持需要
- 报告 HAL 的 MMAP 功能
- 在 HAL 中实现新函数
- 选择性地为 EXCLUSIVE 模式缓冲区实现自定义 ioctl()
- 提供额外的硬件数据路径
- 设置启用 MMAP 功能的系统属性
AAudio 架构
AAudio 是一种新的原生 C API,它提供了 Open SL ES 的替代方案。它使用构建器设计模式来创建音频流。
AAudio 提供低延迟数据路径。在独占模式下,此功能允许客户端应用程序代码直接写入与 ALSA 驱动程序共享的内存映射缓冲区。在共享模式下,MMAP 缓冲区由在 AudioServer 中运行的混音器使用。在独占模式下,由于数据绕过了混音器,因此延迟显著降低。
在独占模式下,服务从 HAL 请求 MMAP 缓冲区并管理资源。MMAP 缓冲区在 NOIRQ 模式下运行,因此没有共享的读/写计数器来管理对缓冲区的访问。相反,客户端维护硬件的计时模型,并预测缓冲区何时将被读取。
在下图中,我们可以看到脉冲编码调制 (PCM) 数据通过 MMAP FIFO 流入 ALSA 驱动程序。时间戳由 AAudio 服务定期请求,然后通过原子消息队列传递到客户端的计时模型。

在共享模式下,也使用计时模型,但它位于 AAudioService 中。
对于音频捕获,也使用类似的模型,但 PCM 数据沿相反方向流动。
HAL 更改
有关 tinyALSA,请参阅
external/tinyalsa/include/tinyalsa/asoundlib.h external/tinyalsa/include/tinyalsa/pcm.c
int pcm_start(struct pcm *pcm); int pcm_stop(struct pcm *pcm); int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames); int pcm_get_poll_fd(struct pcm *pcm); int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames); int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
有关旧版 HAL,请参阅
hardware/libhardware/include/hardware/audio.h hardware/qcom/audio/hal/audio_hw.c
int start(const struct audio_stream_out* stream); int stop(const struct audio_stream_out* stream); int create_mmap_buffer(const struct audio_stream_out *stream, int32_t min_size_frames, struct audio_mmap_buffer_info *info); int get_mmap_position(const struct audio_stream_out *stream, struct audio_mmap_position *position);
有关 HIDL 音频 HAL
hardware/interfaces/audio/2.0/IStream.hal hardware/interfaces/audio/2.0/types.hal hardware/interfaces/audio/2.0/default/Stream.h
start() generates (Result retval); stop() generates (Result retval) ; createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info); getMmapPosition() generates (Result retval, MmapPosition position);
报告 MMAP 支持
系统属性“aaudio.mmap_policy”应设置为 2 (AAUDIO_POLICY_AUTO),以便音频框架知道音频 HAL 支持 MMAP 模式。(请参阅下面的“启用 AAudio MMAP 数据路径”。)
audio_policy_configuration.xml 文件还必须包含特定于 MMAP/NO IRQ 模式的输出和输入配置文件,以便音频策略管理器知道在创建 MMAP 客户端时要打开哪个流
<mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> <mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/> </mixPort>
打开和关闭 MMAP 流
createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info);
可以通过调用 Tinyalsa 函数来打开和关闭 MMAP 流。
查询 MMAP 位置
传递回计时模型的时间戳包含帧位置和以纳秒为单位的单调时间
getMmapPosition() generates (Result retval, MmapPosition position);
HAL 可以通过调用新的 Tinyalsa 函数从 ALSA 驱动程序获取此信息
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
共享内存的文件描述符
AAudio MMAP 数据路径使用硬件和音频服务之间共享的内存区域。共享内存使用由 ALSA 驱动程序生成的文件描述符引用。
内核更改
如果文件描述符直接与 /dev/snd/
驱动程序文件关联,则 AAudio 服务可以在共享模式下使用它。但是,描述符不能传递给独占模式的客户端代码。/dev/snd/
文件描述符将为客户端提供过多的访问权限,因此 SELinux 会阻止它。
为了支持独占模式,有必要将 /dev/snd/
描述符转换为 anon_inode:dmabuf
文件描述符。SELinux 允许将该文件描述符传递给客户端。AAudioService 也可以使用它。
可以使用 Android Ion 内存库生成 anon_inode:dmabuf
文件描述符。
有关更多信息,请参阅以下外部资源
- “Android ION 内存分配器” https://lwn.net/Articles/480055/
- “Android ION 概述” https://wiki.linaro.org/BenjaminGaignard/ion
- “集成 ION 内存分配器” https://lwn.net/Articles/565469/
HAL 更改
AAudio 服务需要知道是否支持此 anon_inode:dmabuf
。在 Android 10.0 之前,唯一的做法是将 MMAP 缓冲区的大小作为负数传递,例如,如果支持,则传递 -2048 而不是 2048。在 Android 10.0 及更高版本中,您可以设置 AUDIO_MMAP_APPLICATION_SHAREABLE
标志。
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
音频子系统更改
AAudio 需要音频子系统前端的额外数据路径,以便它可以与原始 AudioFlinger 路径并行运行。旧版路径用于所有其他系统声音和应用程序声音。此功能可以由 DSP 中的软件混音器或 SOC 中的硬件混音器提供。
启用 AAudio MMAP 数据路径
如果不支持 MMAP 或无法打开流,AAudio 将使用旧版 AudioFlinger 数据路径。因此,AAudio 将与不支持 MMAP/NOIRQ 路径的音频设备一起使用。
在测试 AAudio 的 MMAP 支持时,重要的是要知道您实际上是在测试 MMAP 数据路径还是仅仅在测试旧版数据路径。以下描述了如何启用或强制特定数据路径,以及如何查询流使用的路径。
系统属性
您可以通过系统属性设置 MMAP 策略
- 1 = AAUDIO_POLICY_NEVER - 仅使用旧版路径。甚至不要尝试使用 MMAP。
- 2 = AAUDIO_POLICY_AUTO - 尝试使用 MMAP。如果失败或不可用,则使用旧版路径。
- 3 = AAUDIO_POLICY_ALWAYS - 仅使用 MMAP 路径。不要回退到旧版路径。
这些可以在设备的 Makefile 中设置,如下所示
# Enable AAudio MMAP/NOIRQ data path. # 2 is AAUDIO_POLICY_AUTO so it will try MMAP then fallback to Legacy path. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2 # Allow EXCLUSIVE then fall back to SHARED. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_exclusive_policy=2
您还可以在设备启动后覆盖这些值。您需要重启 audioserver 才能使更改生效。例如,要为 MMAP 启用 AUTO 模式
adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver
ndk/sysroot/usr/include/aaudio/AAudioTesting.h
中提供了函数,允许您覆盖使用 MMAP 路径的策略
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
要了解流是否正在使用 MMAP 路径,请调用
bool AAudioStream_isMMapUsed(AAudioStream* stream);