降低延迟的设计

Android 4.1 版本发布引入了内部框架更改,以实现更低延迟的音频输出路径。 公共客户端 API 或 HAL API 的更改极少。 本文档介绍了最初的设计,该设计随着时间的推移不断发展。 深入理解此设计应有助于设备 OEM 和 SoC 供应商在其特定设备和芯片组上正确实施该设计。 本文不面向应用程序开发者。

音轨创建

客户端可以选择在 AudioTrack C++ 构造函数或 AudioTrack::set()audio_output_flags_t 参数中设置位 AUDIO_OUTPUT_FLAG_FAST。 目前,唯一这样做的客户端是

AudioTrack C++ 实现会审查 AUDIO_OUTPUT_FLAG_FAST 请求,并且可以选择在客户端级别拒绝该请求。 如果它决定传递该请求,则会使用 IAudioFlinger::createTrack()IAudioTrack 工厂方法 track_flags_t 参数的 TRACK_FAST 位来执行此操作。

AudioFlinger 音频服务器会审查 TRACK_FAST 请求,并且可以选择在服务器级别拒绝该请求。 它通过共享内存控制块的 CBLK_FAST 位来通知客户端请求是否被接受。

影响此决策的因素包括

  • 此输出是否存在快速混音器线程(见下文)
  • 音轨采样率
  • 是否存在客户端线程来为此音轨执行回调处理程序
  • 音轨缓冲区大小
  • 可用的快速音轨插槽(见下文)

如果客户端的请求被接受,则称为“快速音轨”。 否则,称为“普通音轨”。

混音器线程

当 AudioFlinger 创建普通混音器线程时,它会决定是否同时创建快速混音器线程。 普通混音器和快速混音器均不与特定音轨关联,而是与一组音轨关联。 始终存在一个普通混音器线程。 快速混音器线程(如果存在)从属于普通混音器线程,并在其控制下运行。

快速混音器

功能

快速混音器线程提供以下功能

  • 混合普通混音器的子混音和最多 7 个客户端快速音轨
  • 每音轨衰减

省略的功能

  • 每音轨采样率转换
  • 每音轨效果
  • 每混音效果

周期

快速混音器周期性运行,建议周期为 2 到 3 毫秒 (ms),或者如果需要调度稳定性,则周期稍高一些,为 5 毫秒。 选择此数字是为了在考虑完整的缓冲区管道后,总延迟约为 10 毫秒。 较小的值是可能的,但可能会导致功耗增加以及出现故障的几率,具体取决于 CPU 调度的可预测性。 较大的值是可能的,最高可达 20 毫秒,但这会导致总延迟降低,因此应避免使用。

调度

快速混音器以提升的 SCHED_FIFO 优先级运行。 它只需要很少的 CPU 时间,但必须经常运行且调度抖动要低。抖动表示周期时间的偏差:它是实际周期时间与预期周期时间之间的差异。 运行过晚会导致因欠载而产生的故障。 运行过早会导致因在音轨提供数据之前从快速音轨拉取数据而产生的故障。

阻塞

理想情况下,快速混音器线程永远不会阻塞,除了在 HAL write() 处。 快速混音器内发生的其他阻塞情况均被视为错误。 特别是,应避免使用互斥锁。 相反,应使用非阻塞算法(也称为无锁算法)。 有关此主题的更多信息,请参阅避免优先级反转

与其他组件的关系

快速混音器与客户端几乎没有直接交互。 特别是,它看不到 binder 级别的操作,但它可以访问客户端的共享内存控制块。

快速混音器通过状态队列接收来自普通混音器的命令。

除了拉取音轨数据外,与客户端的交互均通过普通混音器进行。

快速混音器的主要接收器是音频 HAL。

普通混音器

功能

所有功能均已启用

  • 最多 32 条音轨
  • 每音轨衰减
  • 每音轨采样率转换
  • 效果处理

周期

周期计算为快速混音器周期的第一个大于等于 20 毫秒的整数倍。

调度

普通混音器以提升的 SCHED_OTHER 优先级运行。

阻塞

允许普通混音器阻塞,并且通常在各种互斥锁以及用于写入其子混音的阻塞管道处执行阻塞。

与其他组件的关系

普通混音器与外部世界进行广泛的交互,包括 binder 线程、音频策略管理器、快速混音器线程和客户端音轨。

普通混音器的接收器是到快速混音器音轨 0 的阻塞管道。

标志

AUDIO_OUTPUT_FLAG_FAST 位是一个提示。 不保证请求会得到满足。

AUDIO_OUTPUT_FLAG_FAST 是一个客户端级别的概念。 它不会出现在服务器中。

TRACK_FAST 是一个客户端 -> 服务器概念。