本规范的初始作者是 Google Inc. 的 Ian Hickson,其版权声明如下
© 版权所有 2004-2011 Apple Computer, Inc.、Mozilla Foundation 以及 Opera Software ASA。您被授予使用、复制和创作基于本文档的衍生作品的许可。
自 2011 年 7 月 26 日起的所有后续更改均由 W3C WebRTC 工作组(此前为设备 API 工作组)完成,受以下 版权 © 2011-2023 万维网联盟 (W3C) 条款约束。W3C® 的 责任声明、商标 和 宽松文档许可 规则适用。
本文档定义了一组 JavaScript API,允许从平台请求本地媒体,包括音频和视频。
本节描述了本文件发布时的状态。当前的 W3C 出版物列表和本技术报告的最新版本可以在 W3C 标准和草案索引中找到。
本文档尚未完成。该 API 基于 WHATWG 的初步工作。
在本文档进入“建议推荐标准”阶段之前,WebRTC 工作组打算解决 广泛审阅中出现的议题。
本文档由 Web 实时通信工作组发布,作为候选推荐草案,并采用了推荐标准轨道。
作为候选推荐标准发布并不意味着得到 W3C 及其成员的认可。候选推荐标准草案整合了来自先前候选推荐标准的更改,工作组打算将其包含在后续的候选推荐标准快照中。
这是一份草案文件,可能随时被其他文件更新、替换或废弃。将其作为进展中的工作以外的引用是不恰当的。
本文档由在 W3C 专利政策下运作的组织编写。W3C 维护着一份与该组交付成果相关的专利披露公开列表;该页面还包含了披露专利的说明。任何知晓其认为包含必要权利要求的专利的个人,必须根据 W3C 专利政策第 6 节披露相关信息。
本文件受 2025 年 8 月 18 日 W3C 流程文档约束。
本节是非规范性的。
本文档定义了请求访问本地多媒体设备(如麦克风或摄像机)的 API。
本文档还定义了 MediaStream API,它提供了控制多媒体流数据消耗位置的手段,并提供了一定程度的设备控制能力。它还公开了有关能够捕获和渲染媒体的设备的信息。
除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。
本文档中的关键字 MAY(可以)、MUST(必须)、MUST NOT(不得)、NOT REQUIRED(不要求)和 SHOULD(应该)仅当以全大写形式出现时,应按照 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释。
本规范定义了适用于单一产品的符合性标准:实现其所包含接口的 用户代理 (User Agent)。
以算法或具体步骤表述的一致性要求可以以任何方式实现,只要最终结果等效即可。(特别说明,本规范中定义的算法旨在易于遵循,而非旨在达到高性能。)
使用 ECMAScript [ECMA-262] 实现本规范定义 API 的实现必须以与 Web IDL 规范 [WEBIDL] 中定义的 ECMAScript 绑定一致的方式实现它们,因为本规范使用了该规范及术语。
源 (source) 是提供媒体流轨道的“事物”。源是媒体本身的广播者。源可以是物理摄像头、麦克风、用户硬盘上的本地视频或音频文件、网络资源或静态图像。请注意,本文档仅描述麦克风和摄像头类型源的使用,其他源类型的使用在其他文档中描述。
对于没有关于源的预授权的应用程序,只会被告知可用源的数量、其类型以及与其他设备的关系。当应用程序被授权使用源时,有关源的其他信息才变得可用(参见 9.2.1 访问控制模型)。
源没有约束 —— 轨道有约束。当源连接到轨道时,它必须为该轨道生产符合该轨道上现有约束的媒体。多个轨道可以附加到同一个源。 用户代理 的处理(例如下采样)MAY 用于确保所有轨道都具有适当的媒体。
源具有可约束属性,这些属性在轨道上公开了 能力 (capabilities) 和 设置 (settings)。虽然可约束属性由源“拥有”,但源 MAY 能够同时满足不同的需求。因此,能力对于恰好使用相同源的任何(多个)轨道是通用的,而设置 MAY 因轨道而异(例如,如果绑定到同一源的两个不同轨道对象查询能力和设置信息,它们将获得相同的能力,但可能会获得不同的设置,这些设置旨在满足各自的约束)。
设置是指源的可约束属性的即时当前值。设置始终是只读的。
源条件可能会动态变化,例如当摄像头由于低光条件切换到较低帧率时。在这些情况下,与受影响源相关的轨道可能不再满足设定的约束。平台 SHOULD 尽量减少此类偏差,但即使在存在妨碍满足约束的临时或永久条件时,也会继续提供媒体。
尽管设置是源的一个属性,但它们仅通过附加到源的轨道公开给应用程序。这是通过 ConstrainablePattern 接口公开的。
对于每个可约束属性,都有一种能力来描述它是否受源支持,如果支持,还描述支持值的范围。与设置一样,能力通过 ConstrainablePattern 接口公开给应用程序。
支持的能力值必须归一化为本规范中定义的范围和枚举类型。
轨道上的 getCapabilities() 调用为连接到该源的所有轨道返回相同的底层按源计算的能力。
此 API 特意进行了简化。能力无法描述不同值之间的相互作用。例如,无法准确描述一台相机的能力,该相机可以在低帧率下产生高分辨率视频流,而在高帧率下产生较低分辨率。能力描述了每个值的完整范围。约束之间的相互作用通过尝试应用约束来暴露。
约束提供了一个通用的控制界面,允许应用程序既能为轨道选择合适的源,也能在选择后影响源的运作方式。
约束限制了源在为轨道提供媒体时可以使用的操作模式范围。如果未提供轨道约束,实现可以自由地从其支持能力的全范围内选择源的设置。实现也可以随时在所有已应用约束所施加的范围内调整源设置。
getUserMedia() 使用约束来帮助为轨道选择合适的源并进行配置。此外,轨道上的 ConstrainablePattern 接口包含一个在之后任何时间动态更改轨道约束的 API。
如果轨道的初始约束无法满足,轨道将不会使用 getUserMedia() 连接到源。然而,满足轨道约束的能力会随时间变化,且约束也可以更改。如果情况发生变化导致无法满足约束,ConstrainablePattern 接口定义了一个适当的错误以通知应用程序。5. 模型:源、接收端、约束与设置 更详细地解释了约束如何相互作用。
对于每个可约束属性,都存在一个约束,其名称与相关的源设置名称和能力名称对应。
约束根据其在约束结构中的位置,分为三组。这些组为:
advanced 关键字指定的约束。通常情况下,应用越少的约束,用户代理 优化媒体流体验的灵活性就越高,因此强烈鼓励应用程序作者谨慎使用 必要约束 (required constraints)。
MediaStream API 中的两个主要组件是 MediaStreamTrack 和 MediaStream 接口。MediaStreamTrack 对象表示源自 用户代理 中一个媒体源的单类型媒体,例如由网络摄像头产生的视频。MediaStream 用于将多个 MediaStreamTrack 对象组合成一个单元,以便在媒体元素中录制或渲染。
每个 MediaStream 可以包含零个或多个 MediaStreamTrack 对象。MediaStream 中的所有轨道在渲染时都旨在同步。这并非硬性要求,因为可能无法同步具有不同时钟源的轨道。不同的 MediaStream 对象不需要同步。
虽然目的是同步轨道,但在某些情况下,允许轨道失去同步可能更好。特别是,当轨道是远程来源且实时传输时 [WEBRTC],允许失去同步比累积延迟或导致故障和其他伪影可能更好。预计实现能够理解有关回放同步的选择以及这些选择对用户感知的影响。
单个 MediaStreamTrack 可以表示多通道内容,例如立体声或 5.1 音频或立体视频,其中通道之间具有定义明确的关系。有关通道的信息可能通过其他 API(如 [WEBAUDIO])公开,但本规范不提供对通道的直接访问。
一个 MediaStream 对象具有一个输入和一个输出,代表该对象所有轨道的组合输入和输出。MediaStream 的输出控制对象如何渲染,例如如果对象被录制到文件,保存的是什么,或者如果在 video 元素中使用,显示的是什么。单个 MediaStream 对象可以同时附加到多个不同的输出。
可以使用 MediaStream() 构造函数从现有的媒体流或轨道创建一个新的 MediaStream 对象。构造函数参数可以是现有的 MediaStream 对象(在这种情况下,给定流的所有轨道都会被添加到新的 MediaStream 对象中),或者是一组 MediaStreamTrack 对象。后一种形式使得可以从不同的源流组成流。
MediaStream 和 MediaStreamTrack 对象都可以被克隆。克隆的 MediaStream 包含原始流中所有成员轨道的克隆。克隆的 MediaStreamTrack 拥有一组 独立于其克隆来源的约束,这允许来自同一源的媒体为不同的 消费者 (consumers) 应用不同的约束。MediaStream 对象也用于 getUserMedia 之外的上下文中,例如 [WEBRTC]。
MediaStreamMediaStream 构造函数 由现有轨道组成新流。它接受一个类型为 MediaStream 的可选参数,或一个 MediaStreamTrack 对象数组。调用构造函数时,用户代理必须运行以下步骤
设 stream 为新构建的 MediaStream 对象。
将 stream.id 属性初始化为新生成的值。
如果构造函数的参数存在,运行以下步骤
基于参数类型构建一组轨道 tracks
MediaStream 对象
设 tracks 为包含 MediaStream 轨道集 (track set) 中所有 MediaStreamTrack 对象的集合。
一组 MediaStreamTrack 对象
设 tracks 为包含所提供序列中所有 MediaStreamTrack 对象的集合。
对于 tracks 中的每个 MediaStreamTrack (track),运行以下步骤
返回 stream。
MediaStream 的轨道存储在 轨道集 (track set) 中。该轨道集 MUST 包含对应于流轨道的 MediaStreamTrack 对象。轨道在集合中的相对顺序是由用户代理定义的,API 不会对其顺序提出任何要求。在集合中查找特定 MediaStreamTrack 对象的正确方法是通过其 id 进行查找。
从 MediaStream 的输出中读取数据的对象被称为 MediaStream 消费者 (consumer)。目前 MediaStream 消费者列表包括媒体元素(如 video 和 audio) [HTML]、Web 实时通信(WebRTC; RTCPeerConnection) [WEBRTC]、媒体录制 (MediaRecorder) [mediastream-recording]、图像捕获 (ImageCapture) [image-capture] 以及 Web 音频 (MediaStreamAudioSourceNode) [WEBAUDIO]。
MediaStream 消费者必须能够处理轨道的添加和删除。此行为是按消费者指定的。
当一个 MediaStream 对象至少包含一个尚未 结束 (ended) 的 MediaStreamTrack 时,称该对象为 活跃 (active) 的。没有任何轨道或只有 结束 轨道的 MediaStream 被称为 非活跃 (inactive) 的。
当一个 MediaStream 对象至少包含一个 [[Kind]] 为 "audio" 且尚未 结束 的 MediaStreamTrack 时,称该对象为 可听 (audible) 的。没有任何音频轨道或只有 结束 的音频轨道的 MediaStream 被称为 不可听 (inaudible) 的。
用户代理 可以响应例如外部事件而更新 MediaStream 的 轨道集。本规范不指定任何此类情况,但使用 MediaStream API 的其他规范可能会。一个这样的例子是 WebRTC 1.0 [WEBRTC] 规范,其中从另一个对等点接收到的 MediaStream 的 轨道集 可以由于媒体会话的变化而更新。
要将轨道 track 添加到 MediaStream stream 中,用户代理 MUST 运行以下步骤
如果 track 已在 stream 的 轨道集 中,则终止这些步骤。
将 track 添加到 stream 的 轨道集 中。
触发一个轨道事件 (Fire a track event),名称为 addtrack,带有 track,目标为 stream。
要从 MediaStream stream 中 移除轨道 track,用户代理 MUST 运行以下步骤
如果 track 不在 stream 的 轨道集 中,则终止这些步骤。
触发一个轨道事件,名称为 removetrack,带有 track,目标为 stream。
WebIDL[Exposed=Window]
interface MediaStream : EventTarget {
constructor();
constructor(MediaStream stream);
constructor(sequence<MediaStreamTrack> tracks);
readonly attribute DOMString id;
sequence<MediaStreamTrack> getAudioTracks();
sequence<MediaStreamTrack> getVideoTracks();
sequence<MediaStreamTrack> getTracks();
MediaStreamTrack? getTrackById(DOMString trackId);
undefined addTrack(MediaStreamTrack track);
undefined removeTrack(MediaStreamTrack track);
MediaStream clone();
readonly attribute boolean active;
attribute EventHandler onaddtrack;
attribute EventHandler onremovetrack;
};
id,类型为 DOMString,只读id 属性 MUST 返回对象创建时被初始化到的值。
当创建一个 MediaStream 时,用户代理 MUST 生成一个标识符字符串,并 MUST 将对象的 id 属性初始化为该字符串,除非对象是作为规定流 ID 如何初始化的特殊用途算法的一部分而创建的。一个好的做法是使用 UUID [rfc4122],其规范形式为 36 个字符长。为避免指纹识别,实现 SHOULD 在生成 UUID 时使用 RFC 4122 第 4.4 或 4.5 节中的形式。
指定流 ID 如何初始化的算法的一个例子是关联传入网络组件与 MediaStream 对象的算法。 [WEBRTC]
active,类型为 boolean,只读如果此 MediaStream 是 活跃 (active) 的,则 active 属性 MUST 返回 true,否则返回 false。
onaddtrack,类型为 EventHandler此事件处理程序的事件类型为 addtrack。
onremovetrack,类型为 EventHandler此事件处理程序的事件类型为 removetrack。
getAudioTracks()返回表示此流中音频轨道的 MediaStreamTrack 对象序列。
getAudioTracks 方法 MUST 返回一个序列,该序列表示此流 轨道集 中所有 [[Kind]] 等于 "audio" 的 MediaStreamTrack 对象的快照。从 轨道集 到序列的转换是由 用户代理 定义的,顺序不必在调用之间保持稳定。
getVideoTracks()返回表示此流中视频轨道的 MediaStreamTrack 对象序列。
getVideoTracks 方法 MUST 返回一个序列,该序列表示此流 轨道集 中所有 [[Kind]] 等于 "video" 的 MediaStreamTrack 对象的快照。从 轨道集 到序列的转换是由 用户代理 定义的,顺序不必在调用之间保持稳定。
getTracks()返回表示此流中所有轨道的 MediaStreamTrack 对象序列。
getTracks 方法 MUST 返回一个序列,该序列表示此流 轨道集 中所有 MediaStreamTrack 对象的快照,与 [[Kind]] 无关。从 轨道集 到序列的转换是由用户代理定义的,顺序不必在调用之间保持稳定。
getTrackById()getTrackById 方法 MUST 返回来自此流 轨道集 中 [[Id]] 等于 trackId 的 MediaStreamTrack 对象,或者如果不存在此类轨道,则返回 null。
addTrack()将给定的 MediaStreamTrack 添加到此 MediaStream 中。
当调用 addTrack 方法时,用户代理 MUST 运行以下步骤
设 track 为方法参数,stream 为调用此方法的 MediaStream 对象。
如果 track 已在 stream 的 轨道集 中,则终止这些步骤。
removeTrack()从该 MediaStream 中移除给定的 MediaStreamTrack 对象。
当调用 removeTrack 方法时,用户代理 MUST 运行以下步骤
设 track 为方法参数,stream 为调用此方法的 MediaStream 对象。
如果 track 不在 stream 的 轨道集 中,则终止这些步骤。
clone()克隆给定的 MediaStream 及其所有轨道。
当调用 clone() 方法时,用户代理 MUST 运行以下步骤
设 streamClone 为新构建的 MediaStream 对象。
将 streamClone.MediaStream.id 初始化为新生成的值。
克隆此 MediaStream 对象中的每个轨道,并将结果添加到 streamClone 的 轨道集 中。
期望触发 addtrack 或 removetrack 事件的用户代理代码,应该通过来自另一个对象的引用来保持目标 MediaStream 对象处于存活状态。在尝试对 MediaStream 对象进行垃圾回收时,不需要考虑 addtrack 或 removetrack 事件监听器的存在。
MediaStreamTrack一个 MediaStreamTrack 对象代表 用户代理 中的媒体源。源的一个例子是连接到 用户代理 的设备。其他规范可能会定义覆盖此处指定行为的 MediaStreamTrack 源。多个 MediaStreamTrack 对象可以表示同一个媒体源,例如,当用户在连续两次调用 getUserMedia() 时显示的 UI 中选择了相同的摄像头时。
一个 MediaStreamTrack 源定义了以下属性
MediaStreamTrack 或 MediaStreamTrack 的子类型。默认情况下,它被设置为 MediaStreamTrack。MediaStreamTrack 时执行。这些步骤将新创建的 MediaStreamTrack 作为输入。默认情况下,这些步骤为空。MediaStreamTrack 时执行。这些步骤将源和目标 MediaStreamTrack 作为输入。默认情况下,这些步骤为空。MediaStreamTrack 对象中的数据不一定具有规范的二进制形式;例如,它可能只是“当前来自用户摄像头的视频”。这允许 用户代理 以用户平台上最适合的任何方式操作媒体。
脚本可以使用 stop() 方法指示 MediaStreamTrack 对象不再需要其源。当使用源的所有轨道都已停止或通过其他方式结束时,该源即被 停止 (stopped)。如果源是由 getUserMedia() 公开的设备,则当源停止时,用户代理 MUST 运行以下步骤
设 mediaDevices 为相关的 MediaDevices 对象。
设 deviceId 为源设备的 deviceId。
设置 mediaDevices.[[devicesLiveMap]][deviceId] 为 false。
如果与 mediaDevices 的 相关设置对象 (relevant settings object) 的设备种类和 deviceId 关联的权限的 权限状态 不是 "granted"(已授予),则将 mediaDevices.[[devicesAccessibleMap]][deviceId] 设置为 false。
要 创建 MediaStreamTrack,带有底层 source 和 mediaDevicesToTieSourceTo,运行以下步骤
设 track 为类型为 source 的 MediaStreamTrack 源类型 的新对象。
使用以下内部槽位初始化轨道
[[Source]],初始化为 source。
[[Id]],初始化为新生成的唯一标识符字符串。有关如何生成此类标识符的准则,请参见 MediaStream.id 属性。
[[Kind]],如果 source 是音频源,则初始化为 "audio";如果 source 是视频源,则初始化为 "video"。
[[Label]],如果由用户代理提供,则初始化为 source 的标签,否则为 ""。用户代理 MAY 标记音频和视频源(例如,“内部麦克风”或“外部 USB 网络摄像头”)。
[[ReadyState]],初始化为 "live"。
[[Enabled]],初始化为 true。
[[Muted]],如果 source 是 静音 (muted),则初始化为 true,否则为 false。
ConstrainablePattern 中的规定进行初始化。
[[Restrictable]],初始化为 false。
如果 mediaDevicesToTieSourceTo 不为 null,则 将轨道源绑定到 MediaDevices,带有 source 和 mediaDevicesToTieSourceTo。
以 track 为参数运行 source 的 MediaStreamTrack 源特定构建步骤。
返回 track。
要将 track 的 底层源 (underlying source) 初始化 为 source,运行以下步骤
初始化 track.[[Source]] 为 source。
按照 ConstrainablePattern 中的规定,初始化 track 的 [[Capabilities]]、[[Constraints]] 和 [[Settings]]。
要 将轨道源绑定到 MediaDevices,给定 source 和 mediaDevices,运行以下步骤
将 source 添加到 mediaDevices.[[mediaStreamTrackSources]]。
要 停止所有源 (stop all sources) 的 全局对象 (global object)(命名为 globalObject),用户代理 MUST 运行以下步骤
对于每个 相关全局对象 为 globalObject 的 MediaStreamTrack 对象 track,将 track 的 [[ReadyState]] 设置为 "ended"(已结束)。
如果 globalObject 是一个 Window,则对于 globalObject 的 关联的 MediaDevices.[[mediaStreamTrackSources]] 中的每个 source,停止 (stop) 该 source。
用户代理 MUST 在以下条件下 停止所有源 的 globalObject
如果 globalObject 是一个 WorkerGlobalScope 对象,并且其 关闭 (closing) 标志被设置为 true。
实现可以使用按源引用计数来跟踪源的使用情况,但具体细节超出了本规范的范围。
要 克隆轨道 (clone a track),用户代理 MUST 运行以下步骤
设 track 为要被克隆的 MediaStreamTrack 对象。
设 source 为 track 的 [[Source]]。
设 trackClone 为 创建 MediaStreamTrack 的结果,带有 source 和 null。
将 trackClone 的 [[ReadyState]] 设置为 track 的 [[ReadyState]] 值。
将 trackClone 的 [[Capabilities]] 设置为 track 的 [[Capabilities]] 的克隆。
将 trackClone 的 [[Constraints]] 设置为 track 的 [[Constraints]] 的克隆。
将 trackClone 的 [[Settings]] 设置为 track 的 [[Settings]] 的克隆。
以 track 和 trackClone 为参数运行 source 的 MediaStreamTrack 源特定克隆步骤。
返回 trackClone。
有两个维度与“live”MediaStreamTrack 的媒体流相关:静音/非静音,以及启用/禁用。
静音 (Muted) 指的是 MediaStreamTrack 的输入。当 MediaStreamTrack 的源为 静音 时,即暂时无法向轨道提供数据时,该轨道为 静音。当 MediaStreamTrack 处于 静音 状态时,MUST NOT 向其提供实时样本。
静音 状态不受 Web 应用程序控制,但可以通过读取 muted 属性以及监听相关事件 mute 和 unmute 来观察。导致 MediaStreamTrack 静音的原因由其 源 (source) 定义。
对于摄像头和麦克风源,静音 的原因是由 实现定义 (implementation-defined) 的。这允许用户代理在以下情况下实现隐私缓解:用户按下麦克风上的物理静音按钮,用户合上带有嵌入式摄像头的笔记本电脑盖,用户切换操作系统中的控件,用户点击 用户代理 界面中的静音按钮,用户代理(代表用户)进行静音等。
在某些操作系统上,当另一个具有更高音频优先级的应用程序获取麦克风访问权限时,麦克风访问权限可能会从 用户代理 处被抢占,例如在移动操作系统上来电时。用户代理 SHOULD 通过 muted 及其相关事件将此信息提供给 Web 应用程序。
每当 用户代理 为摄像头或麦克风源发起此类 实现定义 的更改时,它 MUST 使用用户交互任务源来排队一个任务,以 设置轨道的静音状态 (set a track's muted state) 为用户所需的状态。
要将轨道的 静音状态设置为 (set a track's muted state) newState,用户代理 MUST 运行以下步骤
设 track 为所涉及的 MediaStreamTrack。
如果 track.[[Muted]] 已经是 newState,则终止这些步骤。
将 track.[[Muted]] 设置为 newState。
在 track 上触发一个事件 (Fire an event),名称为 eventName。
另一方面,启用/禁用 (Enabled/disabled) 可供应用程序通过 enabled 属性进行控制(和观察)。
对于消费者而言,结果是相同的,即当 MediaStreamTrack 静音或禁用(或两者兼有)时,消费者获得零信息内容,这意味着音频为静音,视频为黑帧。换句话说,只有当 MediaStreamTrack 对象既未静音又已启用时,来自源的媒体才会流动。例如,一个由 MediaStream 提供的视频元素,其中仅包含音频和视频的静音或禁用 MediaStreamTrack,正在播放但渲染的是静音的黑视频帧。
对于新创建的 MediaStreamTrack 对象,适用以下规定:除非另有说明(例如克隆时),否则轨道始终处于启用状态,并且静音状态反映了轨道创建时源的状态。
一个 MediaStreamTrack 在其生命周期中有两种状态:live 和 ended。新创建的 MediaStreamTrack 根据其创建方式可能处于任一状态。例如,克隆已结束的轨道会导致一个新的已结束轨道。当前状态由对象的 readyState 属性反映。
在 live 状态下,轨道是活跃的,并且消费者可以使用媒体(如果 MediaStreamTrack 是 静音 或 禁用,则为零信息内容)。
如果源是由 navigator.mediaDevices.getUserMedia() 公开的设备,那么当轨道变为静音或禁用,且这导致所有连接到该设备的轨道都变为静音、禁用或停止时,UA MAY 使用该设备的 deviceId(deviceId)将 navigator.mediaDevices.[[devicesLiveMap]][deviceId] 设置为 false,前提是 UA 在任何连接到此设备的未停止轨道再次变为非静音或启用时,立即将其设置回 true。
当由 getUserMedia() 暴露的设备所提供的 "live"(实时)、未静音且启用的轨道变为静音或禁用状态,且这导致连接到该设备的所有轨道(跨越用户代理操作的可导航对象)均被静音、禁用或停止时,用户代理 应当 在 3 秒内放弃设备,同时留出时间让观察敏锐的用户注意到这一转换。如果该设备提供的任何实时轨道再次变为未静音且启用状态,且该轨道的相关全局对象的关联 Document 在当时处于视图中,则用户代理 应当 尝试重新获取该设备。如果文档在当时不在视图中,用户代理 应当 改为排队一个任务来静音该轨道,并且在文档进入视图中之前,不排队任何取消静音的任务。如果重新获取设备失败,用户代理 必须 结束该轨道(如果用户代理检测到设备问题,例如设备被物理移除,则 可以 更早地结束它)。
其目的是通过统一物理和逻辑上的“隐私指示器”,至少在当前文档是设备唯一用户的情况下,给予用户物理摄像头(和麦克风)硬件指示灯关闭所带来的隐私保证。
虽然其他同时使用该设备的应用程序和文档有时可能会干扰这一意图,但它们不会干扰所设定的规则。
当 MediaStreamTrack 对象所属的源被断开或耗尽时,称该对象结束。
如果所有使用同一源的 MediaStreamTrack 都已结束,则该源将被停止。
在应用程序调用 MediaStreamTrack 对象上的 stop() 方法后,或者 source(源)永久停止为其轨道生成实时样本后,无论哪个发生得更早,MediaStreamTrack 都被称为已结束。
对于摄像头和麦克风源,除 stop() 之外,源结束的原因由实现定义(例如,用户撤销了页面使用本地摄像头的权限,或用户代理因任何原因指示轨道结束)。
当 MediaStreamTrack track 因调用 stop() 方法之外的任何原因而结束时,用户代理 必须 排队一个任务以执行以下步骤
如果 track 的 [[ReadyState]] 已经具有 "ended" 值,则中止这些步骤。
将 track 的 [[ReadyState]] 设置为 "ended"。
通知 track 的 [[Source]] 该 track 已结束,以便停止该源,除非其他 MediaStreamTrack 对象依赖于它。
如果轨道是在用户请求下到达结束状态的,则此事件的事件源为用户交互事件源。
要使用 permissionName 调用设备权限撤销算法,请执行以下步骤
令 tracks 为当前所有 "live" 的 MediaStreamTrack 集合,其与此类轨道关联的权限("camera" 或 "microphone")匹配 permissionName。
对于 tracks 中的每个 track,结束该轨道。
MediaStreamTrack 是 可约束模式 (Constrainable Pattern) 一节中定义的可约束对象。约束设置在轨道上,并可能影响源。
无论 是在轨道初始化时提供还是需要在运行时稍后建立,ConstraintsConstrainablePattern 接口中定义的 API 均允许检索和操作当前在轨道上建立的约束。
一旦结束,轨道将继续公开一个固有可约束轨道属性列表。该列表包含 deviceId、facingMode 和 groupId。
WebIDL[Exposed=Window]
interface MediaStreamTrack : EventTarget {
readonly attribute DOMString kind;
readonly attribute DOMString id;
readonly attribute DOMString label;
attribute boolean enabled;
readonly attribute boolean muted;
attribute EventHandler onmute;
attribute EventHandler onunmute;
readonly attribute MediaStreamTrackState readyState;
attribute EventHandler onended;
MediaStreamTrack clone();
undefined stop();
MediaTrackCapabilities getCapabilities();
MediaTrackConstraints getConstraints();
MediaTrackSettings getSettings();
Promise<undefined> applyConstraints(optional MediaTrackConstraints constraints = {});
};
kind,类型为 DOMString,只读id,类型为 DOMString,只读label,类型为 DOMString,只读enabled,类型为 boolean该 enabled 属性控制对象的启用状态。
获取时,必须 返回 this.[[Enabled]]。设置时,必须 将 this.[[Enabled]] 设置为新值。
因此,在 MediaStreamTrack 结束后,其 enabled 属性在设置时仍会改变值;只是它不会对该新值采取任何操作。
muted,类型为 boolean,只读onmute,类型为 EventHandler此事件处理程序的事件类型是 mute。
onunmute,类型为 EventHandler此事件处理程序的事件类型是 unmute。
readyState,类型为 MediaStreamTrackState,只读获取时,readyState 属性 必须 返回 this.[[ReadyState]]。
onended,类型为 EventHandler此事件处理程序的事件类型是 ended。
clonestop当调用 MediaStreamTrack 对象的 stop() 方法时,用户代理 必须 执行以下步骤
令 track 为当前的 MediaStreamTrack 对象。
如果 track 的 [[ReadyState]] 为 "ended",则中止这些步骤。
通知 track 的源该 track 已结束。
被通知轨道结束的源将被停止,除非有其他 MediaStreamTrack 对象依赖于它。
将 track 的 [[ReadyState]] 设置为 "ended"。
getCapabilities返回此 MediaStreamTrack(即可约束对象)所代表的源的能力。
有关此方法的定义,请参见 ConstrainablePattern 接口。
由于此方法提供了关于底层设备的可能持久的跨源信息,它增加了该设备的指纹识别面。![]()
getConstraints有关此方法的定义,请参见 ConstrainablePattern 接口。
getSettings当调用 MediaStreamTrack 对象所属的 MediaStreamTrack.getSettings() 方法时,用户代理 必须 执行以下步骤
令 track 为当前的 MediaStreamTrack 对象。
如果 track 的 [[ReadyState]] 为 "ended",则执行以下子步骤
令 settings 为一个新的 MediaTrackSettings 字典。
对于固有可约束轨道属性列表中的每个 property,如果 track 在结束时具有该属性,则将其作为对应属性添加到 settings 中,并使用 track 结束时的值。
返回 settings。
返回 ConstrainablePattern 接口中定义的轨道当前设置。
applyConstraints当调用 MediaStreamTrack 对象所属的 applyConstraints() 方法时,用户代理 必须 执行以下步骤
令 track 为当前的 MediaStreamTrack 对象。
如果 track 的 [[ReadyState]] 为 "ended",则执行以下子步骤
令 p 为一个新的 promise。
解析 (resolve) p 为 undefined。
返回 p。
调用并返回 applyConstraints 模板方法的结果,其中
MediaStreamTrack,且MediaTrackSettings 字典的一个可能实例。用户代理 必须禁止 包含固有的不可变设备属性作为成员,除非它们位于固有可约束轨道属性列表中,或者以其他方式包含不得暴露的设备属性。其他规范可能会定义有时不得暴露的可约束属性。
对于每个将 resizeMode 设置为 "none" 的设置字典,用户代理 必须 包含另一个除此以外完全相同的将 resizeMode 设置为 "crop-and-scale" 的设置字典。不支持围绕非原生模式进行约束。
最终效果是反映出 crop-and-scale 是 none 的超集。
WebIDLenum MediaStreamTrackState {
"live",
"ended"
};
| 枚举值 | 描述 |
|---|---|
实时的 (live) |
轨道处于活动状态(轨道的底层媒体源正在尽最大努力尝试实时提供数据)。 |
ended |
轨道已经结束(轨道的底层媒体源不再提供数据,且永远不会再为该轨道提供数据)。一旦轨道进入此状态,它将永远不会退出。 例如,当用户拔掉作为轨道媒体源的 USB 网络摄像头时, |
MediaTrackSupportedConstraints 表示 用户代理 用于控制 MediaStreamTrack 对象能力的已知约束列表。此字典用作函数返回值,从不作为操作参数使用。
未来的规范可以通过定义包含类型为 boolean 的字典成员的局部字典来扩展 MediaTrackSupportedConstraints 字典。
除非其他规范中另有说明,否则本规范中指定的约束仅适用于由 MediaDevices.getUserMedia() 生成的 MediaStreamTrack 实例。
WebIDLdictionary MediaTrackSupportedConstraints {
boolean width = true;
boolean height = true;
boolean aspectRatio = true;
boolean frameRate = true;
boolean facingMode = true;
boolean resizeMode = true;
boolean sampleRate = true;
boolean sampleSize = true;
boolean echoCancellation = true;
boolean autoGainControl = true;
boolean noiseSuppression = true;
boolean latency = true;
boolean channelCount = true;
boolean deviceId = true;
boolean groupId = true;
boolean backgroundBlur = true;
};
width,类型为 boolean,默认值为 trueheight,类型为 boolean,默认值为 trueaspectRatio,类型为 boolean,默认值为 trueframeRate,类型为 boolean,默认值为 truefacingMode,类型为 boolean,默认值为 trueresizeMode,类型为 boolean,默认值为 truesampleRate,类型为 boolean,默认值为 truesampleSize,类型为 boolean,默认值为 trueechoCancellation,类型为 boolean,默认值为 trueautoGainControl,类型为 boolean,默认值为 truenoiseSuppression,类型为 boolean,默认值为 truelatency,类型为 boolean,默认值为 truechannelCount,类型为 boolean,默认值为 truedeviceId,类型为 boolean,默认值为 truegroupId,类型为 boolean,默认值为 truebackgroundBlur,类型为 boolean,默认值为 trueMediaTrackCapabilities 表示 MediaStreamTrack 对象的能力。
未来的规范可以通过定义包含适当类型成员的局部字典来扩展 MediaTrackCapabilities 字典。
WebIDLdictionary MediaTrackCapabilities {
ULongRange width;
ULongRange height;
DoubleRange aspectRatio;
DoubleRange frameRate;
sequence<DOMString> facingMode;
sequence<DOMString> resizeMode;
ULongRange sampleRate;
ULongRange sampleSize;
sequence<(boolean or DOMString)> echoCancellation;
sequence<boolean> autoGainControl;
sequence<boolean> noiseSuppression;
DoubleRange latency;
ULongRange channelCount;
DOMString deviceId;
DOMString groupId;
sequence<boolean> backgroundBlur;
};
由于历史原因,deviceId 和 groupId 是 DOMString,而不是 Capabilities 在 中所期望的 ConstrainablePatternsequence<DOMString>。
MediaTrackCapabilities 成员width,类型为 ULongRangeheight,类型为 ULongRangeaspectRatio,类型为 DoubleRangeframeRate,类型为 DoubleRangefacingMode,类型为 sequence<DOMString>摄像头可以报告多种朝向模式。例如,在具有多个面向用户的摄像头的高端网真解决方案中,用户左侧的摄像头可以同时报告 "left" 和 "user"。详见 facingMode。
resizeMode,类型为 sequence<DOMString>用户代理 可以 使用裁剪和降采样来提供比摄像头原生生成的更多的分辨率选择。所报告的序列 必须 列出用户代理可能用来为该摄像头衍生分辨率选择的所有方法。值 "none" 必须 存在,表明能够限制用户代理进行裁剪和降采样。详见 resizeMode。
sampleRate,类型为 ULongRangesampleSize,类型为 ULongRangeechoCancellation,类型为 sequence<boolean>如果源不能进行回声消除,则列表中的唯一元素 必须 是单个 false。如果源可以进行回声消除,则 true 必须 包含在列表中。如果脚本可以控制此功能,则列表 必须 至少包含 true 和 false。此外,如果源允许控制哪些音频源将被消除,则必须包含 EchoCancellationModeEnum 枚举中支持的所有值。如果列表中包含 true 或 false,则它们必须出现在 EchoCancellationModeEnum 的任何值之前。详见 echoCancellation。
autoGainControl,类型为 sequence<boolean>如果源不能进行自动增益控制,则报告单个 false。如果自动增益控制不能关闭,则报告单个 true。如果脚本可以控制此功能,源将报告一个包含 true 和 false 作为可能值的列表。详见 autoGainControl。
noiseSuppression,类型为 sequence<boolean>如果源不能进行噪声抑制,则报告单个 false。如果噪声抑制不能关闭,则报告单个 true。如果脚本可以控制此功能,源将报告一个包含 true 和 false 作为可能值的列表。详见 noiseSuppression。
latency,类型为 DoubleRangechannelCount,类型为 ULongRangedeviceId,类型为 DOMStringgroupId,类型为 DOMStringbackgroundBlur,类型为 sequence<boolean>false。如果背景模糊不能关闭,则报告单个 true。如果脚本可以控制此功能,源将报告一个包含 true 和 false 作为可能值的列表。详见 backgroundBlur。WebIDLdictionary MediaTrackConstraints : MediaTrackConstraintSet {
sequence<MediaTrackConstraintSet> advanced;
};
MediaTrackConstraints 成员advanced,类型为 sequence<MediaTrackConstraintSet>有关此元素的定义,请参见 Constraints and ConstraintSet。
未来的规范可以通过定义包含适当类型成员的局部字典来扩展 MediaTrackConstraintSet 字典。
WebIDLdictionary MediaTrackConstraintSet {
ConstrainULong width;
ConstrainULong height;
ConstrainDouble aspectRatio;
ConstrainDouble frameRate;
ConstrainDOMString facingMode;
ConstrainDOMString resizeMode;
ConstrainULong sampleRate;
ConstrainULong sampleSize;
ConstrainBooleanOrDOMString echoCancellation;
ConstrainBoolean autoGainControl;
ConstrainBoolean noiseSuppression;
ConstrainDouble latency;
ConstrainULong channelCount;
ConstrainDOMString deviceId;
ConstrainDOMString groupId;
ConstrainBoolean backgroundBlur;
};
MediaTrackConstraintSet 成员width,类型为 ConstrainULongheight,类型为 ConstrainULongaspectRatio,类型为 ConstrainDoubleframeRate,类型为 ConstrainDoublefacingMode,类型为 ConstrainDOMStringresizeMode,类型为 ConstrainDOMStringsampleRate,类型为 ConstrainULongsampleSize,类型为 ConstrainULongechoCancellation,类型为 ConstrainBooleanOrDOMStringautoGainControl,类型为 ConstrainBooleannoiseSuppression,类型为 ConstrainBooleanlatency,类型为 ConstrainDoublechannelCount,类型为 ConstrainULongdeviceId,类型为 ConstrainDOMStringgroupId,类型为 ConstrainDOMStringbackgroundBlur,类型为 ConstrainBooleanMediaTrackSettings 表示 MediaStreamTrack 对象设置。
未来的规范可以通过定义包含适当类型成员的局部字典来扩展 MediaTrackSettings 字典。
WebIDLdictionary MediaTrackSettings {
unsigned long width;
unsigned long height;
double aspectRatio;
double frameRate;
DOMString facingMode;
DOMString resizeMode;
unsigned long sampleRate;
unsigned long sampleSize;
(boolean or DOMString) echoCancellation;
boolean autoGainControl;
boolean noiseSuppression;
double latency;
unsigned long channelCount;
DOMString deviceId;
DOMString groupId;
boolean backgroundBlur;
};
MediaTrackSettings 成员width,类型为 unsigned longheight,类型为 unsigned longaspectRatio,类型为 doubleframeRate,类型为 doublefacingMode,类型为 DOMStringresizeMode,类型为 DOMStringsampleRate,类型为 unsigned longsampleSize,类型为 unsigned longechoCancellation,类型为 boolean 或 DOMStringautoGainControl,类型为 booleannoiseSuppression,类型为 booleanlatency,类型为 doublechannelCount,类型为 unsigned longdeviceId,类型为 DOMStringgroupId,类型为 DOMStringbackgroundBlur,类型为 boolean,默认值为 trueMediaStreamTrack 的初始可约束属性集名称定义如下。
以下可约束属性定义为适用于视频和音频 MediaStreamTrack 对象
| 属性名称 | 类型 | 注 |
|---|---|---|
| deviceId | DOMString |
生成 MediaStreamTrack 内容的设备标识符。它符合 MediaDeviceInfo.deviceId 的定义。注意,此属性的设置由附加到 MediaStreamTrack 的源唯一确定。特别是,getCapabilities() 对于 deviceId 将只返回单个值。因此,该属性可用于使用 getUserMedia() 进行初始媒体选择。然而,它对于随后的媒体控制(使用 applyConstraints())没有用处,因为任何设置不同值的尝试都将导致不可满足的 ConstraintSet。如果长度为 0 的字符串被用作 getUserMedia() 的 deviceId 值约束,它 可以 被解释为好像没有指定该约束。 |
| groupId | DOMString |
生成 MediaStreamTrack 内容的设备的文档唯一组标识符。它符合 MediaDeviceInfo.groupId 的定义。注意,此属性的设置由附加到 MediaStreamTrack 的源唯一确定。特别是,getCapabilities() 对于 groupId 将只返回单个值。由于此属性在浏览会话之间不稳定,其在 getUserMedia() 中的初始媒体选择作用有限。它对于随后的媒体控制(使用 applyConstraints())没有用处,因为任何设置不同值的尝试都将导致不可满足的 ConstraintSet。 |
以下可约束属性定义为仅适用于视频 MediaStreamTrack 对象
| 属性名称 | 类型 | 注 |
|---|---|---|
| width | unsigned long |
宽度(像素)。作为能力,其有效范围应跨越视频源预设的宽度值,min 等于 1,max 为最大宽度。用户代理 必须 支持降采样至最小宽度范围值与原生分辨率宽度之间的任何值。 |
| height | unsigned long |
高度(像素)。作为能力,其有效范围应跨越视频源预设的高度值,min 等于 1,max 为最大高度。用户代理 必须 支持降采样至最小高度范围值与原生分辨率高度之间的任何值。 |
| frameRate | double |
帧率(每秒帧数)。如果视频源的预设值可以确定帧率,那么作为能力,其有效范围应跨越视频源预设的帧率值,min 等于 0,max 为最大帧率。用户代理 必须 支持从原生分辨率帧率进行整数抽取(decimation)而获得的帧率。如果无法确定帧率(例如,源没有原生提供帧率,或者无法从源流确定帧率),则能力值 必须 参考用户代理的垂直同步显示刷新率。 作为设置,此值代表配置的帧率。如果使用了抽取,这就是该值,而不是原生帧率。例如,如果通过抽取设置的帧率为每秒 25 帧,摄像头的原生帧率为每秒 30 帧,但由于光照条件仅达到每秒 20 帧, |
| aspectRatio | double |
确切的宽高比(以像素为单位的宽度除以以像素为单位的高度,表示为四舍五入到小数点后十位的双精度浮点数)或宽高比范围。 |
| facingMode | DOMString |
此字符串是 VideoFacingModeEnum 的成员之一。这些成员描述了从用户视角看摄像头可以面对的方向。注意, 可能不会为不在此枚举中的字符串返回完全相同的字符串。这保留了为该属性使用 WebIDL 枚举未来版本的可能性。 |
| resizeMode | DOMString |
此字符串是 VideoResizeModeEnum 的成员之一。这些成员描述了分辨率可以由用户代理衍生出的方式。换句话说,用户代理是否被允许在摄像头输出上使用裁剪和降采样。当使用 "none" 时,用户代理 可以 通过降采样、上采样和/或裁剪来伪装摄像头的并发使用,以模仿原生分辨率,但仅当摄像头在用户代理之外的另一个应用程序中被使用时。 可能不会为不在此枚举中的字符串返回完全相同的字符串。这保留了为该属性使用 WebIDL 枚举未来版本的可能性。 |
| backgroundBlur | boolean |
某些平台或用户代理可能提供对视频帧(特别是摄像头视频流)进行背景模糊的内置支持。Web 应用程序可能希望控制,或至少了解背景模糊是在源级别应用的。例如,这可以允许 Web 应用程序更新其 UI,或者自行决定不应用背景模糊。 |
在系统上,如果希望在响应持续环境因素时自动翻转最终捕获视频的 X 轴和 Y 轴,则 width、height 和 aspectRatio 约束和能力 必须 在所有算法中保持不受影响,且仅在主要方向 (primary orientation) 中被考虑,getSettings() 算法除外,其中如果必要,必须翻转这些可约束属性的设置,以匹配任何时间点捕获视频的返回维度。
支持翻转最终捕获视频 X 轴和 Y 轴的系统的主要方向由用户代理为特定系统定义。
WebIDLenum VideoFacingModeEnum {
"user",
"environment",
"left",
"right"
};
| 枚举值 | 描述 |
|---|---|
user |
源面向用户(自拍摄像头)。 |
环境 |
源背向用户(观察环境)。 |
left |
源面向用户的左侧。 |
right |
源面向用户的右侧。 |
下面是视频朝向模式与用户关系的说明。
WebIDLenum VideoResizeModeEnum {
"none",
"crop-and-scale"
};
| 枚举值 | 描述 |
|---|---|
none(无) |
此分辨率和帧率由摄像头、其驱动程序或操作系统提供。 注意:用户代理 可以 报告此值来伪装并发使用,但仅当摄像头在另一个可导航对象中被使用时。 |
crop-and-scale |
此分辨率由用户代理从更高的摄像头分辨率降采样和/或裁剪而来,或者其帧率由用户代理进行抽取。除下文所述外,媒体 不得 进行上采样、拉伸或创建输入源中未出现的虚假数据。 注意:用户代理 可以 进行上采样来伪装并发使用,但仅当摄像头在用户代理之外的另一个应用程序中被使用时。 |
以下可约束属性定义为仅适用于音频 MediaStreamTrack 对象
| 属性名称 | 值 | 注 |
|---|---|---|
| sampleRate | unsigned long |
音频数据的采样率(每秒采样数)。 |
| sampleSize | unsigned long |
线性采样大小,以位为单位。作为一项约束,它仅适用于产生线性采样的音频设备。 |
| echoCancellation | boolean 或 DOMString |
这可以是 false、true,或者是 EchoCancellationModeEnum 的成员之一。当一个或多个音频流在各种麦克风的处理过程中播放时,通常需要尝试从麦克风记录的输入信号中去除正在播放的声音。这被称为回声消除。在某些情况下,不需要回声消除,为了不引入音频伪影,最好将其关闭。这允许应用程序控制此行为。 |
| autoGainControl | boolean |
自动增益控制通常对于麦克风记录的输入信号是理想的。在某些情况下,不需要自动增益控制,为了不改变音频,最好将其关闭。这允许应用程序控制此行为。 |
| noiseSuppression | boolean |
噪声抑制通常对于麦克风记录的输入信号是理想的。在某些情况下,不需要噪声抑制,为了不改变音频,最好将其关闭。这允许应用程序控制此行为。 |
| 延迟 | double |
延迟或延迟范围,以秒为单位。延迟是指从处理开始(例如,当现实世界中出现声音时)到数据可供流程中的下一步使用之间的时间。低延迟对于某些应用程序至关重要;高延迟对于其他应用程序可能是可以接受的,因为它有助于满足功耗约束。该数字应为配置的目标延迟;实际延迟可能与该值略有偏差。 |
| channelCount | unsigned long |
音频数据包含的独立声道数量,即每个采样帧的音频采样数。 |
WebIDLenum EchoCancellationModeEnum {
"all",
"remote-only"
};
| 枚举值 | 描述 |
|---|---|
"all" |
系统 必须 尝试从麦克风的输入信号中去除系统正在播放的所有声音。 此选项旨在提供最大程度的隐私,因为它防止了诸如通知或屏幕阅读器等本地音频的传输。 |
"remote-only" |
系统 必须 尝试从源自 WebRTC 由 UA 决定取消哪些 |
除了 EchoCancellationModeEnum 中的值外,echoCancellation 可约束属性还接受 true 和 false 值。false 意味着不进行任何回声消除。true 意味着由 UA 决定从麦克风记录的信号中去除哪些音频。true 必须 尝试取消至少与 "remote-only" 相同程度的音频,并 应该 尝试取消与 "all" 相同程度的音频。
如果 MediaStreamTrack 对象未 结束,且注册了针对 mute、unmute 或 ended 事件的任何事件监听器,则该对象 不得 被垃圾回收。每个源类型可以进一步细化垃圾回收规则,因为源可能永远不会触发特定的事件。
MediaStreamTrack 上调用 stop(),尤其是捕获轨道,因为底层资源成本高昂,并且会对向用户呈现的隐私指示器产生影响。addtrack 和 removetrack 事件使用 MediaStreamTrackEvent 接口。
addtrack 和 removetrack 事件通知脚本 MediaStream 的 轨道集 已被 用户代理 更新。
触发名为 e 的轨道事件 并带有 MediaStreamTrack track 意味着必须在给定目标上创建并分发一个名称为 e 的事件,该事件不冒泡(除非另有说明),不可取消(除非另有说明),并使用 MediaStreamTrackEvent 接口,且 track 属性设置为 track,必须 创建并分发该事件。
WebIDL[Exposed=Window]
interface MediaStreamTrackEvent : Event {
constructor(DOMString type, MediaStreamTrackEventInit eventInitDict);
[SameObject] readonly attribute MediaStreamTrack track;
};
constructor()构造一个新的 MediaStreamTrackEvent。
track 类型为 MediaStreamTrack,只读track 属性表示与事件关联的 MediaStreamTrack 对象。
WebIDLdictionary MediaStreamTrackEventInit : EventInit {
required MediaStreamTrack track;
};
track 类型为 MediaStreamTrack,必填本节是非规范性的。
用户代理提供从源到宿的媒体管道。在用户代理中,宿是 <img>、<video> 和 <audio> 标签。传统源包括流式内容、文件和 Web 资源。这些源产生的媒体通常不会随时间变化——这些源可以被认为是静态的。
向用户显示这些源的宿(实际的标签本身)具有用于操作源内容的各种控件。例如,<img> 标签将巨大的 1600x1200 像素的源图像缩小以适合由 width="400" 和 height="300" 定义的矩形。
源有生命周期。默认情况下,源的生命周期与创建它的上下文绑定。例如,由 MediaDevices.getUserMedia() 创建的源被视为由其 navigator.mediaDevices 上下文创建。类似地,RTCRtpReceiver 对象的源绑定到 RTCPeerConnection 本身,而它又绑定到其创建上下文。除非在特定源的定义中明确说明,否则当其创建上下文消失时,源始终会 停止。需要注意的是,两个不同上下文的源可能同时使用同一个捕获设备。一个源可以独立于另一个源停止。
getUserMedia API 添加了动态源,如麦克风和摄像头——这些源的特性可以根据应用程序的需求而改变。这些源在本质上可以被认为是动态的。显示来自动态源媒体的 <video> 元素既可以执行缩放,也可以将信息反馈回媒体管道,并让源产生更适合显示的内容。
注:这种反馈循环显然只是实现了一种“优化”,但这是一种非同小可的收益。这种优化可以节省电量,减少网络拥塞,等等...
请注意,MediaStream 宿(如 <video>、<audio> 甚至 RTCPeerConnection)将继续拥有机制来进一步转换源流,超出本规范描述的 设置、功能 和 约束。(宿转换选项,包括 RTCPeerConnection 的转换选项,超出了本规范的范围。)
更改或应用轨道约束的行为可能会影响共享该源的所有轨道的 设置,从而影响使用该源的所有下层宿。许多宿可能能够轻松应对这些变化,例如 < 元素或 video>RTCPeerConnection。其他如记录器 API 可能会因源设置更改而失败。
RTCPeerConnection 是一个有趣的对象,因为它同时充当网络流的宿 和 源。作为宿,它具有源转换能力(例如,降低比特率、放大/缩小分辨率以及调整帧率),而作为源,它自己的设置可能会被轨道源更改。
为了说明对给定源的更改如何影响各种宿,请考虑以下示例。此示例仅使用宽度和高度,但相同的原则适用于本规范中公开的所有 设置。在第一幅图中,家庭客户端已从其本地摄像机获得了视频源。源的宽度和高度设置分别为 800 像素和 600 像素。家庭客户端上的三个 MediaStream 对象包含使用此相同 deviceId 的轨道。这三个媒体流连接到三个不同的宿:一个 < 元素 (A)、另一个 video>< 元素 (B) 和一个对等连接 (C)。对等连接正在将源视频流式传输到远程客户端。在远程客户端上,有两个媒体流,其轨道使用对等连接作为源。这两个媒体流连接到两个 video>< 元素宿(Y 和 Z)。video>
请注意,此时家庭客户端上的所有宿都必须对原始源提供的尺寸设置应用转换。B 正在缩小视频,A 正在放大视频(导致质量损失),C 也正在稍微放大视频以进行网络发送。在远程客户端上,宿 Y 正在将视频大幅缩小,而宿 Z 未应用任何缩放。
为了响应调用 applyConstraints(),其中一个轨道需要来自家庭客户端视频源的更高分辨率(1920 x 1200 像素)。
请注意,源的更改会立即影响家庭客户端上的所有轨道和宿,但不会影响远程客户端上的任何宿(或源)。随着家庭客户端源视频尺寸的增加,宿 A 不再需要执行任何缩放,而宿 B 必须比以前进一步缩小。宿 C(对等连接)现在必须缩小视频,以保持到远程客户端的传输恒定。
虽然未显示,但在远程客户端一侧可以提出同样有效的设置更改请求。除了以与之前影响 A、B 和 C 相同的方式影响宿 Y 和 Z 之外,它还可能导致与家庭客户端上的对等连接重新协商,以更改其应用于家庭客户端视频源的转换。此类更改 不需要 更改任何与宿 A 或 B 或家庭客户端视频源相关的内容。
请注意,本规范没有定义一种机制,使远程客户端视频源的更改能够自动触发家庭客户端视频源的更改。实现可以选择进行此类源到宿的优化,只要它们仅在应用程序建立的约束范围内进行,如下一个示例所示。
显而易见,对给定源的更改将影响宿消费者。然而,在某些情况下,对给定宿的更改也可能导致实现调整源的设置。这在下图中有所说明。在下方的第一幅图中,家庭客户端的视频源正在发送大小为 1920 x 1200 像素的视频流。视频源也是无约束的,因此就应用程序而言,确切的源尺寸是灵活的。两个 MediaStream 对象包含具有相同 deviceId 的轨道,并且这些 MediaStream 连接到两个不同的 < 元素宿 A 和 B。宿 A 的尺寸设置为 video>width="1920" 和 height="1200",并且在没有任何转换的情况下显示源的视频内容。宿 B 的尺寸设置得较小,因此正在将视频缩小以适合其 320 像素宽和 200 像素高的矩形。
当应用程序将宿 A 更改为较小尺寸(从 1920 x 1200 像素宽变为 1024 x 768 像素高)时,用户代理的媒体管道可能会意识到其宿都不需要更高的源分辨率,并且源和宿 A 都在做无用功。在这种情况下,并且没有任何其他约束强制源继续产生更高分辨率的视频,媒体管道 可以 更改源分辨率。
在上图中,为了优化播放,家庭客户端的视频源分辨率被更改为宿 A 和宿 B 中的较大值。虽然上图中未显示,但相同的行为可以应用于对等连接和其他宿。
可能对轨道应用了源无法满足的 约束,原因要么是源本身无法满足该约束,要么是源已经在满足冲突的约束。发生这种情况时,applyConstraints() 返回的 promise 将被 拒绝,且不会应用任何新约束。由于在这种情况下约束没有发生变化,因此由于此条件,源本身也不需要发生变化。这是此行为的一个示例。
在此示例中,两个媒体流每个都有一个共享同一个源的视频轨道。第一个轨道最初没有应用约束。它连接到宿 N。宿 N 的分辨率为 800 x 600 像素,并且正在缩小源的 1024 x 768 分辨率以进行适应。另一个轨道有一个强制关闭源补光灯的 必要约束;它连接到宿 P。宿 P 的宽度和高度等于源的宽度和高度。
现在,第一个轨道添加了一个强制打开补光灯的 必要约束。此时,源无法同时满足两个 必要约束(补光灯不能同时打开和关闭)。由于此状态是由第一个轨道试图应用冲突的约束引起的,因此约束应用失败,源的设置或任一轨道上的约束都没有变化。
MediaStream 可以分配给媒体元素。MediaStream 不可预加载或可寻址,并且表示一个简单的、可能是无限的、线性的 媒体时间线。时间线从 0 开始,只要媒体元素 可能在播放,它就会实时线性递增。当 MediaStream 的播放暂停时,时间线不会递增。
支持本规范的 用户代理 必须 支持 [HTML] 中定义的 HTMLMediaElement 接口的 srcObject 属性,其中包括对播放 MediaStream 对象的支持。
[HTML] 文档概述了 HTMLMediaElement 如何与 媒体提供程序对象 一起工作。当 媒体提供程序对象 是 MediaStream 时,以下内容适用
每当创建 AudioTrack 或 VideoTrack 时,id 和 label 属性必须初始化为 MediaStreamTrack 的相应属性,kind 属性必须初始化为 "main",language 属性初始化为空字符串。
MediaStream 的当前数据,且 不得 进行缓冲。由于 MediaStream 的 轨道集 中的顺序未定义,因此对 AudioTrackList 和 VideoTrackList 的排序方式没有任何要求。
如果元素是 HTMLVideoElement,则在它结束视频播放时,即在以下情况下,称其具有 已结束播放
元素的 readyState 为 HAVE_METADATA 或更高,并且
MediaStream 的状态在 激活 后变为 不活跃,或者
MediaStream 的状态在上次调用 play() 后,经历了 激活、不活跃 后再次变为 激活,并且 autoplay 为 false。
一旦播放结束,如果新的 MediaStreamTrack 被添加到 MediaStream 中,除非 autoplay 为 true 或元素被重启(例如,通过 Web 应用程序调用 play()),否则播放不会恢复。
如果元素是 HTMLAudioElement,则在它结束音频播放时,即在以下情况下,称其具有 已结束播放
元素的 readyState 为 HAVE_METADATA 或更高,并且
MediaStream 的状态在 可听 后变为 不可听,或者
MediaStream 的状态在上次调用 play() 后,经历了 可听、不可听 后再次变为 可听,并且 autoplay 为 false。
一旦播放结束,如果新的音频 MediaStreamTrack 被添加到 MediaStream 中,除非 autoplay 为 true 或元素被重启(例如,通过 Web 应用程序调用 play()),否则播放不会恢复。
对 HTMLMediaElement 上 fastSeek() 方法的任何调用都必须被忽略。
MediaStream 的特性对相关 HTMLMediaElement 属性的行为以及可对其执行的操作施加了某些限制,如下所示。
| 属性名称 | 属性类型 | 当提供程序为 MediaStream 时 Setter/Getter 的行为 | 附加考虑 |
|---|---|---|---|
预加载
|
DOMString |
获取时:none。设置时:被忽略。 |
MediaStream 无法预加载。 |
buffered
|
TimeRanges
|
buffered.length 必须 返回 0。 |
MediaStream 无法预加载。因此,缓冲量始终是空的时间范围。 |
currentTime
|
double |
任何非负整数。初始值为 0,并且每当元素 可能在播放 时,值都会实时线性递增。 |
该值是 官方播放位置,以秒为单位。任何尝试更改它的行为 必须 被忽略。 |
seeking
|
boolean |
false |
MediaStream 不可寻址。因此,此属性 必须 始终返回 false。 |
defaultPlaybackRate
|
double |
获取时:1.0。设置时:被忽略。 |
MediaStream 不可寻址。因此,此属性 必须 始终返回 1.0,任何尝试更改它的行为 必须 被忽略。请注意,这也意味着 ratechange 事件将不会触发。 |
playbackRate
|
double |
获取时:1.0。设置时:被忽略。 |
MediaStream 不可寻址。因此,此属性 必须 始终返回 1.0,任何尝试更改它的行为 必须 被忽略。请注意,这也意味着 ratechange 事件将不会触发。 |
played
|
TimeRanges
|
played.length 必须 返回 1。played.start(0) 必须 返回 0。played.end(0) 必须 返回最后已知的 currentTime。 |
MediaStream 的时间线始终由单个范围组成,从 0 开始,一直延伸到 currentTime。 |
seekable
|
TimeRanges
|
seekable.length 必须 返回 0。 |
MediaStream 不可寻址。 |
loop(循环)
|
boolean |
true, false |
设置 loop 属性没有任何效果,因为 MediaStream 没有定义的终点,因此不能循环播放。 |
由于上述列出的 setter 都没有更改 HTMLMediaElement 的内部状态,因此一旦 MediaStream 不再是元素的 已分配媒体提供程序对象,所列出的属性将看起来恢复了在流分配给元素之前所具有的值。
MediaStream 在 srcObject 被分配 null 或非流对象时,即在 媒体元素加载算法 之前,就不再是元素的 已分配媒体提供程序对象。因此,如果 playbackRate 和 defaultPlaybackRate 与分配 MediaStream 之前不同,则可能会触发 ratechange 事件(来自步骤 7)。
某些操作会抛出或触发 OverconstrainedError。这是 DOMException 的一个扩展,携带与约束失败相关的附加信息。
WebIDL[Exposed=Window]
interface OverconstrainedError : DOMException {
constructor(DOMString constraint, optional DOMString message = "");
readonly attribute DOMString constraint;
};
OverconstrainedError运行以下步骤
令 constraint 为构造函数的第一个参数。
令 message 为构造函数的第二个参数。
令 e 为一个新的 OverconstrainedError 对象。
调用 e 的 DOMException 构造函数,并将 message 参数设置为 message,将 name 参数设置为 "OverconstrainedError"。
此名称没有映射到遗留代码,因此 e 的 code 属性将返回 0。
设置 e.constraint 为 constraint。
返回 e。
constraint 类型为 DOMString,只读与此错误关联的约束名称,如果不揭示特定的约束名称,则为 ""。
本节是非规范性的。
以下事件在 MediaStream 对象上触发
| 事件名称 | Interface | 触发于... |
|---|---|---|
| addtrack | MediaStreamTrackEvent |
一个新的 MediaStreamTrack 已添加到此流中。请注意,当脚本直接修改 MediaStream 的轨道时,不会触发此事件。 |
| removetrack | MediaStreamTrackEvent |
一个 MediaStreamTrack 已从此流中移除。请注意,当脚本直接修改 MediaStream 的轨道时,不会触发此事件。 |
以下事件在 MediaStreamTrack 对象上触发
| 事件名称 | Interface | 触发于... |
|---|---|---|
| mute | Event |
MediaStreamTrack 对象的源暂时无法提供数据。 |
| unmute | Event |
MediaStreamTrack 对象的源在暂时无法提供数据后又恢复活跃。 |
| ended | Event |
|
以下事件在 MediaDevices 对象上触发
| 事件名称 | Interface | 触发于... |
|---|---|---|
| devicechange | DeviceChangeEvent |
用户代理可用的媒体设备集已更改。当前的设备列表可在 devices 属性中获得。 |
本节描述了脚本可用于向用户代理查询已连接媒体输入和输出设备(例如网络摄像头或耳机)的 API。
MediaDevicesMediaDevices 对象是用于检查并获取对 用户代理 可用媒体设备访问权限的 API 的入口点。
要 创建 MediaDevices 对象,给定 realm,运行以下步骤
令 mediaDevices 为 realm 中的一个新 MediaDevices 对象,并用以下内部插槽初始化
令 settings 为 mediaDevices 的 相关设置对象。
对于 MediaDevices.getUserMedia() 公开的每种设备类型 kind,运行以下步骤
如果 settings 的与 kind(例如 "camera"、"microphone")关联的权限的 权限状态 为 "granted",则设置 mediaDevices.[[kindsAccessibleMap]][kind] 为 true,否则设置为 false。
对于 MediaDevices.getUserMedia() 公开的每个单独设备,使用设备的 deviceId,deviceId,运行以下步骤
设置 mediaDevices.[[devicesLiveMap]][deviceId] 为 false,如果 settings 的与设备类型和 deviceId 关联的权限的 权限状态 为 "granted",则设置 mediaDevices.[[devicesAccessibleMap]][deviceId] 为 true,否则设置为 false。
返回 mediaDevices。
对于 getUserMedia() 公开的每种设备类型 kind,每当发生权限状态转换时,对于 mediaDevices 的 相关设置对象,如果该转换是与 kind 关联的权限,则运行以下步骤
如果转换是从其他值变为 "granted",则设置 mediaDevices.[[kindsAccessibleMap]][kind] 为 true。
如果转换是从 "granted" 变为其他值,则设置 mediaDevices.[[kindsAccessibleMap]][kind] 为 false。
对于 getUserMedia() 公开的每个设备,每当发生与设备类型和设备 deviceId (deviceId) 关联的权限的 权限状态 转换时,对于 mediaDevices 的 相关设置对象,运行以下步骤
如果转换是从其他值变为 "granted",则设置 mediaDevices.[[devicesAccessibleMap]][deviceId] 为 true(如果它尚未为 true)。
如果转换是从 "granted" 变为其他值,且该设备当前已 停止,则设置 mediaDevices.[[devicesAccessibleMap]][deviceId] 为 false。
当新的媒体输入和/或输出设备对 用户代理 可用,或者任何可用设备变得不可用,或者 MediaDeviceKind 的系统默认输入/输出设备发生更改时,用户代理 必须 为每个 MediaDevices 对象 mediaDevices 运行以下 设备更改通知步骤,其中 设备枚举可以进行 为 true,但对于其他 MediaDevices 对象则不然。
令 lastExposedDevices 为 创建设备信息对象列表 (mediaDevices 和 mediaDevices.[[storedDeviceList]]) 的结果。
令 deviceList 为所有对 用户代理 可用的媒体输入和/或输出设备的列表。
令 newExposedDevices 为 创建设备信息对象列表 (mediaDevices 和 deviceList) 的结果。
如果 newExposedDevices 中的 MediaDeviceInfo 对象与 lastExposedDevices 中的对象匹配且顺序相同,则中止这些步骤。
由于 enumerateDevices 算法,上述步骤将 devicechange 事件的触发限制为 允许使用 enumerateDevices 来枚举特定 MediaDeviceKind 设备的文档。
将 mediaDevices.[[storedDeviceList]] 设置为 deviceList。
排队执行一个任务,该任务 触发一个事件,命名为 devicechange,使用 DeviceChangeEvent 构造函数,并将 devices 初始化为 newExposedDevices,在 mediaDevices 上触发。
用户代理 可以 在多个事件到期时或同时添加/移除多个设备(例如带有麦克风的相机)时,将触发多个事件合并为一个事件。
此外,如果遍历到的 MediaDevices 对象稍后满足 设备枚举可以进行 的标准(例如 进入视图),用户代理 必须 在那时对该 MediaDevices 对象执行 设备更改通知步骤。
这些事件可能会在不同来源的文档上同时触发。用户代理 可以 在事件的时间上添加模糊处理,以避免跨源活动关联。![]()
WebIDL[Exposed=Window, SecureContext]
interface MediaDevices : EventTarget {
attribute EventHandler ondevicechange;
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
};
ondevicechange 类型为 EventHandler此事件处理程序的事件类型为 devicechange。
enumerateDevices收集关于 用户代理 可用的媒体输入和输出设备的信息。
此方法返回一个 promise。如果枚举成功,该 promise 将以表示 用户代理 可用媒体输入和输出设备的 MediaDeviceInfo 对象序列进行 完成。
此序列中表示输入设备的元素将是 InputDeviceInfo 类型,它扩展了 MediaDeviceInfo。
摄像头和麦克风源 应该 是可枚举的。添加其他类型源的规范将提供关于源类型是否应该可枚举的建议。
当调用 enumerateDevices() 方法时,用户代理 必须运行以下步骤
令 p 为一个新的 promise。
令 mediaDevices 为 此。
并行执行以下步骤
当 proceed 为 false 时,用户代理 必须 等待继续执行下一步,直到排队执行一个任务将 proceed 设置为 设备枚举可以进行 (mediaDevices) 的结果,该结果会将 proceed 设置为 true。
令 resultList 为 创建设备信息对象列表 (mediaDevices 和 mediaDevices.[[storedDeviceList]]) 的结果。
解析 p 为 resultList。
返回 p。
要执行 创建设备信息对象列表,给定 mediaDevices 和 deviceList,运行以下步骤
令 resultList 为一个空列表。
令 microphoneList、cameraList 和 otherDeviceList 为空列表。
令 document 为 mediaDevices 的 相关全局对象 的 关联 Document。
对 deviceList 中的每个已发现设备 device 运行以下子步骤
如果 device 不是麦克风,或者 document 不 允许使用 由 "microphone" 标识的功能,则中止这些子步骤并继续处理下一个设备(如果有)。
令 deviceInfo 为 创建设备信息对象 (device, mediaDevices) 以表示 device 的结果。
如果 device 是系统默认麦克风,则将 deviceInfo 预置到 microphoneList。否则,将 deviceInfo 追加到 microphoneList。
对 deviceList 中的每个已发现设备 device 运行以下子步骤
如果 mediaDevices 上 麦克风信息可以被公开 为 false,则将 microphoneList 截断为其第一项。
如果 mediaDevices 上 相机信息可以被公开 为 false,则将 cameraList 截断为其第一项。
对 deviceList 中的每个已发现设备 device 运行以下子步骤
如果 device 是麦克风或相机,则中止这些子步骤并继续处理下一个设备(如果有)。
运行 针对除相机和麦克风之外设备的暴露决策算法,并将 device、microphoneList、cameraList 和 mediaDevices 作为输入。如果该算法的结果为 false,则中止这些子步骤并继续处理下一个设备(如果有)。
令 deviceInfo 为 创建设备信息对象 (device, mediaDevices) 以表示 device 的结果。
如果 device 是系统默认音频输出,运行以下子步骤
将 microphoneList 中的所有设备按顺序追加到 resultList。
将 cameraList 中的所有设备按顺序追加到 resultList。
将 otherDeviceList 中的所有设备按顺序追加到 resultList。
返回 resultList。
由于此方法通过媒体捕获设备的可用性在浏览会话和来源之间返回持久信息,它增加了 用户代理 公开的指纹识别表面。![]()
只要相关全局对象的关联 Document 未进行捕获,此方法将仅限于暴露两比特的信息:是否有摄像头以及是否有麦克风。用户代理可以通过假定系统拥有摄像头和麦克风来减轻此风险,例如在相关全局对象的关联 Document 使用被认为合理的约束调用 getUserMedia() 之前。![]()
当相关全局对象的关联 Document 开始捕获后,它会通过所有媒体捕获设备的列表提供额外的持久跨源信息,包括它们的分组和与捕获设备关联的人类可读标签,这进一步增加了指纹识别的攻击面。![]()
用户代理可以通过清理设备标签来限制信息暴露。例如,这可能意味着移除标签中包含的用户姓名,但保留设备制造商或型号信息。重要的是,清理后的标签应允许用户识别对应的设备。![]()
上述算法意味着对媒体设备信息的访问取决于相关全局对象的关联 Document 是否进行了捕获。
对于摄像头和麦克风设备,如果相关全局对象的关联 Document 未进行捕获(即未调用 getUserMedia() 或从未成功解析),则 MediaDeviceInfo 对象将包含 kind 的有效值,但 deviceId、label 和 groupId 的值为空字符串。此外,在 enumerateDevices() 的结果中,每种 kind 最多只会被列出一个设备。
否则,MediaDeviceInfo 对象将包含 deviceId、kind、label 和 groupId 的有意义的值。所有可用设备都会列在 enumerateDevices() 的结果中。
要执行 创建设备信息对象 以表示一个被发现的设备(device,给定 mediaDevices),请执行以下步骤
令 deviceInfo 为一个新的 MediaDeviceInfo 对象,用于表示 device。
为 device 初始化 deviceInfo.kind。
如果 deviceInfo.kind 等于 "videoinput" 且 摄像头信息可以被暴露(在 mediaDevices 上)为 false,则返回 deviceInfo。
如果 deviceInfo.kind 等于 "audioinput" 且 麦克风信息可以被暴露(在 mediaDevices 上)为 false,则返回 deviceInfo。
为 device 初始化 deviceInfo.label。
如果 device 存在存储的 deviceId,则将 deviceInfo.deviceId 初始化为该值。否则,让 deviceInfo.deviceId 成为如 deviceId 下所述生成的新唯一标识符。
如果 device 属于已经为 document 表示的设备的同一物理设备,则将 deviceInfo.groupId 初始化为现有 MediaDeviceInfo 对象的 groupId 值。否则,让 deviceInfo.groupId 成为如 groupId 下所述生成的新唯一标识符。
返回 deviceInfo
要执行 设备枚举能否进行 检查(给定 mediaDevices),请执行以下步骤
要执行 设备信息可以被暴露 检查(给定 mediaDevices),请执行以下步骤
如果 摄像头信息可以被暴露(在 mediaDevices 上),返回 true。
如果 麦克风信息可以被暴露(在 mediaDevices 上),返回 true。
返回 false。
要执行 摄像头信息可以被暴露 检查(给定 mediaDevices),请执行以下步骤
如果任何“videoinput”类型的本地设备被挂载到 mediaDevices 的相关全局对象的关联 Document 中的活动 MediaStreamTrack 上,则返回 true。
返回 mediaDevices.[[canExposeCameraInfo]]。
要执行 麦克风信息可以被暴露 检查(给定 mediaDevices),请执行以下步骤
如果任何“audioinput”类型的本地设备被挂载到 relevant global object 的关联 Document 中的活动 MediaStreamTrack 上,则返回 true。
返回 mediaDevices.[[canExposeMicrophoneInfo]]。
要执行 是否在视图中 检查(给定 mediaDevices),请执行以下步骤
如果 mediaDevices 的相关全局对象的关联 Document 是完全活跃的,且其可见性状态为 "visible",则返回 true。否则,返回 false。
要执行 系统是否有焦点 检查(给定 mediaDevices),请执行以下步骤
要执行 设备暴露是否可扩展 检查(给定 deviceType),请执行以下步骤
要设置设备信息暴露(在 mediaDevices 上,给定 requestedTypes 集合 和布尔值 value),请执行以下步骤
如果 "video" 在 requestedTypes 中,请执行以下子步骤
将 mediaDevices.[[canExposeCameraInfo]] 设置为 value。
如果 value 为 true 且 设备暴露是否可扩展(对于 "microphone")为真,则将 mediaDevices.[[canExposeMicrophoneInfo]] 设置为 true。
如果 "audio" 在 requestedTypes 中,请执行以下子步骤
将 mediaDevices.[[canExposeMicrophoneInfo]] 设置为 value。
如果 value 为 true 且 设备暴露是否可扩展(对于 "camera")为真,则将 mediaDevices.[[canExposeCameraInfo]] 设置为 true。
除摄像头和麦克风以外的设备的 暴露决策算法 接收 device、microphoneList、cameraList 和 mediaDevices 作为输入,并返回一个布尔值,以决定是否向网页暴露有关 device 的信息。
默认情况下,返回 false。
其他规范可以定义特定设备类型的算法。
要对 globalObject 执行 上下文是否正在捕获 检查,请执行以下步骤
如果 globalObject 不是 Window,则返回 false。
令 mediaDevices 为 globalObject 的 关联 MediaDevices。
对于 mediaDevices.[[mediaStreamTrackSources]] 中的每个 source,执行以下子步骤
令 deviceId 为 source 的设备 ID。
如果 mediaDevices.[[devicesLiveMap]][deviceId] 为 true,则返回 true。
返回 false。
此算法涵盖了所有捕获轨道,包括麦克风、摄像头和显示器。
WebIDL[Exposed=Window, SecureContext]
interface MediaDeviceInfo {
readonly attribute DOMString deviceId;
readonly attribute MediaDeviceKind kind;
readonly attribute DOMString label;
readonly attribute DOMString groupId;
[Default] object toJSON();
};
deviceId 类型为 DOMString,只读所表示设备的标识符。该设备 必须 由其标识符及其 kind 唯一标识。
为了确保存储的标识符被识别,在顶层可遍历对象中,同源 Document 中的标识符 必须 相同。在子可导航对象中,是否在跨文档中保持标识符相同的决定 必须 遵循用户代理的存储分区规则(如 localStorage),以避免干扰跨站点关联的缓解措施。如果标识符能够唯一地识别用户,则它在来自其他源的文档中 必须 是不可猜测的,以防止标识符被用于在不同源之间关联同一用户。标识符可以在不同源之间重用,只要它不与用户绑定且可以通过其他方式(如 User-Agent 字符串)猜测出来即可。
如果任何本地设备已挂载到来自此源的页面中的活动 MediaStreamTrack 上,或者已授予此源访问本地设备的存储权限,则该标识符 必须 被持久化,除非下文另有详细说明。唯一且稳定的标识符让应用程序能够跨多次访问保存、识别可用性并直接请求特定的源。
然而,只要没有任何本地设备挂载到来自此源的页面中的活动 MediaStreamTrack,并且没有授予此源访问本地设备的存储权限,则用户代理 可以 在来自此源的最后一次浏览会话关闭后清除该标识符。如果用户代理选择在这种情况下不清除该标识符,则它 必须 提供让用户能够像查看和删除 Cookie 一样显式查看和删除该标识符的途径。
由于 deviceId 可能会在浏览会话之间持久存在,为了减少其作为指纹识别机制的可能性,deviceId 应被视为像 Cookie [COOKIES] 一样的其他持久化存储机制,即用户代理 不得 为被禁止使用 Cookie 的站点持久化设备标识符,并且用户代理 必须 在清除其他持久化存储时轮换跨源设备标识符。![]()
kind 类型为 MediaDeviceKind,只读所表示设备的种类。
label 类型为 DOMString,只读描述此设备的标签(例如 "External USB Webcam")。此标签旨在让最终用户能够区分不同设备。应用程序不能假设标签包含任何特定信息,例如设备类型或型号。如果设备没有关联的标签,则此属性 必须 返回空字符串。
groupId 类型为 DOMString,只读所表示设备的组标识符。如果两个设备属于同一物理设备,它们具有相同的组标识符。例如,代表同一耳机扬声器和麦克风的音频输入和输出设备具有相同的 groupId。
组标识符 必须 为每个 文档 唯一生成。
toJSONWebIDLenum MediaDeviceKind {
"audioinput",
"audiooutput",
"videoinput"
};
MediaDeviceKind 枚举描述 |
|
|---|---|
audioinput |
表示音频输入设备;例如麦克风。 |
audiooutput |
表示音频输出设备;例如一副耳机。 |
videoinput |
表示视频输入设备;例如摄像头。 |
InputDeviceInfo 接口提供了对其所表示的输入设备能力的访问。
WebIDL[Exposed=Window, SecureContext]
interface InputDeviceInfo : MediaDeviceInfo {
MediaTrackCapabilities getCapabilities();
};
getCapabilities返回一个 MediaTrackCapabilities 对象,描述设备 MediaStream(根据其 kind 值)的主音频或视频轨道,在没有任何用户提供的约束的情况下。这些能力 必须 与通过在 getUserMedia({deviceId: id}) 返回的 MediaStream 中的此类第一个 MediaStreamTrack 上调用 getCapabilities() 所获得的能力完全一致,其中 id 是该 MediaDeviceInfo 的 deviceId 属性值。
如果尚未授予对任何本地设备的访问权限,且此 InputDeviceInfo 已就唯一识别信息进行了过滤(参见上述 enumerateDevices() 结果的描述),则此方法返回一个空字典。
devicechange 事件使用 DeviceChangeEvent 接口。
WebIDL[Exposed=Window]
interface DeviceChangeEvent : Event {
constructor(DOMString type, optional DeviceChangeEventInit eventInitDict = {});
[SameObject] readonly attribute FrozenArray<MediaDeviceInfo> devices;
[SameObject] readonly attribute FrozenArray<MediaDeviceInfo> userInsertedDevices;
};
devices 类型为 FrozenArray<MediaDeviceInfo>,只读devices 属性返回一个 MediaDeviceInfo 对象数组,表示此时可用设备的列表。
userInsertedDevices 类型为 FrozenArray<MediaDeviceInfo>,只读userInsertedDevices 属性返回一个仅包含 devices 中用户最近物理插入或激活的 MediaDeviceInfo 对象的数组,且这些对象是作为结果新暴露的。否则,返回一个空列表。
用户代理 可以 包含用户在调用 getUserMedia() 之前插入或激活的设备,前提是此事件标志着它们的首次暴露,且用户未在 getUserMedia() 中选择设备。
如果存在 MediaDeviceInfo 对象,它们也 必须 存在于 devices 中。
用户在调用期间(或紧接在调用之前)插入设备可能是他们希望立即使用该设备的强烈信号。
鼓励应用程序依赖此属性将此信号与因设备信息暴露变化而可能发生的 devices 差异区分开来。
WebIDLdictionary DeviceChangeEventInit : EventInit {
sequence<MediaDeviceInfo> devices = [];
};
devices 类型为 sequence<MediaDeviceInfo>,默认值为 []devices 成员是一个 MediaDeviceInfo 对象数组,表示可用设备。
本节扩展了 Navigator 和 MediaDevices,使其具有请求访问 用户代理 可用的媒体输入设备权限的 API。
或者,可以从特定类型的 DOM 元素(如 video 元素 [mediacapture-fromelement])中捕获本地 MediaStream。这对于自动化测试非常有用。
MediaDevices 接口扩展getUserMedia() 的定义反映了与过去数月在 Navigator 下存在的方法定义相比的两个重大变化。首先,getUserMedia() 方法的官方定义(且鼓励开发者使用)现在是在 MediaDevices 下定义的版本。这一决定反映了共识,即在原始 API 为了向后兼容性原因仍可在 Navigator 对象下的 Navigator.getUserMedia 使用的前提下,保留该 API。工作组承认这些 API 的早期用户已被鼓励定义 "var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;",以使他们的代码在流行 用户代理 中正式实现 getUserMedia() 之前和之后都能正常工作。为了确保功能等价,Navigator 下的 getUserMedia() 方法是根据此处的方法定义的。
其次,此处定义的方法是基于 Promise 的,而 Navigator 下定义的方法目前仍是基于回调的。强烈建议期望在 Navigator 下找到 getUserMedia() 的开发者阅读那里提供的详细说明。
getSupportedConstraints 方法旨在允许应用程序确定 用户代理 可识别的约束。应用程序可能需要此信息才能可靠地使用 必要约束 或从高级约束中的组合逻辑获得可预测的结果。
WebIDLpartial interface MediaDevices {
MediaTrackSupportedConstraints getSupportedConstraints();
Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints = {});
};
getSupportedConstraints返回一个字典,其成员是 用户代理 已知的可约束属性。受支持的可约束属性 必须 被表示,而 用户代理 不支持的任何可约束属性 不得 出现在返回的字典中。返回的值代表 用户代理 所实现的内容,并且在浏览会话期间不会改变。
提示用户授予使用其摄像头或其他视频或音频输入设备的权限。
constraints 参数是一个 MediaStreamConstraints 类型的字典。
此方法返回一个 Promise。如果用户接受了下述有效轨道,该 Promise 将被兑现(fulfilled),并返回一个合适的 MediaStream 对象。
如果未能找到有效的轨道或用户拒绝权限(如下所述),该 Promise 将被拒绝(rejected)。
当调用 getUserMedia() 方法时,用户代理 必须 运行以下步骤
令 constraints 为该方法的第一个参数。
令 requestedMediaTypes 为 constraints 中值为字典或 true 的媒体类型集合。
如果 requestedMediaTypes 是空集,返回一个被 TypeError 拒绝的 Promise。根据 WebIDL 规则,“optional”一词出现在 WebIDL 中,但调用成功必须提供该参数。
令 document 为 相关全局对象 的关联 Document。
如果 document 不完全活跃,返回一个被 DOMException 对象 拒绝的 Promise,其 name 属性值为 "InvalidStateError"。
如果 requestedMediaTypes 包含 "audio" 且 document 未被允许使用“麦克风”权限名称标识的功能,跳转到下方的 Permission Failure 步骤。
如果 requestedMediaTypes 包含 "video" 且 document 未被允许使用“摄像头”权限名称标识的功能,跳转到下方的 Permission Failure 步骤。
令 mediaDevices 为 此。
令 isInView 为 是否在视图中 算法的结果。
令 p 为一个新的 promise。
并行执行以下步骤
当 isInView 为 false 时,用户代理 必须 等待,直到排队的任务将 isInView 设置为 是否在视图中 算法的结果,并使 isInView 为 true,然后才进入下一步。
令 finalSet 为一个(初始时)空集。
对于 requestedMediaTypes 中的每种媒体类型 kind,运行以下步骤
对于每种类型为 kind 的每个可能源设备的配置,设想一个 候选者 作为最终 MediaStreamTrack 的占位符,该轨道持有一个源设备并配置有由其特定设置组成的设置字典。
将此候选集合称为 candidateSet。
如果 candidateSet 是空集,跳转到下方的 NotFound Failure 步骤。
true,则将 CS 设置为空约束集(无约束)。否则,继续并将 CS 设置为 constraints 中 kind 条目的值。MediaStreamTrack 对象定义的可约束属性。这意味着 "video" 中的纯音频约束和 "audio" 中的纯视频约束会被直接忽略,而不会导致 OverconstrainedError。如果 CS 包含一个必要约束成员,且其名称不在设备选择的允许必要约束列表中,则用 TypeError 拒绝 p,并终止这些步骤。
对 candidateSet 中的每个候选者执行 SelectSettings 算法,并以 CS 作为约束集。如果该算法返回 undefined,从 candidateSet 中移除该候选者。这通过验证至少存在一个满足约束的设置字典,来排除无法满足约束的设备。
如果 candidateSet 为空集,令 failedConstraint 为任何在执行 SelectSettings 算法时检查的所有设置字典中适应度距离为无穷大的必要约束,若无则为 "",并跳转到下方的 Constraint Failure 步骤。
此错误在用户给出任何设备授权之前,提供了有关底层设备无法生成什么的信息,因此可以用作指纹识别攻击面。![]()
读取当前 Document 中所有未挂载到活动 MediaStreamTrack 上的 candidateSet 中候选设备的当前权限状态。从 candidateSet 中移除设备权限状态为 "denied" 的任何候选者。
如果 candidateSet 现在为空,表明该类型的所有设备都处于 "denied" 状态,跳转到下方的 Permission Failure 步骤。
根据之前确定的用户偏好、出于安全原因或平台限制,可选择跳转到下方的 Permission Failure 步骤。
将 candidateSet 中的所有候选者添加到 finalSet。
令 stream 为一个新的空 MediaStream 对象。
对于 requestedMediaTypes 中的每种媒体类型 kind,最好同时运行以下子步骤
鼓励 用户代理 将对不同种类媒体的并发请求捆绑为单个面向用户的权限提示。
请求使用 PermissionDescriptor 的权限,将其 name 成员设置为与 kind 关联的权限名称(例如,"video" 的 "camera","audio" 的 "microphone"),同时将当前 Document 中挂载到活动且 相同权限 MediaStreamTrack 上的所有设备视为具有 "granted" 的权限状态,从而得到一组提供的媒体。相同权限 在此上下文中意味着获得该 MediaStreamTrack 所需的权限级别与当前请求的相同(例如,未隔离)。
在询问用户权限时,用户代理 必须 披露权限是仅授予所选设备,还是授予该 kind 的所有设备。
如果用户从未响应,此算法在此步骤停滞。
如果请求的结果为 "denied",跳转到下方的 Permission Failure 步骤。
令 hasSystemFocus 为 false。
当 hasSystemFocus 为 false 时,用户代理 必须 等待,直到排队的任务将 hasSystemFocus 设置为 系统是否有焦点 算法的结果,并使 hasSystemFocus 为 true,然后才进入下一步。
设置设备信息暴露(在 mediaDevices 上,使用 requestedMediaTypes 和 true)。
对于 requestedMediaTypes 中的每种媒体类型 kind,运行以下子步骤
令 finalCandidate 为所提供的媒体,它 必须 精确地是 finalSet 中类型为 kind 的一个 候选者。从 finalSet 中选择哪一个候选者的决定完全由 用户代理 决定,并可以通过询问用户来确定。
用户代理 应当 使用来自 SelectSettings 算法的计算出的 适应度距离 值作为选择算法的输入。然而,它 可以 也使用关于设备的其他内部可用信息,例如用户偏好。
这意味着不保证满足非必要约束的值。
鼓励 用户代理 默认使用用户针对该 kind 的首选或系统默认设备(如果可能)。用户代理 可以 允许用户使用任何媒体源,包括预录制的媒体文件。
请求的结果为 "granted"。如果操作系统/程序/网页锁等硬件错误阻止了访问,则从 finalSet 中移除对应的候选者。如果 finalSet 中没有类型为 kind 的候选者,用一个新的 DOMException 对象 拒绝 p,其 name 属性值为 "NotReadableError",并终止这些步骤。否则,用更新后的 finalSet 重启这些子步骤。
如果设备访问因上述原因之外的任何其他原因失败,从 finalSet 中移除对应的候选者。如果 finalSet 中没有类型为 kind 的候选者,用一个新的 DOMException 对象 拒绝 p,其 name 属性值为 "AbortError",并终止这些步骤。否则,用更新后的 finalSet 重启这些子步骤。
令 grantedDevice 为 finalCandidate 的源设备。
使用 grantedDevice 的 deviceId(即 deviceId),将 mediaDevices.[[devicesLiveMap]][deviceId] 设置为 true(如果尚未为 true),并将 mediaDevices.[[devicesAccessibleMap]][deviceId] 设置为 true(如果尚未为 true)。
令 track 为使用 grantedDevice 和 mediaDevices 创建 MediaStreamTrack 的结果。MediaStreamTrack 的源 不得 改变。
将 track 添加到 stream 的轨道集中。
对 stream 中的所有轨道运行 ApplyConstraints 算法,并使用适当的约束。如果其中任何一个返回除 undefined 之外的结果,令 failedConstraint 为该结果,并跳转到下方的 Constraint Failure 步骤。
对于 stream 中的每个 track,使用 track.[[Source]] 和 mediaDevices 将轨道源绑定到 MediaDevices。
解析 p 并返回 stream,然后终止这些步骤。
NotFound Failure:
如果 getUserMedia 特殊失败是允许的(给定 requestedMediaTypes)返回 false,跳转到下方的 Permission Failure 步骤。
用一个新的 DOMException 对象 拒绝 p,其 name 属性值为 "NotFoundError"。
Constraint Failure:
如果 getUserMedia 特殊失败是允许的(给定 requestedMediaTypes)返回 false,跳转到下方的 Permission Failure 步骤。
令 message 为 undefined 或一个信息性的人类可读消息,令 constraint 为 failedConstraint(如果 设备信息可以被暴露 为 true),否则为 ""。
拒绝 p,并返回一个新的 OverconstrainedError,它是通过调用 OverconstrainedError(constraint, message) 创建的。
Permission Failure: 拒绝 p,并返回一个新的 DOMException 对象,其 name 属性值为 "NotAllowedError"。
返回 p。
要检查 getUserMedia 特殊失败是否允许(给定 requestedMediaTypes),运行以下步骤
在上述算法中,约束被检查了两次——一次在设备选择时,一次在访问批准后。这两次检查之间可能会有时间流逝,因此所选设备可能不再适用。在这种情况下,将产生 NotReadableError。
MediaStreamConstraints 字典用于指示 用户代理 在 getUserMedia() 返回的 MediaStream 中包含什么样的 MediaStreamTrack。
WebIDLdictionary MediaStreamConstraints {
(boolean or MediaTrackConstraints) video = false;
(boolean or MediaTrackConstraints) audio = false;
};
MediaStreamConstraints 成员video 类型为 (boolean or MediaTrackConstraints),默认值为 false如果为 true,则请求返回的 MediaStream 包含一个视频轨道。如果提供了 Constraints 结构,则进一步指定该视频轨道的性质和设置。如果为 false,则 MediaStream 不得 包含视频轨道。
audio 类型为 (boolean or MediaTrackConstraints),默认值为 false如果为 true,则请求返回的 MediaStream 包含一个音频轨道。如果提供了 Constraints 结构,则进一步指定该音频轨道的性质和设置。如果为 false,则 MediaStream 不得 包含音频轨道。
本节是非规范性的。
本节中 getUserMedia() 的定义反映了最初提出的调用格式;它仅为希望保留向后兼容性的浏览器在此记录。它在两个重要方面与推荐的接口不同。
首先,getUserMedia() 方法的官方定义(且鼓励开发者使用)现在位于 MediaDevices。这一决定反映了共识,即在原始 API 为了向后兼容性原因仍可在 Navigator 对象下使用时,保留该 API。因为工作组承认这些 API 的早期用户已被鼓励定义 "var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;",以使他们的代码在流行浏览器中正式实现 getUserMedia() 之前和之后都能正常工作。为了确保功能等价,此处定义的 getUserMedia() 方法是根据 MediaDevices 下的方法定义的。
其次,将规范中所有其他基于回调的方法更改为基于 Promise 的决定,要求 navigator.getUserMedia() 的定义在使用 navigator.mediaDevices.getUserMedia() 时反映这一点。因为 navigator.getUserMedia() 现在是规范中唯一剩余的基于回调的方法,所以目前正在讨论 a) 它是否仍属于该规范,以及 b) 如果属于,其语法是否应保持基于回调还是更改为使用 Promise。鼓励对这些问题提供反馈,特别是来自正在使用当前功能实现的开发者。
注意,其他从基于回调语法更改为基于 Promise 语法的那些方法,被认为实现不够广泛,无需考虑遗留使用。
实现无需实现此接口即可被视为符合规范。
WebIDL
getUserMedia提示用户授予使用其摄像头或其他视频或音频输入设备的权限。
constraints 参数是一个 MediaStreamConstraints 类型的字典。
如果用户接受了在 MediaDevices 上的 getUserMedia() 中所述的有效轨道,则 successCallback 将以合适的 MediaStream 对象作为其参数被调用。
如果未能找到有效的轨道或用户拒绝权限(如 MediaDevices 上的 getUserMedia() 中所述),则 errorCallback 将被调用。
当调用 getUserMedia() 方法时,用户代理 必须 执行以下步骤
令 constraints 为该方法的第一个参数。
令 successCallback 为该方法第二个参数所指示的回调函数。
令 errorCallback 为该方法第三个参数所指示的回调函数。
以 constraints 为参数运行 getUserMedia() 算法,并将结果 Promise 记为 p。
当 p 成功履行(fulfillment)并获得值 stream 时,执行以下步骤
调用 successCallback,并将 stream 作为参数传入。
当 p 被拒绝(rejection)并获得原因 r 时,执行以下步骤
调用 errorCallback,并将 r 作为参数传入。
本节是非规范性的。
鼓励 用户代理 在确定对 getUserMedia() 的给定调用将会成功时预留资源。最好在解析返回的 Promise 之前预留资源。后续对 getUserMedia() 的调用(在本页面或任何其他页面中)应将先前分配的资源以及被其他应用程序持有的资源视为忙碌状态。除非用户指定,否则标记为忙碌的资源不应作为源提供给当前网页。或者,用户代理 可以选择提供来自忙碌源的流,但仅限于源与保持该源忙碌的原始流所有者匹配的页面。
本文档建议,在权限授予对话框或设备选择界面(如果存在)中,应允许用户选择任何可用的硬件作为页面所请求流的源(前提是该资源能够满足任何指定的 必要约束)。尽管未明确推荐为最佳实践,但请注意,某些 用户代理 可能支持用本地文件和其他媒体替换视频或音频源的能力。文件选择器可用于向用户提供此功能。
本文档还建议,应向用户展示所有因先前调用 getUserMedia()(在本页面或任何仍然存活的其他页面中)而导致忙碌的资源,并允许用户终止该流并改用此资源供当前页面使用。如果当前操作系统环境允许,也建议以相同方式展示并处理当前被其他应用程序持有的资源。如果用户选择了此选项,则必须移除对应于受影响流页面的资源轨道。
当请求设备权限时,用户代理 可以选择存储此权限以供同一源后续使用,以便用户无需在将来再次授予权限。用户代理 可以自主选择是否提供分别存储每个设备、特定类别的所有设备或所有设备权限的功能;此选择必须对用户透明,并且必须已授予要存储权限的整个集合的权限,例如,要存储使用所有摄像头的权限,用户必须已授予使用所有摄像头(而不仅仅是一个)的权限。
如上所述,本规范并未规定授予权限是否一定会导致权限存储。当权限未存储时,权限将仅持续到该设备的所有 MediaStreamTrack 被停止为止。
MediaStream 可能包含多个视频和音频轨道。例如,这使得可以在单个流对象中包含来自两个或多个摄像头的视频成为可能。然而,当前的 API 不允许页面表示需要来自独立源的多个视频流。
建议允许从同一页面多次调用 getUserMedia(),作为页面请求多个离散视频和/或音频流的一种方式。
还要注意,如果页面进行了多次 getUserMedia() 调用,它们请求资源的顺序以及它们完成的顺序不受本规范约束。
单次调用 getUserMedia() 总会返回一个包含零个或一个音频轨道,以及零个或一个视频轨道的流。如果脚本在达到稳定状态之前多次调用 getUserMedia(),本文档建议 UI 设计者将权限对话框合并,以便用户可以在一次对话交互中为使用多个摄像头和/或媒体源授予权限。每个 getUserMedia 调用的约束可用于决定哪个流获得哪些媒体源。
生成 deviceId 的高效实践是:根据私钥 +(源或源 + 顶级源,基于用户代理的分区规则)+ 盐值 + 驱动程序中设备的基础(硬件)ID 生成加密哈希,并将生成的哈希作为字母数字字符串呈现。建议使用 32 位或更少的哈希值,但不宜过低,以避免冲突风险。
一种以存储为代价的低熵替代方案是:根据 用户代理 的分区规则,为每个源或源 + 顶级源遇到的每个新设备随机分配 0 到 255 之间的数字,如果数字用完,则弃用最久未见的数字。
为了管理用户的隐私,由摄像头或麦克风生成的轨道可能随时会被 用户代理 强制 静音。然而,这样做可能会引发 Web 兼容性问题,并泄露用户活动信息,因此建议谨慎行事。
最佳实践是在以下情况下 静音 摄像头或麦克风轨道
可约束模式(Constrainable pattern)允许应用程序检查和调整实现它的对象(即 可约束对象)的属性。它被拆分为一组单独的定义,以便其他规范可以引用。核心概念是“能力(Capability)”,它由对象的可约束属性及其可能值的集合组成,这些值可以指定为范围或枚举。例如,摄像头可能具有 20 到 50 帧/秒(范围)的帧率(属性),并且可能能够定位(属性)为面向用户、背向用户,或面向用户的左侧或右侧(枚举集)。应用程序可以通过 getCapabilities() 访问器检查可约束属性所支持的“能力”。
应用程序可以通过基本和/或高级约束集(ConstraintSet)以及 applyConstraints() 方法选择其所需对象能力的(范围)值。约束集由对象的一个或多个属性名称及其对应的期望值(或期望值范围)组成。这些属性/值对中的每一对都可以被视为一个单独的约束。例如,应用程序可以设置一个包含两个约束的约束集,第一个声明摄像头的帧率在 30 到 40 帧/秒之间(范围),第二个声明摄像头应面向用户(特定值)。各个约束如何交互取决于它们是在基本约束结构(这是一个带有额外 'advanced' 属性的 ConstraintSet)中提供的,还是在高级列表中的 ConstraintSet 中提供的。行为如下:基本约束结构中的所有 'min'、'max' 和 'exact' 约束一起被视为 必要约束。如果无法同时满足这些针对指定属性名称的单个约束,则 用户代理 必须 拒绝 返回的 Promise。否则,它必须应用必要约束。接下来,它将按指定的顺序考虑 advanced 列表中的任何 ConstraintSet,并尝试满足/应用每个完整的 ConstraintSet(即一起满足 ConstraintSet 中的所有约束),但仅当无法完全满足/应用它时才会跳过。接下来,用户代理 必须 尝试单独应用任何 'ideal' 约束或作为属性裸值给出的约束(称为 可选基本约束)。对于这些属性,它 必须 以任何顺序尽可能多地满足这些约束。最后,用户代理 必须 解析 返回的 Promise。
以下示例可能有助于理解约束的工作原理。第一个示例展示了一个基本约束结构。给出了三个约束,用户代理 将尝试单独满足每一个约束。根据此摄像头可用的解析度,可能无法同时满足所有三个约束。如果是这样,用户代理 将尝试满足其中两个,如果连两个都无法同时满足,则仅满足一个。注意,如果无法同时满足全部三个约束,则可能存在多组可满足的两个约束的组合。如果是这样,用户代理 将自行进行选择。
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: 1280,
height: 720,
aspectRatio: 3/2
}
});
下一个示例增加了一点复杂性。仍然给出了宽度和高度的 ideal 值,但这次也给出了各自的最小值,以及必须满足的最小 frameRate。如果它无法满足帧率、宽度或高度的最小值,它将 拒绝 该 Promise。否则,它将尝试满足宽度、高度和 aspectRatio 的目标值,然后 解析 该 Promise。
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: {min: 640, ideal: 1280},
height: {min: 480, ideal: 720},
aspectRatio: 3/2,
frameRate: {min: 20}
}
});
} catch (error) {
if (error.name != "OverconstrainedError") {
throw error;
}
// Overconstrained. Try again with a different combination (no prompt was shown)
}
此示例通过添加 'advanced' 属性,展示了约束结构可能实现的完全控制。在这种情况下,用户代理 在处理必要约束方面的行为相同,但在尝试满足 ideal 值之前,它将处理 'advanced' 列表。在此示例中,'advanced' 列表包含两个 ConstraintSet。第一个指定宽度和高度约束,第二个指定 aspectRatio 约束。注意,在高级列表中,这些裸值被视为 'exact' 值。此示例表示:“我需要视频宽度至少为 640 像素,高度至少为 480 像素。我的首选是精确的 1920x1280,但如果无法提供,则尽量给我 4x3 的 aspectRatio。如果连这也不可能,请给我尽可能接近 1280x720 的分辨率。”
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: {min: 640, ideal: 1280},
height: {min: 480, ideal: 720},
frameRate: {min: 30},
advanced: [
{width: 1920, height: 1280},
{aspectRatio: 4/3},
{frameRate: {min: 50}},
{frameRate: {min: 40}}
]
}
});
} catch (error) {
if (error.name != "OverconstrainedError") {
throw error;
}
// Overconstrained. Try again with a different combination (no prompt was shown)
}
高级 ConstraintSet 的顺序非常重要。在前一个示例中,不可能同时满足 1920x1280 的 ConstraintSet 和 4x3 的长宽比 ConstraintSet。由于 1920x1280 在列表中排在首位,用户代理 将首先尝试满足它。因此,应用程序开发者可以通过为同一属性指定多个高级 ConstraintSet 来实现回退策略。应用程序还指定了另外两个高级 ConstraintSet,第一个要求帧率大于 50,第二个要求帧率大于 40。如果 用户代理 能够将帧率设置为大于 50,它就会这样做(后续 ConstraintSet 将被琐碎地满足)。然而,如果 用户代理 无法将帧率设置为 50 以上,它将跳过该 ConstraintSet 并尝试将其设置为 40 以上。如果 用户代理 无法满足这两个 ConstraintSet 中的任何一个,基本 ConstraintSet 中的 'min' 值将强制要求 30 作为下限。换句话说,如果 用户代理 无法获得大于 30 的值,它将完全失败,但如果可能,它会选择大于 50 的值,然后再尝试大于 40 的值。
注意,与基本约束不同,高级列表中的 ConstraintSet 内的约束必须一起满足或一起跳过。因此,{width: 1920, height: 1280} 是对该特定分辨率的请求,而不是对该宽度或该高度的请求。可以将基本约束视为对单个约束的“或”(非排他性)请求,而每个高级 ConstraintSet 则是对 ConstraintSet 中单个约束的“与”请求。应用程序可以通过 getConstraints() 访问器检查当前生效的整套约束。
用户代理 为可约束属性选择的具体值称为设置(Setting)。例如,如果应用程序应用了一个约束集,指定帧率至少为 30 帧/秒,且不超过 40,则设置可以是任何中间值,例如 32、35 或 37 帧/秒。应用程序可以通过 getSettings() 访问器查询对象可约束属性的当前设置。
虽然本规范将 ConstrainablePattern 正式定义为 WebIDL 接口,但它实际上是其他接口的模板或模式,不能直接继承,因为方法的返回值需要扩展,而 WebIDL 无法做到这一点。因此,每个希望使用此处定义功能的接口都必须提供此处给出的函数和接口的 WebIDL 本身副本。但是,它可以引用此处定义的语义,这些语义不会改变。有关示例,请参见 MediaStreamTrack 接口定义。
此模式依赖于 可约束对象 定义三个内部槽位:
[[Capabilities]] 内部槽位,初始化为 Capabilities 字典,描述每个所暴露可约束属性的聚合允许值(详见 Capabilities),若无则为空字典。
[[Constraints]] 内部槽位,初始化为空的 Constraints 字典。
[[Settings]] 内部槽位,初始化为 Settings 字典,描述每个所暴露可约束属性当前激活的设置值(详见 Settings),若无则为空字典。
WebIDL[Exposed=Window]
interface ConstrainablePattern {
Capabilities getCapabilities();
Constraints getConstraints();
Settings getSettings();
Promise<undefined> applyConstraints(optional Constraints constraints = {});
};getCapabilities() 方法返回对象支持的可约束属性名称字典。调用时,用户代理 必须 返回 [[Capabilities]] 内部槽位的值。
底层硬件可能无法精确映射到为可约束属性定义的范围。如果可能,条目 应当 定义如何将硬件设置转换和缩放到为该属性定义的值上。例如,假设一个假设的 fluxCapacitance 属性范围从 -10(最小值)到 10(最大值),但普通硬件设备仅支持“关”、“中”和“全”的值。可约束属性定义可能会指定,对于此类硬件,用户代理 应将范围值 -10 映射到“关”,10 映射到“全”,0 映射到“中”。它还可能指出,当 ConstraintSet 强制要求值为 3 时,用户代理 应尝试在硬件上设置“中”值,并且 getSettings() 应返回 fluxCapacitance 为 0,因为这是定义为对应于“中”的值。
getConstraintsgetConstraints() 方法返回该对象最近一次成功调用 ApplyConstraints 算法 时的参数 Constraints,并保持指定的顺序。注意,返回的一些高级 ConstraintSet 可能当前并未满足。要检查哪些 ConstraintSet 当前生效,应用程序应使用 getSettings。UA 可以 返回一个在所有情况下与所应用约束效果相同的约束集,而不是返回如上所述的确切约束。调用时,用户代理 必须 返回 [[Constraints]] 内部槽位的值。
getSettings() 方法返回对象所有可约束属性的当前设置,无论它们是平台默认值还是由 ApplyConstraints 算法 设置的。注意,设置是一个符合约束的目标值,因此有时可能与测得的性能不同。调用时,用户代理 必须 返回 [[Settings]] 内部槽位的值。
当 applyConstraints 模板方法 被调用时,用户代理 必须 执行以下步骤:
令 object 为调用此方法的对象。
令 newConstraints 为此方法的参数。
令 p 为一个新的 promise。
并行运行以下步骤,如果多次调用此方法,需保持调用顺序:
令 failedConstraint 为以 newConstraints 为参数运行 ApplyConstraints 算法 的结果。
令 successfulSettings 为上述步骤中的算法完成后 object 的当前设置。
排队执行一个运行以下步骤的任务
如果 failedConstraint 不为 undefined,令 message 为 undefined 或人类可读的提示信息,拒绝 p,并传入由调用 OverconstrainedError(failedConstraint, message) 创建的新 OverconstrainedError,然后终止这些步骤。在这种情况下,现有约束保持有效。
将 object 的 [[Constraints]] 内部槽位设置为 newConstraints 或一个在所有情况下效果与 newConstraints 相同的 Constraints 字典。
将 object 的 [[Settings]] 内部槽位设置为 successfulSettings。
解析 (resolve) p 为 undefined。
返回 p。
用于应用约束的 ApplyConstraints 算法 如下所述。以下是一些用于算法陈述的初步定义。
我们使用术语 设置字典(settings dictionary) 来表示可能作为设置应用于对象的一组值。
对于字符串值约束,若序列中的一个值与被比较的值完全相同,则定义下文中的 "==" 为真。
我们定义 适应度距离(fitness distance) 为 设置字典 与约束集 CS 之间,对于 CS 中 存在 的每个成员(由 constraintName 和 constraintValue 对表示)的下列值的总和:
如果 constraintName 不受 用户代理 支持,适应度距离为 0。
如果该约束是 必要(required) 的(即 constraintValue 包含一个或多个名为 'min'、'max' 或 'exact' 的成员,或者其本身是高级 ConstraintSet 中的裸值),且 设置字典 的 constraintName 成员的值不满足该约束或不 存在,则适应度距离为正无穷大。
如果约束不适用于此类对象,适应度距离为 0(即该约束不影响适应度距离)。
如果 constraintValue 是布尔值,但可约束属性不是,则适应度距离基于 设置字典 的 constraintName 成员是否 存在,根据公式计算。
(constraintValue == exists) ? 0 : 1
(actual == ideal) ? 0 : |actual - ideal| / max(|actual|, |ideal|)
(actual == ideal) ? 0 : 1
更多定义
我们定义 SelectSettings 算法如下:
注意,未知属性会被 WebIDL 丢弃,这意味着未知/不受支持的必要约束将静默消失。为避免这种情况带来的意外,应用程序开发者应首先如以下示例所示,使用 getSupportedConstraints() 方法。
ConstrainablePattern 对象。令 copy 为 object 的无约束副本(即,copy 的行为应如同移除了所有 ConstraintSet 的 object)。针对 copy 的每个可能的 设置字典,计算其 适应度距离,将属性的裸值视为理想值。令 candidates 为适应度距离有限的 设置字典 集合。
如果 candidates 为空,则返回 undefined 作为 SelectSettings 算法的结果。
计算它与 candidates 中每个设置字典之间的 适应度距离,将属性的裸值视为精确值(exact)。
如果一个或多个 candidates 中的设置字典的适应度距离有限,则保留这些设置字典,丢弃其他。
如果所有 candidates 中的设置字典的适应度距离均为无限大,则忽略此约束集。
从 candidates 中选择一个设置字典,并将其作为 SelectSettings 算法的结果返回。用户代理 必须 使用在步骤 3 中计算出的具有最小 适应度距离 的设置字典。如果多个设置字典具有相同的最小适应度距离,用户代理 将根据系统默认属性值和 用户代理 默认属性值选择其中一个。
对于所选设备具有系统默认值的任何属性,如果与上述算法兼容,则 应当 使用系统默认值。这通常适用于诸如 sampleRate 或 sampleSize 等属性。其他属性,如 echoCancellation 或 resizeMode 通常没有系统默认值。用户代理 为这些属性定义自己的默认值。实现者需要谨慎选择良好的默认值,因为它们通常会影响媒体内容的生成方式。
建议参考现有实现来选择有意义的默认值。请注意,默认值可能会因系统而异,例如桌面端与移动端。在撰写本文时,用户代理 实现倾向于使用以下默认值,这些值因其适合将 RTCPeerConnection 作为汇聚点而被选中:
width 设置为 640。
height 设置为 480。
frameRate 设置为 30。
echoCancellation 设置为 true。
要将 ApplyConstraints 算法 应用于给定的 object(参数为 newConstraints),用户代理 必须 执行以下步骤:
令 successfulSettings 为以 newConstraints 为约束集运行 SelectSettings 算法的结果。
如果 successfulSettings 为 undefined,令 failedConstraint 为所有在执行 SelectSettings 算法期间检查的设置字典中适应度距离均为无穷大的任意 必要约束,若无则设为 "",然后返回 failedConstraint 并终止这些步骤。
undefined。任何结果与上述算法相同的实现都是被允许的。例如,实现可以选择跟踪在所考虑约束下可行的设置的最大值和最小值,而不是跟踪设置的所有可能值。
在选择设置字典时,UA 可以使用其可用的任何信息。此类信息的示例包括选择是否作为 getUserMedia 中设备选择的一部分完成、摄像头的能量使用在不同的设置字典之间是否有变化,或者使用该设置字典是否会导致设备驱动程序应用重采样。
用户代理 可以 在任何时候为对象的可约束属性选择新设置。当这样做时,它 必须 以上述算法中描述的方式尝试满足所有当前约束,令 successfulSettings 为生成的最终新设置,并排队执行一个运行以下步骤的任务:
令 object 为一个或多个可约束属性的新设置已更改的 对象。ConstrainablePattern
将 object 的 [[Settings]] 内部槽位设置为 successfulSettings。
以下是可以传递给 applyConstraints() 或作为 constraints 值返回的约束示例。它使用了为摄像头源 MediaStreamTrack 定义的 可约束属性。在此示例中,所有约束都是理想值,这意味着结果是基于用户特定摄像头的“尽力而为”。
await track.applyConstraints({
width: 1920,
height: 1080,
frameRate: 30,
});
const {width, height, frameRate} = track.getSettings();
console.log(`${width}x${height}x${frameRate}`); // 1920x1080x30, or it might be e.g.
// 1280x720x30 as best effort
为了实现更精细的控制,应用程序可以坚持要求精确匹配,前提是它已准备好处理失败情况。
try {
await track.applyConstraints({
width: {exact: 1920},
height: {exact: 1080},
frameRate: {min: 25, ideal: 30, max: 30},
});
const {width, height, frameRate} = track.getSettings();
console.log(`${width}x${height}x${frameRate}`); // 1920x1080x25-30!
} catch (error) {
if (error.name != "OverconstrainedError") {
throw error;
}
console.log(`This camera cannot produce the requested ${error.constraint}.`);
}
约束不仅可以作为初始化的便利传递给 getUserMedia,还可以影响设备选择。在这种情况下,内在约束 也可用。
以下示例展示了如何使用约束来优先选择上次访问时的特定摄像头和麦克风,并对尺寸提出要求以及对立体声的偏好(授予权限后应用),并帮助在请求的设备不再可用时找到合适的替代品(或者在某些用户代理中,被用户覆盖)。
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: localStorage.camId,
width: {min: 800, ideal: 1024, max: 1280},
height: {min: 600}
},
audio: {
deviceId: localStorage.micId,
channelCount: 2
}
});
// Granted. Store deviceIds for next time
localStorage.camId = stream.getVideoTracks()[0].getSettings().deviceId;
localStorage.micId = stream.getAudioTracks()[0].getSettings().deviceId;
} catch (error) {
if (error.name != "OverconstrainedError") {
throw error;
}
// Overconstrained. No suitable replacements found
}
上述示例避免使用 {exact: deviceId},以便浏览器可以使用内部可用的设备信息(如用户偏好或设备缺失情况)来代替所提供的 deviceId。
该示例还在每次授予权限时存储 deviceId,以防它们代表一个新的选择。
相比之下,这里有一个使用约束来实现内容中摄像头选择器的示例。在这种情况下,我们使用 exact 并完全依赖于用户从选择列表中选出的 deviceId。
async function switchCameraTrack(freshlyChosenDeviceId, oldTrack) {
if (isMobile) {
oldTrack.stop(); // Some platforms can only open one camera at a time.
}
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: {exact: freshlyChosenDeviceId}
}
});
const [track] = stream.getVideoTracks();
localStorage.camId = track.getSettings().deviceId;
return track;
}
这里有一个请求手机后置摄像头的示例,理想情况下是 720p,但也接受任何接近该分辨率的设置。请注意尺寸约束是如何以横向模式指定的。
async function getBackCamera() {
return await navigator.mediaDevices.getUserMedia({
video: {
facingMode: {exact: 'environment'},
width: 1280,
height: 720
}
});
}
这里有一个“我想要接近 720p 的原生 16:9 分辨率,但帧率要求精确为 10,即使原生不支持”的示例。这需要分两步完成:第一步是发现原生模式,第二步是应用自定义帧率。这也展示了如何从当前设置中派生约束,这些设置可能是已旋转的。
async function nativeResolutionButDecimatedFrameRate() {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
resizeMode: 'none', // means native resolution and frame rate
width: 1280,
height: 720,
aspectRatio: 16 / 9 // aspect ratios may not be exactly accurate
}
});
const [track] = stream.getVideoTracks();
const {width, height, aspectRatio} = track.getSettings();
// Constraints are in landscape, while settings may be rotated (portrait)
if (width < height) {
[width, height] = [height, width];
aspectRatio = 1 / aspectRatio;
}
await track.applyConstraints({
resizeMode: 'crop-and-scale',
width: {exact: width},
height: {exact: height},
frameRate: {exact: 10},
aspectRatio,
});
return stream;
}
这里有一个展示如何使用 getSupportedConstraints 的示例,适用于应用程序无法容忍因用户代理缺乏支持而忽略约束的情况。
async function getFrontCameraRes() {
const supports = navigator.mediaDevices.getSupportedConstraints();
for (const constraint of ["facingMode", "aspectRatio", "resizeMode"]) {
if (!(constraint in supports) {
throw new OverconstrainedError(constraint, "Not supported");
}
}
return await navigator.mediaDevices.getUserMedia({
video: {
facingMode: {exact: 'user'},
advanced: [
{aspectRatio: 16/9, height: 1080, resizeMode: "none"},
{aspectRatio: 4/3, width: 1280, resizeMode: "none"}
]
}
});
}
有效输入的定义语法取决于值的类型。除了标准的原子类型(布尔值、long、double、DOMString)外,有效值还包括原子类型的列表以及下文定义的最小值-最大值范围。
列表值 必须 被解释为析取(OR)。例如,如果摄像头属性 'facingMode' 被定义为具有有效值 ["left", "right", "user", "environment"],这意味着 'facingMode' 可以取值 "left"、"right"、"environment" 和 "user"。同样,限制 'facingMode' 为 ["user", "left", "right"] 的 Constraints 将意味着 用户代理 应选择一个摄像头(或调整摄像头方向,如果可能),使 "facingMode" 为 "user"、"left" 或 "right"。因此,该约束将请求摄像头不应背向用户,但会允许 用户代理 允许用户选择其他方向。
WebIDLdictionary ConstrainDoubleRange : DoubleRange {
double exact;
double ideal;
};
WebIDLdictionary ULongRange {
[Clamp] unsigned long max;
[Clamp] unsigned long min;
};
max,类型为 unsigned long此属性的最大有效值。
min,类型为 unsigned long此属性的最小值。
WebIDLdictionary ConstrainULongRange : ULongRange {
[Clamp] unsigned long exact;
[Clamp] unsigned long ideal;
};
exact,类型为 unsigned long此属性所需的精确值。
ideal,类型为 unsigned long此属性的理想(目标)值。
WebIDLdictionary ConstrainDOMStringParameters {
(DOMString or sequence<DOMString>) exact;
(DOMString or sequence<DOMString>) ideal;
};
WebIDLdictionary ConstrainBooleanOrDOMStringParameters {
(boolean or DOMString) exact;
(boolean or DOMString) ideal;
};
WebIDLtypedef ([Clamp] unsigned long or ConstrainULongRange) ConstrainULong;
ConstrainULong 用于指代 ([Clamp] unsigned long or ConstrainULongRange) 类型。WebIDLtypedef (double or ConstrainDoubleRange) ConstrainDouble;
ConstrainDouble 用于指代 (double or ConstrainDoubleRange) 类型。WebIDLtypedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
ConstrainBoolean 用于指代 (boolean or ConstrainBooleanParameters) 类型。WebIDLtypedef (DOMString or
sequence<DOMString> or
ConstrainDOMStringParameters) ConstrainDOMString;
ConstrainDOMString 用于指代 (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) 类型。WebIDLtypedef (boolean or DOMString or ConstrainBooleanOrDOMStringParameters) ConstrainBooleanOrDOMString;
ConstrainBooleanOrDOMString 用于指代 (boolean or DOMString or ConstrainBooleanOrDOMStringParameters) 类型。Capabilities 是一个包含一个或多个键值对的字典,其中每个键 必须 是一个可约束属性,且每个值 必须 是该属性允许值集的子集。值表达式的确切语法取决于属性的类型。Capabilities 字典指定了哪些可约束属性可以作为约束应用于 可约束对象。注意,可约束对象 的 Capabilities 可以是 Web 平台上定义的属性的子集,且包含这些属性对应值集的子集。注意,Capabilities 是由 用户代理 返回给应用程序的,不能由应用程序指定。但是,应用程序可以通过 Constraints 控制 用户代理 为可约束属性选择的设置(Settings)。
Capabilities 字典的示例如下。在这种情况下,可约束对象 是一个能力集非常有限的视频源。
{
frameRate: {min: 1.0, max: 60.0},
facingMode: ['user', 'left']
}
下面的下一个示例指出,范围值的能力为单个可约束属性提供范围,而不是组合。这对于视频宽度和高度尤为相关,因为宽度和高度的范围是分别报告的。在示例中,如果 可约束对象 只能提供 640x480 和 800x600 分辨率,则返回的相关能力将是:
{
width: {min: 640, max: 800},
height: {min: 480, max: 600},
aspectRatio: {min: 4/3, max: 4/3}
}
注意在上面的示例中,aspectRatio 会表明宽度和高度的任意组合是不可能的,尽管它仍然暗示有超过两种分辨率可用。
使用可约束模式(Constrainable Pattern)的规范不应将下面的字典子类化,而应提供其自己的定义。有关示例,请参见MediaTrackCapabilities。WebIDLdictionary Capabilities {};
Settings 是一个包含一个或多个键值对的字典。它 必须 包含在 getCapabilities() 中返回的、且在其返回的对象类型上定义的每个键;例如,音频 MediaStreamTrack 没有 "width" 属性。每个键 必须 有一个单一值,且该值 必须 是 getCapabilities() 为该属性定义的集合的成员。Settings 字典包含 用户代理 为对象可约束属性选择的实际值。值的确切语法取决于属性类型。
符合标准的 用户代理 必须 支持本规范中定义的所有可约束属性。
Settings 字典的示例如下。此示例不太现实,因为 用户代理 实际上需要支持比这些更多的可约束属性。
{
frameRate: 30.0,
facingMode: 'user'
}
MediaTrackSettings。
由于 WebIDL 的限制,实现可约束模式的接口不能简单地将 Constraints 和 ConstraintSet 子类化(正如它们在此定义的那样)。相反,它们必须提供遵循此模式的各自定义。有关示例,请参见 MediaTrackConstraints。
WebIDLdictionary ConstraintSet {};
ConstraintSet 的每个成员对应一个可约束属性,并指定了该属性有效能力值的一个子集。应用 ConstraintSet 指示 用户代理 将相应可约束属性的设置限制为指定的值或值范围。给定属性 可以 同时出现在基本约束集和高级约束集列表中,并且在高级列表中的每个 ConstraintSet 中最多出现一次。
WebIDLdictionary Constraints : ConstraintSet {
sequence<ConstraintSet> advanced;
};
advanced,类型为 sequence<ConstraintSet>这是 用户代理 必须 按顺序尝试满足的 ConstraintSet 列表,仅跳过那些无法满足的约束。这些 ConstraintSet 的顺序非常重要。特别是,当它们作为参数传递给 applyConstraints 时,用户代理 必须 尝试按指定的顺序满足它们。因此,如果高级 ConstraintSet C1 和 C2 可以单独满足,但不能同时满足,则此列表中排在前面的将被满足,另一个则不会。用户代理 必须 尝试满足列表中的所有 ConstraintSet,即使某些无法满足。因此,在前一个示例中,如果约束 C3 指定在 C1 和 C2 之后,则即使 C2 无法满足,用户代理 也会尝试满足 C3。注意,给定的属性名称在每个 ConstraintSet 中只能出现一次,但可以出现在多个 ConstraintSet 中。
此示例代码暴露了一个按钮。点击后,按钮被禁用,并提示用户提供流。用户可以通过提供流(例如,授予页面访问本地摄像头的权限)然后禁用流(例如,撤销访问权限)来使按钮重新启用。
<button id="startBtn">Start</button>
<script>
const startBtn = document.getElementById('startBtn');
startBtn.onclick = async () => {
try {
startBtn.disabled = true;
const constraints = {
audio: true,
video: true
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
for (const track of stream.getTracks()) {
track.onended = () => {
startBtn.disabled = stream.getTracks().some((t) => t.readyState == 'live');
};
}
} catch (err) {
console.error(err);
}
};
</script>
此示例允许人们通过本地视频摄像头拍摄自己的照片。请注意,图像捕获规范 [image-capture] 提供了一种更简单的方法来实现这一点。
<script>
window.onload = async () => {
const video = document.getElementById('monitor');
const canvas = document.getElementById('photo');
const shutter = document.getElementById('shutter');
try {
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
await new Promise(resolve => video.onloadedmetadata = resolve);
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
document.getElementById('splash').hidden = true;
document.getElementById('app').hidden = false;
shutter.onclick = () => canvas.getContext('2d').drawImage(video, 0, 0);
} catch (err) {
console.error(err);
}
};
</script>
<h1>Snapshot Kiosk</h1>
<section id="splash">
<p id="errorMessage">Loading...</p>
</section>
<section id="app" hidden>
<video id="monitor" autoplay></video>
<button id="shutter">📷</button>
<canvas id="photo"></canvas>
</section>
本规范定义了两个由 名称 "camera" 和 "microphone" 标识的 强大功能(powerful features)。
它定义了以下类型和算法。
WebIDLdictionary CameraDevicePermissionDescriptor : PermissionDescriptor {
boolean panTiltZoom = false;
};
权限涵盖对至少一种设备的访问权限。
该描述符的语义是它查询对该类中任何设备的访问权限。因此,如果对 "camera" 权限的查询返回 "granted",则客户端知道无需权限提示即可访问一个摄像头;如果返回 "denied",则知道任何针对摄像头的 getUserMedia 请求都将失败。
如果用户代理认为已授予对某类中部分(而非全部)设备的权限,则查询将返回 "granted"。
如果用户代理认为已拒绝访问某类中的所有设备,则查询将返回 "denied"。
{name: "camera", panTiltZoom: true} 比 {name: "camera", panTiltZoom: false} 权限级别更高。
"granted" 权限并不能保证 getUserMedia 一定成功。它仅表明用户不会被提示授予权限。还有许多其他因素(例如约束或摄像头正在使用中)可能导致 getUserMedia 失败。
name 作为参数的结果。本规范定义了两个由字符串 "camera" 和 "microphone" 标识的 策略控制功能(policy-controlled features)。两者都有一个默认允许列表 "self"。
文档的 权限策略 决定了该文档中的任何内容是否允许使用 getUserMedia 分别请求摄像头或麦克风。如果任何文档中禁用了该功能,则该文档中的任何内容都将 不被允许使用 getUserMedia 分别请求摄像头或麦克风。这由 请求使用权限 算法强制执行。
此外,enumerateDevices 将仅枚举该文档 允许使用 的设备。
本规范使用算法从单个 MediaDevices 对象的角度表达隐私指示要求。鼓励实施者推演这些原则,以统一指示器的呈现方式,从而覆盖由于 iframe 而可能在页面上共存的多个 MediaDevices 对象。
对于 getUserMedia() 所暴露的每种 kind 的设备:
[[kindsAccessibleMap]][kind] 值与该种类设备的所有 [[devicesAccessibleMap]][deviceId] 值的逻辑或。[[kindsAccessibleMap]][kind] 值与该种类设备的所有 [[devicesLiveMap]][deviceId] 值的逻辑或。定义 anyAccessible 为所有 any<kind>Accessible 值的逻辑或。
定义 anyLive 为所有 any<kind>Live 值的逻辑或。
以下是针对 用户代理 (User Agent) 的要求:
[[devicesAccessibleMap]][deviceId] 值和 [[devicesLiveMap]][deviceId] 值,它 必须 至少在值发生变化时进行指示。以下是鼓励 用户代理 执行的行为:
[[devicesAccessibleMap]][deviceId] 值和 [[devicesLiveMap]][deviceId] 值,鼓励其提供关于该值当前状态的持续指示。还鼓励其使任何设备特定的硬件指示灯与对应的 [[devicesLiveMap]][deviceId] 值匹配。本节是非规范性的;它没有规定任何新行为,而是总结了规范其他部分已有的信息。
本规范扩展了 Web 平台以管理媒体输入设备——特别是麦克风和摄像头。它还有可能允许暴露有关其他媒体设备的信息,例如音频输出设备(扬声器和耳机),但此类暴露的细节留待其他规范处理。从用户的麦克风和摄像头捕获音频和视频会向应用程序暴露个人身份信息,因此本规范要求在共享之前获取明确的用户同意。
在进行摄像头或麦克风捕获之前,应用程序(即“drive-by web”)仅被赋予能够告知用户是否有摄像头或麦克风(但不能得知数量)的能力。设备标识符的设计旨在不被用于跨源跟踪用户的指纹,但摄像头或麦克风功能的存在为指纹表面增加了两比特的信息。建议将跨源持久性标识符 deviceId 视为其他持久性存储(如 cookie)来处理。
一旦开始摄像头或麦克风捕获,本规范描述了如何获取和使用来自上述设备的媒体数据。这些数据可能是敏感的;建议提供指示器以显示设备正在使用中,但许可的性质和正在使用中的设备的指示器均由平台决定。
开始捕获的许可可以按需授予,也可以是持久的。在按需许可的情况下,用户能够以防止 UI 在获得许可前阻止用户交互的方式说“不”是很重要的——可以通过提供一种说“永久拒绝”的方式,或者不使用模态许可对话框来实现。
一旦开始摄像头或麦克风捕获,Web 文档便获得列出所有可用媒体捕获设备及其标签的能力。此能力持续到 Web 文档关闭,且不能被持久化。在大多数情况下,标签在跨源之间是稳定的,因此有可能提供一种跨时间和源跟踪特定设备的方法。
本规范暴露了除正在使用的设备之外的设备信息。这是出于向后兼容和遗留原因。建议未来的规范不要使用此模型,而是遵循 设备枚举设计原则 中描述的最佳实践。
对于已经开始或进行过捕获的开放 Web 文档,或者对于 处于视图中 的 Web 文档,每当添加或移除新的媒体设备时,devicechange 事件可能最终会在 可导航对象 (navigables) 和源之间同时触发;用户代理可以通过对这些事件的触发时间进行模糊化,或者将其触发推迟到这些 Web 文档 进入视图,来降低跨源关联浏览活动的风险。
一旦 Web 文档获得对来自捕获设备的媒体流的访问权限,它也获得了有关该设备的详细信息,包括其运行能力范围(例如摄像头的可用分辨率)。这些运行能力在很大程度上在浏览会话和源之间是持久的,因此提供了一种跨时间和源跟踪特定设备的方法。
一旦获得对来自捕获设备的视频流的访问权限,该流很可能被用于唯一地指纹化所述设备(例如通过死像素检测)。类似地,一旦获得对音频流的访问权限,该流很可能被用于将用户位置指纹化到房间级别,甚至识别不同用户是否同时占据同一房间(例如通过分析环境音频或故意通过设备扬声器播放的独特音频)。针对音频和视频的用户级缓解措施包括遮盖摄像头和/或麦克风,或通过 用户代理 浏览器控件撤销许可。
可以使用约束条件,使得 getUserMedia 调用失败时返回有关系统设备的信息,而无需提示用户,这增加了可用于指纹识别的表面积。用户代理 应考虑限制失败的 getUserMedia 调用的频率,以限制这种额外的表面积。
对于已存储的用于开始捕获的持久许可,重要的是能够轻松找到已授予的许可列表并撤销用户希望撤销的许可。
一旦授予许可,用户代理 应使两件事对用户显而易见:
拥有已存储许可的网站的开发者应小心,不要滥用这些许可。这些许可可以使用 [Permissions] API 撤销。
特别是,他们不应提供自动将来自授权媒体设备的音频或视频流发送到第三方可选择的端点的功能。
确实,如果一个网站提供诸如 https://webrtc.example.org/?call=user 之类的 URL,自动建立呼叫并向 user 传输音频/视频,它将面临以下滥用风险:
已经授予存储许可给 https://webrtc.example.org/ 的用户可能会被诱导将他们的音频/视频流发送给攻击者 EvilSpy,只需点击一个链接或被重定向到 https://webrtc.example.org/?user=EvilSpy。
本节是非规范性的。
尽管未来可能会发布本规范的新版本,但也预期其他标准将需要定义构建在本规范基础上的新功能。本节的目的是为此类扩展的创建者提供指导。
本规范中任何 WebIDL 定义的接口、方法或属性都可以扩展。两个可能的扩展点是定义新的媒体类型和定义新的可约束属性。
kind 媒体(音频和视频之外)至少,定义一种新的媒体类型将需要:
MediaStream 接口添加该类型的新 getXXXXTracks() 方法,MediaStreamTrack 接口上 kind 属性的额外有效值,HTMLMediaElement 如何处理包含该新媒体类型轨道的 MediaStream(见 6. 媒体元素中的 MediaStream),包括为新媒体类型添加一个类似于 可听/不可听 (audible/inaudible) 的推论,MediaDeviceKind,getCapabilities() 和 getUserMedia() 的描述,MediaStreamConstraints 字典中,kind 关联的新 PermissionDescriptor 名称,并定义这些权限(以及访问权限的启动与结束,还有静音/禁用状态)如何影响任何新的和/或现有的“on-air”和“设备可访问”指示器状态(见 MediaDevices)。此外,还应包括更新:
MediaStreamTrack 接口上 label 属性的描述,它还可能包括:
MediaStreamTrackState 中提供此类轨道如何结束的示例。这将需要深思熟虑并定义该属性的约束、能力和设置(见 3. 术语)将如何工作。MediaTrackSupportedConstraints、MediaTrackCapabilities、MediaTrackConstraints、MediaTrackSettings、4.3.8 可约束属性 和 MediaStreamConstraints 中的相关文本即为所使用的模型。
强烈鼓励扩展规范的创建者在 规范存储库 上通知规范维护者。
本规范的未来版本以及 WebRTC 工作组创建的其他规范将考虑所有已知的扩展,以试图减少潜在的用法冲突。
MediaStreamTrack 和 MediaStream 的新接收器 (sink)其他规范可以定义 MediaStream 和/或 MediaStreamTrack 的新接收器。至少,MediaStreamTrack 的新消费者需要定义:
MediaStreamTrack 在其所处的各种状态下(包括静音和禁用)将被如何消费(见 4.3.1 媒体流与生命周期)。MediaStreamTrack 的新 源 (source)其他规范可以定义 MediaStreamTrack 的新源。至少,MediaStreamTrack 的新源将需要:
kind),(getUserMedia() 专门用于摄像头和麦克风源),kind 适用于哪些可约束属性(见 4.3.8 可约束属性,如果有的话),以及它们如何与此源配合工作,编辑们谨向工作组主席及团队联系人 Harald Alvestrand、Stefan Håkansson、Erik Lagerway 和 Dominique Hazaël-Massieux 表示感谢,感谢他们的支持。本规范中的大量文本由多人提供,包括 Jim Barnett、Harald Alvestrand、Travis Leithead、Josh Soref、Martin Thomson、Jan-Ivar Bruaroey、Peter Thatcher、Dominique Hazaël-Massieux 和 Stefan Håkansson。Dan Burnett 谨感谢 Voxeo 和 Aspect 在本规范开发期间提供的重大支持。
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自