MediaDevices:getUserMedia() 方法

基线 广泛可用

此功能已经稳定,并且可以在许多设备和浏览器版本上运行。它自以下日期起在浏览器中可用: 2017 年 9 月.

安全上下文:此功能仅在安全上下文(HTTPS)中可用,在某些或所有支持的浏览器中。

MediaDevices 接口的getUserMedia() 方法会提示用户是否允许使用媒体输入,该输入会生成一个包含请求的媒体类型的轨道的MediaStream

例如,该流可以包含视频轨道(由硬件或虚拟视频源生成,例如摄像头、录像设备、屏幕共享服务等),音频轨道(类似地,由物理或虚拟音频源生成,例如麦克风、A/D 转换器等),以及可能的其他轨道类型。

它返回一个Promise,该 Promise 解析为一个MediaStream 对象。如果用户拒绝权限,或无法获取匹配的媒体,则 Promise 会分别被拒绝,并返回 NotAllowedErrorNotFoundError DOMException

注意:返回的 Promise 可能既不解析也不拒绝,因为用户无需做出任何选择,并且可以忽略请求。

语法

js
getUserMedia(constraints)

参数

constraints

一个对象,用于指定要请求的媒体类型以及每种类型的任何要求。

constraints 参数是一个包含两个成员的对象:videoaudio,描述了请求的媒体类型。必须指定其中一个或两个。如果浏览器找不到满足给定约束的所有具有指定类型的媒体轨道,则返回的 Promise 会被拒绝,并返回 NotFoundError DOMException

对于 videoaudio,其值可以是布尔值或对象。默认值为 false

  • 如果为媒体类型指定了 true,则生成的流必须包含该类型的轨道。如果由于任何原因无法包含,则返回的 Promise 将被拒绝。
  • 如果为媒体类型指定了 false,则生成的流不能包含该类型的轨道,否则返回的 Promise 将被拒绝。由于 videoaudio 均默认为 false,因此如果 constraints 对象不包含任何属性或根本不存在,则返回的 Promise 将始终被拒绝。
  • 如果为媒体类型指定了对象,则该对象将被读取为MediaTrackConstraints 字典。

返回值

一个Promise,其 fulfilled 处理程序在成功获取请求的媒体时接收一个MediaStream 对象。

异常

AbortError DOMException

尽管用户和操作系统都授予了对硬件设备的访问权限,并且没有发生会导致 NotReadableError DOMException 的硬件问题,但如果发生阻止使用设备的一些问题,则会抛出此异常。

InvalidStateError DOMException

如果当前文档未完全激活,则抛出此异常。

NotAllowedError DOMException

如果一个或多个请求的源设备此时无法使用,则抛出此异常。如果浏览上下文不安全(即,页面使用 HTTP 而不是 HTTPS 加载),则会出现这种情况。如果用户指定当前浏览实例不允许访问该设备,或者用户已拒绝当前会话的访问权限,或者用户已全局拒绝所有对用户媒体设备的访问权限,也会发生这种情况。在支持使用权限策略管理媒体权限的浏览器中,如果权限策略未配置为允许访问输入源,则会返回此错误。

注意:较旧版本的规范为此使用了 SecurityErrorSecurityError 具有新的含义。

NotFoundError DOMException

如果未找到满足给定约束的指定类型的媒体轨道,则抛出此异常。

NotReadableError DOMException

如果用户授予了使用匹配设备的权限,但在操作系统、浏览器或网页级别发生了硬件错误,从而阻止了对设备的访问,则抛出此异常。

OverconstrainedError DOMException

如果指定的约束导致没有满足请求条件的候选设备,则抛出此异常。该错误是一个 OverconstrainedError 类型的对象,并且具有一个 constraint 属性,其字符串值为无法满足的约束的名称,以及一个 message 属性,其中包含一个解释问题的易于理解的字符串。

注意:即使用户尚未授予使用底层设备的权限,也可能发生此错误,因此它可能被用作指纹表面。

SecurityError DOMException

如果在调用 getUserMedia()Document 上禁用了用户媒体支持,则抛出此异常。启用和禁用用户媒体支持的机制由各个用户代理决定。

TypeError

如果指定的约束列表为空,或者所有约束都设置为 false,则抛出此异常。如果您尝试在不安全上下文中调用 getUserMedia(),也会发生这种情况,因为在不安全上下文中navigator.mediaDevicesundefined

隐私和安全

作为可能涉及重大隐私问题的 API,getUserMedia() 的规范规定了浏览器必须满足的各种隐私和安全要求。

getUserMedia() 是一项强大的功能,只能在安全上下文中使用;在不安全上下文中,navigator.mediaDevicesundefined,从而阻止访问 getUserMedia()。简而言之,安全上下文是指使用 HTTPS 或 file:/// URL 方案加载的页面,或从 localhost 加载的页面。

此外,访问用户的音频和视频输入始终需要用户权限。只有有效来源的窗口的顶级文档上下文才能请求使用 getUserMedia() 的权限,除非顶级上下文明确授予给定<iframe>使用权限策略执行此操作的权限。否则,系统将永远不会询问用户是否允许使用输入设备。

有关这些要求和规则的更多详细信息、它们如何在代码运行的上下文中反映出来,以及浏览器如何管理用户隐私和安全问题,请继续阅读。

用户隐私

作为可能涉及重大隐私问题的 API,规范对 getUserMedia() 提出了非常具体的用户通知和权限管理要求。首先,getUserMedia() 必须始终在打开任何媒体收集输入(如网络摄像头或麦克风)之前获取用户权限。浏览器可能会提供每个域一次的权限功能,但它们必须至少在第一次询问,并且如果用户选择这样做,则必须明确授予持续权限。

通知规则同样重要。除了可能存在的任何硬件指示器之外,浏览器还必须显示一个指示器,表明摄像头或麦克风正在使用。即使设备目前未处于活动录制状态,它们也必须显示已授予使用设备进行输入的权限的指示器。

例如,在 Firefox 中,URL 栏会显示一个脉冲红色图标,以指示正在进行录制。如果权限已到位但当前未进行录制,则该图标为灰色。设备的物理指示灯用于指示当前是否正在进行录制。如果您已将摄像头静音(所谓的“面部静音”),则摄像头的活动指示灯会熄灭,以指示摄像头未主动录制您,而不会丢弃在静音结束后恢复使用摄像头的权限。

安全

用户代理中,安全管理和控制有多种方式可能导致 getUserMedia() 返回与安全相关的错误。

权限策略

适用于 getUserMedia() 的两个权限策略指令为 cameramicrophone

例如,此 HTTP 标头将启用文档和从同一来源加载的任何嵌入式<iframe> 元素使用摄像头。

http
Permissions-Policy: camera=(self)

这将请求访问当前来源和特定来源https://mdn.org.cn的麦克风。

http
Permissions-Policy: microphone=(self "https://mdn.org.cn")

如果您在<iframe>内使用getUserMedia(),您可以仅请求该框架的权限,这显然比请求更通用的权限更安全。在这里,表示我们需要能够使用摄像头和麦克风。

html
<iframe src="https://mycode.example.net/etc" allow="camera; microphone">
</iframe>

基于加密的安全

getUserMedia()方法仅在安全上下文中可用。安全上下文是指浏览器相当有把握地认为包含以HTTPS/TLS安全加载的文档,并且对不安全上下文的暴露有限的上下文。如果文档未在安全上下文中加载,则navigator.mediaDevices属性为undefined,使得无法访问getUserMedia()

在这种情况下尝试访问getUserMedia()将导致TypeError

文档源安全

由于getUserMedia()在意外使用或没有仔细管理安全的情况下存在明显的安全问题,因此它只能在安全上下文中使用。有一些不安全的方式来加载文档,这些文档可能会尝试调用getUserMedia()。以下是一些不允许调用getUserMedia()的情况的示例

  • 加载到沙盒化<iframe>元素中的文档无法调用getUserMedia(),除非<iframe>sandbox属性设置为allow-same-origin
  • 使用data://blob:// URL(没有来源,例如用户在地址栏中键入其中一个URL时)加载的文档无法调用getUserMedia()。从JavaScript代码加载的这些类型的URL继承脚本的权限。
  • 任何其他没有来源的情况,例如当使用srcdoc属性指定框架内容时。

示例

使用getUserMedia()

通常,您将使用navigator.mediaDevices访问MediaDevices单例对象,如下所示

js
async function getMedia(constraints) {
  let stream = null;

  try {
    stream = await navigator.mediaDevices.getUserMedia(constraints);
    /* use the stream */
  } catch (err) {
    /* handle the error */
  }
}

类似地,直接使用原始Promise,代码如下所示

js
navigator.mediaDevices
  .getUserMedia(constraints)
  .then((stream) => {
    /* use the stream */
  })
  .catch((err) => {
    /* handle the error */
  });

注意:如果当前文档未安全加载,则navigator.mediaDevices将为undefined,并且您无法使用getUserMedia()。有关此问题以及与使用getUserMedia()相关的其他安全问题的更多信息,请参阅安全

以下是一些constraints参数的示例。

以下请求音频和视频,没有任何特定要求

js
getUserMedia({
  audio: true,
  video: true,
});

出于隐私原因,用户摄像头和麦克风的信息无法访问,但应用程序可以使用其他约束来请求其需要和想要的摄像头和麦克风功能。以下表示对1280x720摄像头分辨率的偏好

js
getUserMedia({
  audio: true,
  video: { width: 1280, height: 720 },
});

浏览器将尝试满足此要求,但如果无法找到完全匹配项,或者用户覆盖了它,则可能会返回其他分辨率。

强制要求功能,请使用关键字minmaxexact(也称为min === max)。以下要求最低分辨率为1280x720

js
getUserMedia({
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 },
  },
});

如果不存在此分辨率或更高分辨率的摄像头,则返回的Promise将被拒绝并显示OverconstrainedError,并且不会提示用户。

行为差异的原因是关键字minmaxexact本质上是强制性的——而普通值和称为ideal的关键字则不是。这是一个完整的示例

js
getUserMedia({
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 576, ideal: 720, max: 1080 },
  },
});

ideal值在使用时具有权重——这意味着浏览器将尝试查找设置(如果您有多个摄像头,则查找摄像头),其与给定理想值的适应度距离最小。

普通值本质上是理想的,这意味着我们上面第一个分辨率示例可以这样编写

js
getUserMedia({
  audio: true,
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 },
  },
});

并非所有约束都是数字。例如,在移动设备上,以下内容将优先选择前置摄像头(如果可用)而不是后置摄像头

js
getUserMedia({
  audio: true,
  video: { facingMode: "user" },
});

强制要求后置摄像头,请使用

js
getUserMedia({
  audio: true,
  video: {
    facingMode: { exact: "environment" },
  },
});

另一个非数字约束是deviceId约束。如果您从mediaDevices.enumerateDevices()获得了deviceId,则可以使用它来请求特定设备

js
getUserMedia({
  video: {
    deviceId: myPreferredCameraDeviceId,
  },
});

以上将返回您请求的摄像头,或者如果该特定摄像头不再可用,则返回不同的摄像头。同样,要强制要求特定摄像头,您将使用

js
getUserMedia({
  video: {
    deviceId: {
      exact: myExactCameraOrBustDeviceId,
    },
  },
});

宽度和高度

此示例表示对摄像头分辨率的偏好,并将生成的MediaStream对象分配给视频元素。

js
// Prefer camera resolution nearest to 1280x720.
const constraints = {
  audio: true,
  video: { width: 1280, height: 720 },
};

navigator.mediaDevices
  .getUserMedia(constraints)
  .then((mediaStream) => {
    const video = document.querySelector("video");
    video.srcObject = mediaStream;
    video.onloadedmetadata = () => {
      video.play();
    };
  })
  .catch((err) => {
    // always check for errors at the end.
    console.error(`${err.name}: ${err.message}`);
  });

帧率

在某些情况下,较低的帧率可能更可取,例如带宽受限的WebRTC传输。

js
const constraints = {
  video: { frameRate: { ideal: 10, max: 15 } },
};

前后摄像头

在手机上。

js
let front = false;
document.getElementById("flip-button").onclick = () => {
  front = !front;
};

const constraints = {
  video: { facingMode: front ? "user" : "environment" },
};

注意:在某些情况下,可能需要释放当前摄像头方向模式才能切换到不同的模式。为确保摄像头切换,建议在请求不同的方向模式之前,通过在轨道上调用“stop()”方法来释放媒体资源。

规范

规范
媒体捕获和流
# dom-mediadevices-getusermedia

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅