灏天阁

WebRTC 之媒体 getUserMedia 使用限制及解决

· Yin灏

http 访问处理

理论上来说,媒体 getUserMedia 是不支持 http 访问,必须要用 https 协议,而且有合法的域名

但是有些情况下,必须要在 http 中访问,那就需要对浏览器进行特殊配置,让浏览器支持。

谷歌浏览器中,手动指定某些域名安全,如下图所示配置:

chrome://flags/#unsafely-treat-insecure-origin-as-secure

getUserMedia_secure.png

ip 访问处理

在开发测试阶段时,如果没有申请好域名时,可以用 IP 地址进行临时访问。

但是第一次访问时,需要添加安全例外,进行授信处理。

之后每次清除缓存后重新访问,也需要处理,添加安全例外

getUserMedia_safe.png

申请授权处理

初次访问网站使用 getUserMedia,会弹出禁止允许的授权弹框,选择了之后,浏览器会记住用户选择

下次再请求时,就不用弹出授权弹框了,直接用之前用户的选择。如下图所示:

getUserMedia_auth.png

注意:如果要再次弹出授权框的话,需要清除浏览器缓存中的记录

在 iframe 中的使用

  1. 在 iframe 中使用媒体功能,需要进行如下配置
iframe.allow = "midi;encrypted-media;microphone *;camera *;";
  1. 在 iframe 中使用屏幕共享功能,需要进行如下配置:
iframe.allow = "display-capture *;";
  1. 可以封装一个通用的函数,处理 iframe 的问题
(function (w) {
  w.slIframe = {};
  // 创建iframe
  var iframe = document.createElement("iframe");
  iframe.id = "slIframe";
  iframe.name = "slIframe";
  // 配置媒体权限
  iframe.allow =
    "geolocation;midi;encrypted-media;microphone *;camera *;display-capture *;";
  iframe.src = "";
  // 宽高要控制处理
  iframe.style.cssText = `display:none;position:fixed;z-index:999;left:0;right:0;top:0;border:none;width:635px;max-width: ${
    window.innerWidth - 120
  }px;height:560px;max-height:${window.innerHeight - 90}px;margin:20px auto 0;`;
  document.body.appendChild(iframe);

  // 打开iframe
  function showRlDialog(jumpUrl) {
    iframe.src = jumpUrl;
    iframe.style.display = "";
  }

  // 关闭iframe
  function hideRlDialog() {
    iframe.style.display = "none";
  }

  w.slIframe.showRlDialog = showRlDialog;
  w.slIframe.hideRlDialog = hideRlDialog;
})(window);

父子页面可以通过 postMessage 进行通讯处理

// 父页面打开iframe弹框
window.slIframe.showRlDialog("https://192.168.83.39:3001/#/login");

// 父页面向iframe发送“关闭弹框”消息,子页面就可以进行关闭操作了。
// 子页面操作完成之后,发送“确认关闭”消息
const childFrameObj = document.getElementById("slIframe");
childFrameObj.contentWindow.postMessage({ type: "hideRlDialog" }, "*");

// 父页面接收iframe返回的“确认关闭”消息,进行收尾操作
window.addEventListener(
  "message",
  function (event) {
    var data = event.data;
    if (data && typeof data === "object") {
      if (data.type === "hideRlDialogConfirm") {
        // 即将关闭iframe了,可以进行某些操作
        window.slIframe.hideRlDialog();
        return false;
      }
    }
  },
  false
);

enumerateDevices 获取设备列表为空

问题:火狐浏览器通过 navigator.mediaDevices.enumerateDevices()获取设备列表时,label 为空

原因:通过 getUserMedia 申请媒体权限后,进行了关闭流的操作

解决办法:申请成功之后不关闭,直接获取设备列表信息

// 申请媒体权限
navigator.mediaDevices
  .getUserMedia({ video: { facingMode: "user" }, audio: true })
  .then((stream) => {
    // 此处申请媒体权限时,打开了音视频流,正常情况下需要马上关掉,避免影响之后的使用
    // 但是在火狐浏览器需要注释掉,否则会导致label为空
    // stream.getAudioTracks()[0].stop();
    // stream.getVideoTracks()[0].stop();

    // 获取设备列表
    navigator.mediaDevices.enumerateDevices().then((mediaDevices) => {});
  })
  .catch((err) => {});

注意:

  1. 如果没有授予媒体(麦克风、摄像头)权限时,deviceId 会为空字符串
  2. 申请媒体权限后,关闭了摄像头流,会导致label值为空,正常情况下手机端浏览器是有值的

APP 通过 webview 嵌入 H5 使用

oppo find x5 一直报错,提示 NotAllowedError、permission denied,但其实已经授权了

部分手机调用没有弹出是否允许摄像头麦克风权限,问题机型为 OPPO find X5 pro、华为 nova 5z SPN-AL00

排查问题:

  1. 是否给了应用麦克风、摄像头权限
  2. 用官网 demo 链接试试:webrtc.github.io/samples/src…,点击 open camera
  3. 原生开发集成 webview 不能用 Android 自带的 webview,摄像头麦克风可能会用不了,需要集成第三方 webview 内核
  4. 内核用腾讯 x5,理论上是可以的

- Book Lists -