chromium audio_focus 音频焦点

153 阅读3分钟

音频焦点处理

MediaSession 会收集一个标签页中的所有音频生成对象(Audio-producing objects)。当多个 MediaSession 同时播放声音时,通常会带来不佳的听觉体验。音频焦点处理通过管理 MediaSession 并以适当方式混合音频,这是桌面版默认媒体会话功能的一部分。

处理模型

音频焦点类型

存在 "persistent"(持久)和 "transient"(临时)两种音频焦点类型:

  • Persistent 用于长时媒体播放,不应与其他音频混合。开始播放时应该暂停所有其他播放内容
  • Transient 用于短时媒体播放(如消息提示音)。开始播放时应在其他播放内容之上播放,其他播放内容应降低音量(Ducking)

MediaSession

Audio-producing objects生成音频的对象

生成音频的对象需要产生声音时应当加入 MediaSession。该对象有以下状态:

  • ACTIVE:拥有音频焦点,其生成音频的对象可以播放
  • SUSPENDED:没有音频焦点,所有生成音频的对象暂停,可在重新获得焦点时恢复
  • INACTIVE:没有音频焦点且不包含任何生成音频的对象

此外,MediaSession 带有 DUCKING 标志(表示其管理的生成音频的对象已降低音量),该标志与会话状态相互独立。

AudioFocusManager

这个全局实例用于管理所有 MediaSession 的状态,主要适用于没有系统级音频焦点的平台(如 Android):

  • 生成音频的对象要播放时:加入 MediaSession 并指定所需焦点类型 → MediaSession 向 AudioFocusManager 请求焦点 → 成功则允许播放
  • 生成音频的对象停止时:从 MediaSession 移除 → 若会话变空则放弃焦点 → AudioFocusManager 通知其他会话状态变更

音频焦点处理算法

AudioFocusManager 使用堆栈实现,跟踪所有 ACTIVE/SUSPENDED 状态的 MediaSession。请求焦点时放入栈顶,放弃焦点时出栈(注意:出栈的不一定是栈顶元素,可以是栈中的任意元素)。

请求焦点流程

  1. 将请求会话移至栈顶并授予焦点
  2. 若为 persistent 类型:暂停栈中其他所有会话
  3. 若为 transient 类型:其他会话降低音量(Ducking)
    • 下个栈顶是 transient:不操作
    • 下个栈顶是 persistent:该 persistent 会话降低音量(Ducking)

放弃焦点流程

  1. 非栈顶会话:直接移除
  2. 栈顶会话移除后:
    • 栈空:不操作
    • 新栈顶是 transient:不操作
    • 新栈顶是 persistent:停止其 ducking 状态

音频焦点类型与行为对比

类型使用场景获取焦点时的行为其他会话受影响方式
Persistent长时播放(音乐/视频)暂停所有其他会话完全暂停
Transient短时音效(通知声)其他会话降低音量(Ducking)音量减半(默认行为)

会话栈维护规则:

操作影响
新Persistent入栈暂停栈中所有会话,新会话置顶
新Transient入栈仅对栈顶Persistent做Ducking,其他会话不变
会话放弃焦点从栈中移除,若原栈顶是Persistent则停止Ducking

Pepper 特殊处理

(由于 Pepper 插件无法暂停,只能调节音量)

  • Pepper 加入会话时必须请求 persistent 类型
  • 当需要暂停含 Pepper 的会话时改为启动 ducking
  • 放弃焦点后若新栈顶处于 INACTIVE 状态,需查找下一个含 Pepper 的会话并取消其 ducking