音频会话

W3C 工作草案

关于本文档的更多详细信息
此版本:
https://www.w3.org/TR/2024/WD-audio-session-20241113/
最新发布版本:
https://www.w3.org/TR/audio-session/
编辑草案:
https://w3c.github.io/audio-session/
先前版本:
历史:
https://www.w3.org/standards/history/audio-session/
反馈:
GitHub
编辑:
Apple
Mozilla

摘要

此 API 定义了一个 API 表面,用于控制音频如何被渲染以及如何与其他播放音频的 应用程序交互。

本文档的状态

本节描述本文档在其发布时的状态。当前 W3C 出版物列表以及此技术报告的最新修订版可在 W3C 技术报告索引中找到,地址为 https://www.w3.org/TR/。

欢迎对此规范提出反馈和评论。对于此规范的讨论,首选使用 GitHub Issues。 或者,你也可以将评论发送到媒体工作组的邮件列表,public-media-wg@w3.org归档)。 此草案突出显示了工作组中仍待讨论的一些待决问题。 关于这些问题的结果,包括它们是否有效,尚未作出任何决定。

本文档由 媒体工作组作为 工作草案使用 推荐标准 轨道发布。 本文档旨在成为一项 W3C 推荐标准。

作为工作草案发布并不意味着获得 W3C 及其成员的认可。

本文档是一份草案文档,可能随时被其他文档更新、替换或废弃。 除了作为正在进行的工作外,不宜引用本文档。

本文档由一个根据 W3C 专利政策运作的小组制作。W3C 维护一份任何专利披露的公开列表, 这些披露是与该小组的交付成果相关而作出的;该页面还包括披露 专利的说明。实际知晓某项专利且认为该专利包含必要权利要求的个人,必须根据 W3C 专利政策第 6 节披露该信息。

本文档受 2023年11月03日 W3C 流程文档管辖。

1. 引言

人们越来越多地通过 Web 消费媒体(音频/视频), Web 已成为访问这类内容的主要渠道。 但是,Web 上的媒体通常缺乏与底层平台的无缝集成。 Audio Session API 通过在支持音频会话管理或类似音频焦点功能的平台上 增强媒体处理来弥补这一空白。 此 API 改进了基于 Web 的音频与其他应用的交互方式, 允许根据上下文进行更好的音频混音或独占播放, 从而在各种设备上提供更一致且更集成的媒体体验。

此外,一些平台会根据媒体播放以及用于播放音频的 API 自动管理站点的音频会话。 但是,这种行为可能并不总是符合用户预期。 此 API 允许开发者覆盖默认行为,并获得对音频会话的更多控制。

2. 概念

网页可以通过多种方式进行音频处理,结合使用不同的 API,例如 HTMLMediaElementAudioContext。 此音频处理有开始和停止,它聚合了所使用的所有不同音频 API。 音频会话 表示这种聚合的音频处理。它允许网页表达该网页所执行的音频处理的一般性质。

音频会话可以具有特定的 类型,并且 处于特定的状态音频会话管理一组单独源(麦克风录制) 和接收端(音频渲染)的音频,这些源和接收端称为音频会话 元素

音频会话元素具有 若干属性:

如果一个音频会话元素可听标志true,则它是一个 可听元素

此外,音频会话元素具有关联步骤,用于处理各种状态 变化。默认情况下,这些步骤中的每一个都是空的步骤列表:

本规范在 § 6 音频源和接收端集成一节中,为一些音频会话元素定义这些步骤、默认类型以及可听标志。 定义其他元素的规范需要定义这些步骤和属性。

顶级浏览上下文具有一个所选音频会话。在任何音频会话发生 变化的情况下,用户代理将更新哪个音频会话成为所选音频会话。 如果顶级浏览上下文所选音频会话不为 null 且其状态为 active, 则称该顶级浏览上下文具有音频焦点

用户代理可以决定是否允许多个顶级浏览上下文具有音频 焦点,或者强制在任意给定时间只有一个顶级浏览上下文具有音频焦点

3. AudioSession 接口

AudioSession 是此 API 的主接口。 它通过 Navigator 接口访问(见 § 4 Navigator 接口的扩展)。

[Exposed=Window]
interface AudioSession : EventTarget {
  attribute AudioSessionType type;

  readonly attribute AudioSessionState state;
  attribute EventHandler onstatechange;
};

要在 realm 中创建一个 AudioSession 对象,运行以下步骤:

  1. audioSessionrealm 中的一个新的 AudioSession 对象,并使用以下内部槽初始化:

    1. [[type]],用于存储音频会话的类型,初始化为 auto

    2. [[state]],用于存储音频会话的状态,初始化为 inactive

    3. [[elements]],用于存储音频会话的元素,初始化为空列表。

    4. [[interruptedElements]],用于存储在可听时 被中断的音频会话元素,初始化为空列表。

    5. [[appliedType]],用于存储应用于该 音频会话的类型,初始化为 auto

    6. [[isTypeBeingApplied]] 标志,用于存储该类型是否正在应用于该音频会话, 初始化为 false

  2. 返回 audioSession

每个 AudioSession 对象都唯一地绑定到其底层音频会话

AudioSession state 属性反映其音频 会话状态。 获取时,它必须返回 AudioSession[[state]] 值。

AudioSession type 属性反映其音频 会话类型,但 auto 除外。

获取时,它必须返回 AudioSession[[type]] 值。

设置时,它必须以 newValue 作为要在 audioSession 上设置的新值运行以下步骤:

  1. 如果 audioSession.[[type]] 等于 newValue,则中止这些步骤。

  2. audioSession.[[type]] 设置为 newValue

  3. 更新 audioSession 的类型。

3.1. 音频会话类型

按照惯例,存在若干不同的音频会话类型,用于不同 目的。 在 API 中,它们由 AudioSessionType 枚举表示:

playback
播放音频,用于视频或音乐播放、播客等。它们不应与其他播放音频混合。(也许)它们应无限期地暂停所有其他音频。
transient
瞬态音频,例如通知提示音。它们通常应在播放音频之上播放(并且也可能“压低”持久音频)。
transient-solo
瞬态独奏音频,例如行车导航。它们应暂停/静音所有其他音频并独占播放。 当一个 transient-solo 音频结束时,它应恢复被暂停/静音的音频。
ambient
环境音频,可与其他类型的音频混合。这在一些特殊情况下很有用,例如 用户想要混合来自多个页面的音频时。
play-and-record
播放和录制音频,用于录制音频。这在使用麦克风或视频会议应用程序中很有用。
auto
Auto 允许用户代理根据网页对音频的使用选择最佳音频会话类型。 这是 AudioSession 的默认类型。
enum AudioSessionType {
  "auto",
  "playback",
  "transient",
  "transient-solo",
  "ambient",
  "play-and-record"
};

如果一个 AudioSessionTypeplaybackplay-and-recordtransient-solo, 则它是独占类型

3.2. 音频会话状态

音频会话可以处于以下 状态之一,这些状态在 API 中由 AudioSessionState 枚举表示:

active
音频会话正在播放声音或录制麦克风。
interrupted
音频会话未播放声音也未录制麦克风, 但在不再被中断时可以恢复。
inactive
音频会话未播放声音也未录制麦克风。
enum AudioSessionState {
  "inactive",
  "active",
  "interrupted"
};

音频会话的状态可能 变化,这将通过通知状态变化的步骤自动反映在其 AudioSession 对象上。

4. Navigator 接口的扩展

每个 Window 都有一个关联的 AudioSession,它是一个 AudioSession 对象。 它表示用户代理用于自动设置音频会话参数的默认音频会话。 当音频会话元素开始或结束播放时, 用户代理将请求或放弃音频焦点。 在创建 Window 对象时,其关联的 AudioSession 必须被设置为一个新创建的 AudioSession 对象,其 realm 为该 Window 对象的相关 realm

关联的 AudioSession元素列表,会随着 Window 对象的音频源和接收端被创建或移除而动态更新。

[Exposed=Window]
partial interface Navigator {
  // The default audio session that the user agent will use when media elements start/stop playing.
  readonly attribute AudioSession audioSession;
};

5. 音频会话算法

5.1. 更新 AudioSession 的 type

更新类型 audioSession,用户代理必须运行以下步骤:

  1. 如果 audioSession.[[isTypeBeingApplied]]true, 则中止这些步骤。

  2. audioSession.[[isTypeBeingApplied]] 设置为 true

  3. 排队一个任务以运行以下步骤:

    1. audioSession.[[isTypeBeingApplied]] 设置为 false

    2. 如果 audioSession.[[type]]audioSession.[[appliedType]] 相同,则中止这些步骤。

    3. audioSession.[[appliedType]] 设置为 audioSession.[[type]]

    4. 更新所有 AudioSession 状态,即 audioSession顶级浏览上下文的所有状态,并使用 audioSession

    5. 对于 audioSession.[[elements]] 中的每个 element更新 element

    6. newType计算 audioSession 的类型的结果。

    7. 并行地,将 audioSession音频会话类型设置为 newType

5.2. 更新 AudioSession 的 state

当音频会话元素开始或停止时,用户代理将运行会 设置状态的步骤,即通过 停用尝试激活算法来设置 一个音频会话的状态。 将音频会话状态设置为 active 会产生后果,尤其是当音频会话类型独占类型时:

反过来,音频会话 状态也可以在音频会话元素变化之外被 修改。 当用户代理观察到这种修改时,用户代理必须排队一个任务,以使用 audioSession绑定到 被修改的音频会话AudioSession 对象,并以 newState 作为新的音频会话状态,来通知状态变化

一个处于 active 的 playback 音频会话可能会被来电中断,或者被另一个 playback 会话中断,该会话将在另一个标签页中开始播放新的媒体内容。

要使用 audioSessionnewState 通知 状态变化,用户代理必须运行以下步骤:

  1. isMutatingStatetrue,如果 audioSession.[[state]] 不是 newState,否则为 false

  2. audioSession.[[state]] 设置为 newState

  3. 如果 newStateinactive, 则将 audioSession.[[interruptedElements]] 设置为空列表。

  4. 对于 audioSession.[[elements]] 中的每个 element更新 element

  5. 如果 isMutatingStatefalse,则中止这些步骤。

  6. 更新所有 AudioSession 状态,即 audioSession顶级浏览上下文的所有状态,并使用 audioSession

  7. audioSession 上触发名为 statechange 的事件。

停用名为 audioSessionAudioSession,用户代理必须运行以下步骤:

  1. 如果 audioSession.[[state]]inactive, 则中止这些步骤。

  2. 并行运行以下步骤:

    1. audioSession音频会话的状态设置为 inactive

    2. 断言audioSession音频会话状态inactive

    3. 排队一个任务,以使用 audioSession 及其音频会话状态通知 状态变化

尝试激活名为 audioSessionAudioSession,用户代理必须运行以下步骤:

  1. 如果 audioSession.[[state]]active, 则中止这些步骤。

  2. 并行运行以下步骤:

    1. audioSession音频会话的状态设置为 active将状态设置active 可能会失败,在这种情况下,音频会话状态将为 inactiveinterrupted

    2. 排队一个任务,以使用 audioSession 及其音频会话状态通知 状态变化

激活一个音频会话可能会因 各种原因而失败。 例如,一个 Web 应用程序可能会在一个更高权限的应用程序(如电话通话应用程序)已经在播放音频时, 尝试开始播放某些音频。

5.3. 更新所选音频会话

更新 所选音频会话,即名为 context顶级浏览上下文的所选音频会话, 用户代理必须运行以下步骤:

  1. activeAudioSessionscontext 及其子级中所有绑定到 AudioSession 对象的音频会话列表, 按广度优先顺序排列,并且同时匹配以下两个约束:

    1. 状态active

    2. 计算AudioSession 对象类型的结果为独占类型

  2. 如果 activeAudioSessions 为空,则中止这些步骤。

  3. 如果 activeAudioSessions 中只有一个音频 会话,则将所选音频会话 设置为此音频会话, 并中止这些步骤。

  4. 断言: 对于任何绑定 到 activeAudioSessions 中名为 audioSession音频 会话AudioSession 对象, audioSession.[[type]]auto

    预期在任意时间点,只有一个具有显式独占类型音频会话可以 处于 active。 如果 activeAudioSessions 中存在多个 active 的音频会话,则它们的[[type]] 只能为 auto
  5. 用户代理可以应用特定的启发式方法来重新排序 activeAudioSessions

  6. 所选音频会话设置为 activeAudioSessions 中的第一个音频会话

5.4. 其他算法

更新 所有 AudioSession 状态,即名为 context顶级浏览上下文的所有状态,并使用 updatedAudioSession,运行以下步骤:

  1. 更新 context 的所选音频会话。

  2. updatedType计算 updatedAudioSession 类型的结果。

  3. 如果 updatedType 不是独占类型,或者 updatedAudioSession.[[state]] 不是 active, 则中止这些步骤。

  4. audioSessionscontext 及其子级中所有 AudioSession 对象的列表,按广度优先顺序排列。

  5. 对于 audioSessions 中除 updatedAudioSession 以外的每个 audioSession,运行以下步骤:

    1. 如果 audioSession.[[state]] 不是 active, 则中止这些步骤。

    2. type计算 audioSession 类型的结果。

    3. 如果 type 不是独占类型,则中止这些步骤。

    4. 如果 typeupdatedType 都是 auto, 则中止这些步骤。

    5. 停用 audioSession

计算 音频会话类型,即 audioSession 的音频会话类型,用户代理必须运行以下步骤:

  1. 如果 audioSession.[[type]] 不是 auto, 则返回 audioSession.[[type]]

  2. 如果 audioSession.[[elements]] 中的任何 element默认类型play-and-record 且其状态active, 则返回 play-and-record

  3. 如果 audioSession.[[elements]] 中的任何 element默认类型playback 且其状态active, 则返回 playback

  4. 如果 audioSession.[[elements]] 中的任何 element默认类型transient-solo 且其状态active, 则返回 transient-solo

  5. 如果 audioSession.[[elements]] 中的任何 element默认类型transient 且其状态active, 则返回 transient

  6. 返回 ambient

6. 音频源和接收端集成

本节描述音频 会话元素用于 AudioContextHTMLMediaElement 和麦克风 MediaStreamTrack 的步骤和属性。

元素状态为:

更新元素,即名为 element 的元素,用户代理必须运行以下步骤:

  1. audioSessionelementAudioSession

  2. 运行 element更新步骤

  3. 如果 element可听元素,且 audioSession.[[state]]interrupted, 则运行以下步骤:

    1. element 添加到 audioSession.[[interruptedElements]]

    2. 运行 element挂起步骤

  4. 如果 element 位于 audioSession.[[interruptedElements]] 中,并且 audioSession.[[state]]active, 则运行以下步骤:

    1. audioSession.[[interruptedElements]] 中移除 element

    2. 运行 element恢复步骤

audioSession 的某个元素可听标志 发生变化时,用户代理必须运行以下步骤:

  1. 如果可听标志 变为 true,则尝试激活 audioSession

  2. 否则,如果 audioSession.[[elements]] 中的任何 element状态interrupted, 则中止这些步骤。

  3. 否则,停用 audioSession

6.1. AudioContext

AudioContext 是具有以下属性的元素

创建 AudioContext 时,用户代理必须运行以下步骤:

  1. audioContext 为新创建的 AudioContext

  2. audioSession 为创建 audioContext 所在的 Window 对象的 AudioSession 对象。

  3. audioContext 添加到 audioSession.[[elements]]

6.2. HTMLMediaElement

HTMLMediaElement 是具有以下属性的元素

HTMLMediaElement节点文档发生变化时,用户代理必须运行以下 步骤:

  1. mediaElement 为其节点文档正在发生变化的 HTMLMediaElement

  2. previousWindow 为与 mediaElement 先前的节点文档关联的 Window 对象(如果有),否则为 null

  3. 如果 previousWindow 不是 null,则从 previousWindow关联的 AudioSession.[[elements]] 中移除 mediaElement

  4. newWindow 为与 mediaElement 新的节点文档关联的 Window 对象(如果有),否则为 null

  5. 如果 newWindow 不是 null,则将 mediaElement 添加到 newWindow关联的 AudioSession.[[elements]]

6.3. 麦克风 MediaStreamtrack

麦克风捕获 MediaStreamTrack 是具有以下属性的元素

当创建麦克风捕获 MediaStreamTrack 时,用户代理必须运行以下步骤:

  1. track 为新创建的 MediaStreamTrack

  2. audioSession 为创建 track 所在的 Window 对象的 AudioSession 对象。

  3. track 添加到 audioSession.[[elements]]

FIXME:我们应当挂接到存储在 Window 的 mediaDevices 的 mediaStreamTrackSources 中的音频轨道源,而不是 MediaStreamTrack。 这应当处理已转移的麦克风轨道的情况。

7. 隐私考虑事项

8. 安全考虑事项

9. 示例

9.1. 站点主动将其音频会话类型设置为 "play-and-record"

navigator.audioSession.type = 'play-and-record';
// 从现在开始,音量可能会基于 'play-and-record' 设置。
...
// 开始播放远程媒体
remoteVideo.srcObject = remoteMediaStream;
remoteVideo.play();
// 开始捕获
navigator.mediaDevices
  .getUserMedia({ audio: true, video: true })
  .then((stream) => {
    localVideo.srcObject = stream;
  });

9.2. 站点对中断作出反应

navigator.audioSession.type = "play-and-record";
// 从现在开始,音量可能会基于 'play-and-record' 设置。
...
// 开始播放远程媒体
remoteVideo.srcObject = remoteMediaStream;
remoteVideo.play();
// 开始捕获
navigator.mediaDevices
  .getUserMedia({ audio: true, video: true })
  .then((stream) => {
    localVideo.srcObject = stream;
  });

navigator.audioSession.onstatechange = async () => {
  if (navigator.audioSession.state === "interrupted") {
    localVideo.pause();
    remoteVideo.pause();
    // 向用户清楚表明通话已中断。
    showInterruptedBanner();
    for (const track of localVideo.srcObject.getTracks()) {
      track.enabled = false;
    }
  } else {
    // 让用户决定何时重启通话。
    const shouldRestart = await showOptionalRestartBanner();
    if (!shouldRestart) {
      return;
    }
    for (const track of localVideo.srcObject.getTracks()) {
      track.enabled = true;
    }
    localVideo.play();
    remoteVideo.play();
  }
};

10. 致谢

工作组感谢以下人员为本规范作出的宝贵贡献:

一致性

文档 约定

一致性要求通过描述性断言 和 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中的描述解释。 但是,为了可读性, 这些词在本规范中并不全以大写字母出现。

本规范的所有文本都是规范性的, 除非某些章节被明确标记为非规范性、示例和注释。 [RFC2119]

本规范中的示例以 “for example” 一词引入, 或者通过 class="example" 与规范性文本分开, 如下所示:

这是资料性示例的一个示例。

资料性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本分开, 如下所示:

Note,这是一个资料性注释。

一致性 算法

作为算法一部分以祈使句表述的要求 (例如 "strip any leading space characters" 或 "return false and abort these steps") 应按引入该算法时所使用的关键词 ("must"、"should"、"may" 等) 的含义解释。

以算法或具体步骤表述的一致性要求 可以以任何方式实现, 只要最终结果是等价的。 特别是,本规范中定义的算法 旨在易于理解, 而非旨在具有高性能。 鼓励实现者进行优化。

索引

本规范定义的术语

通过 引用定义的术语

参考文献

规范性参考文献

[DOM]
Anne van Kesteren。DOM 标准。现行标准。 URL:https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren;等。HTML 标准。 现行标准。URL:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。现行标准。URL:https://infra.spec.whatwg.org/
[MEDIACAPTURE-STREAMS]
Cullen Jennings;等。媒体捕获和 流。2024年10月3日。CR。URL:https://www.w3.org/TR/mediacapture-streams/
[RFC2119]
S. Bradner。用于 RFC 中表示 要求级别的关键词。1997年3月。最佳当前实践。URL:https://datatracker.ietf.org/doc/html/rfc2119
[WEBAUDIO]
Paul Adenot;Hongchan Choi。Web Audio API。2021年6月17日。REC。URL:https://www.w3.org/TR/webaudio/
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL 标准。现行 标准。URL:https://webidl.spec.whatwg.org/

IDL 索引

[Exposed=Window]
interface AudioSession : EventTarget {
  attribute AudioSessionType type;

  readonly attribute AudioSessionState state;
  attribute EventHandler onstatechange;
};

enum AudioSessionType {
  "auto",
  "playback",
  "transient",
  "transient-solo",
  "ambient",
  "play-and-record"
};

enum AudioSessionState {
  "inactive",
  "active",
  "interrupted"
};

[Exposed=Window]
partial interface Navigator {
  // The default audio session that the user agent will use when media elements start/stop playing.
  readonly attribute AudioSession audioSession;
};