MediaDevices: getUserMedia() 方法
getUserMedia() 方法是 MediaDevices 接口的一个方法,它会提示用户授予权限以使用媒体输入设备,从而生成一个包含所需媒体类型轨道的 MediaStream。
该媒体流可以包含,例如,一个视频轨道(由硬件或虚拟视频源生成,如摄像头、视频录制设备、屏幕共享服务等),一个音频轨道(同样,由物理或虚拟音频源生成,如麦克风、A/D 转换器等),以及可能其他类型的轨道。
它返回一个 Promise,该 Promise 在成功时解析为一个 MediaStream 对象。如果用户拒绝权限,或者没有可用的匹配媒体,则 Promise 会分别以 NotAllowedError 或 NotFoundError DOMException 拒绝。
注意: 返回的 Promise 既可能不解析也不拒绝,因为用户不一定需要做出选择,并且可能会忽略请求。
语法
getUserMedia(constraints)
参数
约束-
一个对象,用于指定请求的媒体类型,以及每种类型的任何要求。
constraints参数是一个包含video和audio两个成员的对象,用于描述请求的媒体类型。必须指定其中一个或两个。如果浏览器找不到所有符合给定约束条件的指定类型的媒体轨道,则返回的 Promise 会以NotFoundErrorDOMException拒绝。对于
video和audio,其值可以是布尔值或对象。默认值为false。- 如果为媒体类型指定
true,则生成的媒体流必须包含该类型的轨道。如果由于任何原因无法包含,返回的 Promise 将被拒绝。 - 如果为媒体类型指定
false,则生成的媒体流不得包含该类型的轨道,否则返回的 Promise 将被拒绝。因为video和audio都默认为false,所以如果constraints对象不包含任何属性或根本不存在,返回的 Promise 将始终被拒绝。 - 如果为媒体类型指定了一个对象,该对象将被读取为
MediaTrackConstraints字典。
- 如果为媒体类型指定
返回值
一个 Promise,当成功获取到请求的媒体时,其完成处理程序会接收一个 MediaStream 对象。
异常
AbortErrorDOMException-
尽管用户和操作系统都已授予对硬件设备的访问权限,并且没有发生导致
NotReadableErrorDOMException的硬件问题,但如果发生阻止设备使用的问题,则抛出此异常。 InvalidStateErrorDOMException-
如果当前文档未完全激活,则抛出此异常。
NotAllowedErrorDOMException-
如果一个或多个请求的源设备当前无法使用,则抛出此异常。这会在浏览上下文不安全(即页面使用 HTTP 而非 HTTPS 加载)时发生。如果用户指定当前浏览实例不允许访问设备、用户已拒绝当前会话的访问权限,或者用户已全局拒绝所有用户媒体设备的访问权限,也会发生这种情况。在支持使用 Permissions Policy 管理媒体权限的浏览器上,如果 Permissions Policy 未配置为允许访问输入源,则返回此错误。
注意: 规范的旧版本使用
SecurityError代替此错误;SecurityError已具有新含义。 NotFoundErrorDOMException-
如果未找到满足给定约束条件的指定类型的媒体轨道,则抛出此异常。
NotReadableErrorDOMException-
如果用户已授予使用匹配设备的权限,但操作系统、浏览器或网页级别发生硬件错误,阻止了对设备的访问,则抛出此异常。
OverconstrainedErrorDOMException-
如果指定的约束条件导致没有符合请求标准的候选设备,则抛出此异常。该错误是一个
OverconstrainedError类型的对象,并具有一个constraint属性,其字符串值是无法满足的约束条件的名称,以及一个message属性,其中包含解释问题的可读字符串。注意: 由于此错误甚至可能在用户尚未授予使用底层设备的权限时发生,因此它可能被用作 指纹识别 表面。
SecurityErrorDOMException-
如果调用
getUserMedia()的Document上禁用了用户媒体支持,则抛出此异常。启用和禁用用户媒体支持的机制由各个用户代理自行决定。 TypeError-
如果指定的约束列表为空,或所有约束都设置为
false,则抛出此异常。如果在不安全的环境中尝试调用getUserMedia()也可能发生这种情况,因为在不安全的环境中navigator.mediaDevices是undefined。
隐私和安全
作为一项可能涉及重大隐私问题的 API,getUserMedia() 的规范规定了浏览器必须满足的广泛隐私和安全要求。
getUserMedia() 是一项强大的功能,只能在 安全上下文 中使用;在不安全上下文中,navigator.mediaDevices 是 undefined,从而阻止访问 getUserMedia()。简而言之,安全上下文是使用 HTTPS 或 file:/// URL 方案加载的页面,或从 localhost 加载的页面。
此外,始终需要用户权限才能访问用户的音频和视频输入。只有有效来源的窗口顶层文档上下文才能请求使用 getUserMedia() 的权限,除非顶层上下文使用 Permissions Policy 明确授予给定 这样做权限。否则,用户甚至不会被要求授予使用输入设备的权限。
有关这些要求和规则、它们如何反映在您的代码运行的上下文中,以及浏览器如何管理用户隐私和安全问题的更多详细信息,请继续阅读。
用户隐私
作为一项可能涉及重大隐私问题的 API,getUserMedia() 在规范中对用户通知和权限管理有非常具体的要求。首先,getUserMedia() 必须始终在打开任何媒体采集输入(例如网络摄像头或麦克风)之前获得用户许可。浏览器可能会提供一次性域权限功能,但它们至少在第一次必须请求,并且如果用户选择这样做,则必须明确授予持续权限。
同样重要的是通知规则。浏览器必须显示一个指示器,表明摄像头或麦克风正在使用中,这超出了任何可能存在的硬件指示器。它们还必须显示一个指示器,表明已授予使用设备进行输入的权限,即使该设备当前未 actively 录制。
例如在 Firefox 中,URL 地址栏会显示一个脉冲红色图标,指示正在录制。如果权限已到位但当前未进行录制,则图标为灰色。设备的物理灯用于指示录制是否当前处于活动状态。如果您已将摄像头静音(所谓的“面部静音”),您的摄像头的活动指示灯会熄灭,表示摄像头当前未主动录制您,但不会放弃在静音结束后恢复使用摄像头的权限。
安全
用户代理 中的安全管理和控制有多种方式可能导致 getUserMedia() 返回与安全相关的错误。
权限策略
适用于 getUserMedia() 的两个 Permissions Policy 指令是 camera 和 microphone。
例如,此 HTTP 标头将允许文档和任何从相同来源加载的嵌入式 元素使用摄像头。
Permissions-Policy: camera=(self)
这将请求对当前来源和特定来源 https://mdn.org.cn 的麦克风访问权限。
Permissions-Policy: microphone=(self "https://mdn.org.cn")
如果您在 中使用 getUserMedia(),您可以仅请求该框架的权限,这显然比请求更普遍的权限更安全。这里,表示我们需要使用摄像头和麦克风的能力。
<iframe src="https://mycode.example.net/etc" allow="camera; microphone">
</iframe>
基于加密的安全
getUserMedia() 方法仅在 安全上下文 中可用。安全上下文是浏览器合理地确信其中包含安全加载的文档(使用 HTTPS/TLS)并且与不安全上下文的暴露有限的上下文。如果文档未在安全上下文中加载,则 navigator.mediaDevices 属性是 undefined,从而无法访问 getUserMedia()。
在这种情况下尝试访问 getUserMedia() 将导致 TypeError。
文档源安全
由于 getUserMedia() 如果意外使用或未仔细管理安全性,可能会带来明显的安全问题,因此它只能在安全上下文中使用。有许多不安全的加载文档的方式,这些文档反过来可能会尝试调用 getUserMedia()。以下是禁止调用 getUserMedia() 的情况示例:
示例
使用 getUserMedia()
通常,您将使用 navigator.mediaDevices 访问 MediaDevices 单例对象,如下所示:
async function getMedia(constraints) {
let stream = null;
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
/* use the stream */
} catch (err) {
/* handle the error */
}
}
同样,直接使用原始 Promise,代码如下:
navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) => {
/* use the stream */
})
.catch((err) => {
/* handle the error */
});
注意: 如果当前文档未安全加载,navigator.mediaDevices 将是 undefined,您将无法使用 getUserMedia()。有关此问题以及与使用 getUserMedia() 相关的其他安全问题的更多信息,请参阅安全。
以下是 constraints 参数的一些示例。
以下请求音频和视频,没有任何具体要求:
getUserMedia({
audio: true,
video: true,
});
尽管出于隐私原因无法访问有关用户摄像头和麦克风的信息,但应用程序可以使用额外的约束条件请求其需要和想要的摄像头和麦克风功能。以下表示对 1280x720 摄像头分辨率的偏好:
getUserMedia({
audio: true,
video: { width: 1280, height: 720 },
});
浏览器将尝试遵循约束,如果底层硬件支持,将返回匹配的轨道。如果不支持,浏览器可能会尝试从底层硬件裁剪和缩小更高分辨率的流以匹配约束(如果帧速率也受到约束,也可能会降低帧速率)。可以通过将 resizeMode 约束设置为 crop-and-scale 来强制执行此行为(或通过将其设置为 none 来禁用)
getUserMedia({
audio: true,
video: { width: 1280, height: 720, resizeMode: "crop-and-scale" },
});
如果无法获得精确匹配且源无需缩放,浏览器可能会返回其他分辨率。
要要求某个功能并在其不可用时失败,请使用关键字 min、max 或 exact(也即 min === max)。以下要求最小分辨率为 1280x720:
getUserMedia({
audio: true,
video: {
width: { min: 1280 },
height: { min: 720 },
},
});
如果不存在具有此分辨率或更高分辨率的摄像头,则返回的 Promise 将被 OverconstrainedError 拒绝,并且不会提示用户。
行为差异的原因是关键字 min、max 和 exact 本质上是强制性的——而普通值和名为 ideal 的关键字则不是。这是一个完整的示例:
getUserMedia({
audio: true,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 576, ideal: 720, max: 1080 },
},
});
ideal 值在使用时具有“引力”——这意味着浏览器将尝试找到与给定理想值 适应距离 最小的设置(以及摄像头,如果您有多个)。
普通值本质上是理想的,这意味着我们上面第一个分辨率示例可以这样写:
getUserMedia({
audio: true,
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
},
});
并非所有约束都是数字。例如,在移动设备上,以下将优先选择前置摄像头(如果可用)而不是后置摄像头:
getUserMedia({
audio: true,
video: { facingMode: "user" },
});
要要求使用后置摄像头,请使用:
getUserMedia({
audio: true,
video: {
facingMode: { exact: "environment" },
},
});
另一个非数字约束是 deviceId 约束。如果您有来自 mediaDevices.enumerateDevices() 的 deviceId,您可以使用它来请求特定设备:
getUserMedia({
video: {
deviceId: myPreferredCameraDeviceId,
},
});
上述代码将返回您请求的摄像头,如果该特定摄像头不再可用,则返回另一个摄像头。同样,要要求使用特定摄像头,您可以使用:
getUserMedia({
video: {
deviceId: {
exact: myExactCameraOrBustDeviceId,
},
},
});
宽度和高度
此示例优先选择摄像头分辨率,并将生成的 MediaStream 对象分配给视频元素。
// 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 传输,较低的帧速率可能更可取。
const constraints = {
video: { frameRate: { ideal: 10, max: 15 } },
};
前置和后置摄像头
在移动电话上。
let front = false;
document.getElementById("flip-button").onclick = () => {
front = !front;
};
const constraints = {
video: { facingMode: front ? "user" : "environment" },
};
注意: 在某些情况下,可能需要先释放当前摄像头朝向模式,然后才能切换到不同的模式。为确保摄像头切换,建议在请求不同的朝向模式之前,通过调用轨道的“stop()”方法来释放媒体资源。
规范
| 规范 |
|---|
| 媒体捕获和流 # dom-mediadevices-getusermedia |
浏览器兼容性
加载中…
另见
- 较旧的
Navigator.getUserMedia()传统 API MediaDevices.enumerateDevices(): 列出可用的媒体设备- WebRTC API
- 媒体捕获和流 API
- 屏幕捕获 API: 将屏幕内容捕获为
MediaStream MediaDevices.getDisplayMedia(): 获取包含屏幕内容的媒体流- 拍摄网络摄像头照片: 一个关于如何使用
getUserMedia()拍摄静态照片而非视频的教程