音频焦点处理
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。请求焦点时放入栈顶,放弃焦点时出栈(注意:出栈的不一定是栈顶元素,可以是栈中的任意元素)。
请求焦点流程
- 将请求会话移至栈顶并授予焦点
- 若为 persistent 类型:暂停栈中其他所有会话
- 若为 transient 类型:其他会话降低音量(Ducking)
- 下个栈顶是 transient:不操作
- 下个栈顶是 persistent:该 persistent 会话降低音量(Ducking)
放弃焦点流程
- 非栈顶会话:直接移除
- 栈顶会话移除后:
- 栈空:不操作
- 新栈顶是 transient:不操作
- 新栈顶是 persistent:停止其 ducking 状态
音频焦点类型与行为对比
| 类型 | 使用场景 | 获取焦点时的行为 | 其他会话受影响方式 |
|---|---|---|---|
| Persistent | 长时播放(音乐/视频) | 暂停所有其他会话 | 完全暂停 |
| Transient | 短时音效(通知声) | 其他会话降低音量(Ducking) | 音量减半(默认行为) |
会话栈维护规则:
| 操作 | 影响 |
|---|---|
| 新Persistent入栈 | 暂停栈中所有会话,新会话置顶 |
| 新Transient入栈 | 仅对栈顶Persistent做Ducking,其他会话不变 |
| 会话放弃焦点 | 从栈中移除,若原栈顶是Persistent则停止Ducking |
Pepper 特殊处理
(由于 Pepper 插件无法暂停,只能调节音量)
- Pepper 加入会话时必须请求 persistent 类型
- 当需要暂停含 Pepper 的会话时改为启动 ducking
- 放弃焦点后若新栈顶处于 INACTIVE 状态,需查找下一个含 Pepper 的会话并取消其 ducking