getUserMedia() 配了三年,今天 Chrome 终于给了一个原生替代——usermedia 把摄像头权限和流获取全包了

以前让用户打开摄像头,代码要写十几行、还要处理一堆回调和错误状态。现在 Chrome 151 原生支持 <usermedia>,一个 HTML 标签直接替代整个流程——连用户拒绝过权限的恢复路径都给你做好了。

以前怎么写:getUserMedia() 的三段式噩梦

Web 应用接入摄像头和麦克风,标准做法是调用 navigator.mediaDevices.getUserMedia():

// 触发权限提示
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  .then(stream => {
    videoPreview.srcObject = stream;
    // 业务逻辑
  })
  .catch(err => {
    if (err.name === "NotAllowedError") {
      // 用户拒绝了,需要引导去设置里手动打开
      showRecoveryInstructions();
    } else if (err.name === "NotFoundError") {
      // 没找到设备
    } else if (err.name === "NotReadableError") {
      // 硬件被其他应用占用
    }
  });

这段代码有三个问题:

一是提示体验差。 getUserMedia() 是脚本主动触发浏览器的权限提示,浏览器很难判断用户是真的想打开摄像头还是被某个自动化脚本劫持了。很多情况下,即使用户真的点了允许,浏览器静默拦截了请求。

二是拒绝后恢复难。 用户一旦误点了拒绝,想重新打开就需要去浏览器设置里翻好几层菜单才能找到对应网站,很多用户直接就放弃了这个功能。

三是代码冗长。 每加一个视频通话功能,都要复制粘贴这套模板,改来改去容易出 bug。

Chrome 151 的答案:<usermedia> 是什么

<usermedia> 是 Capability Elements 系列的第二个成员(第一个是 Chrome 144 的 <geolocation>)。它本质上是一个浏览器控制的声明式组件,替代了原来 imperative 的 getUserMedia() 调用。

基本用法:

<usermedia id="media-ctrl">
  <button>启用摄像头和麦克风</button>
</usermedia>
const el = document.getElementById("media-ctrl");

// 设置硬件参数(在用户点击之前设置)
el.setConstraints({
  video: { width: 1280, height: 720 },
  audio: { echoCancellation: true }
});

// 获取到媒体流时触发
el.addEventListener("stream", () => {
  videoPreview.srcObject = el.stream;
});

// 获取失败时触发
el.addEventListener("error", () => {
  console.error(`错误类型: ${el.error?.name}`);
});

// 用户关闭了权限提示时触发
el.addEventListener("cancel", () => {
  console.log("用户关闭了权限提示");
});

整个流程由浏览器控制,用户点击按钮 → 浏览器弹出权限提示 → 权限允许后 stream 事件自动触发,流直接通过 el.stream 属性暴露出来。

最大的改进:拒绝后恢复

这是 <usermedia> 最有价值的部分。

根据 Google 发布的真实数据:

  • Cisco 测试发现:用户第一次拒绝权限后,用旧版 API 只有 10% 能重新成功授权;用 <usermedia> 后成功率跳到 65% 以上。
  • Google Meet 报告:使用新元素后,麦克风不工作的反馈减少了 17%;之前拒绝过权限的用户重新授权成功率提升了 131%。
  • Zoom 报告:摄像头和麦克风被系统级阻断的错误减少了 46.9%。

背后的原因是:旧版 API 触发权限提示时,浏览器只能猜测用户意图,容易触发静默阻断;<usermedia> 的提示只在用户真实点击按钮后才出现,浏览器会把它视为明确的意图信号,直接绕过静默阻断。

如果用户之前拒绝过,点一下按钮就能在当前页直接重新授权,不需要去浏览器设置里翻菜单。

对比一览

特性 getUserMedia() JS API <usermedia> HTML 元素
触发方式 脚本直接调用 用户点击浏览器控制元素
浏览器角色 根据状态和启发式判断是否弹提示 作为数据中介,管理授权和流传递
站点职责 调用 API、处理回调、管理错误 监听 stream 事件、访问 stream 属性
恢复路径 需手动引导用户去设置翻菜单 按钮点击自动触发专用恢复流程
代码量 15+ 行模板 声明式标签 + 几个事件监听

实际场景:视频通话接入

把 <usermedia> 集成到一个视频通话功能里:

<div id="pre-call-ui">
  <usermedia id="video-call" controls>
    <button>加入会议</button>
  </usermedia>
  <video id="preview" autoplay muted playsinline></video>
</div>
const videoCall = document.getElementById("video-call");

videoCall.setConstraints({
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 },
    facingMode: "user"
  },
  audio: {
    echoCancellation: true,
    noiseSuppression: true
  }
});

videoCall.addEventListener("stream", (e) => {
  const stream = e.target.stream;
  document.getElementById("preview").srcObject = stream;
  // 连接到 WebRTC 或者发给服务器
  connectToConference(stream);
});

videoCall.addEventListener("error", (e) => {
  const errorName = e.target.error?.name;
  if (errorName === "NotAllowedError") {
    showToast("请在浏览器设置中允许摄像头访问");
  } else if (errorName === "NotFoundError") {
    showToast("未检测到摄像头,请确认已连接");
  }
});

和旧版 API 相比,这段代码逻辑更清晰——事件驱动而不是 Promise 链,error 事件统一处理所有失败情况,不再需要手动对照错误名做分支判断。

浏览器支持情况

目前 <usermedia> 随 Chrome 151 发布,属于 Chrome 专属特性,Safari 和 Firefox 尚未支持。和 <geolocation> 一样,这是一个渐进增强的场景——在不支持的浏览器里,需要回退到原来的 getUserMedia() 实现:

if ("usermedia" in HTMLElement.prototype) {
  // 用新 API
} else {
  // 回退到 getUserMedia()
}

下一步

如果你的应用里有任何需要调用摄像头或麦克风的场景,都值得看一下 <usermedia> 能帮你省掉多少模板代码。尤其是视频通话、在线面试、远程协作这类功能,用户的权限恢复路径往往是被忽视的环节——用户第一次拒绝后找不到重新授权的入口,就会直接放弃使用。把这个流程做成按钮点击就能解决,实际的用户完成率会有明显提升。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 1689 阅读