WebRTC 之媒体 getUserMedia 使用限制及解决
·
Yin灏
http 访问处理
理论上来说,媒体 getUserMedia
是不支持 http
访问,必须要用 https
协议,而且有合法的域名
。
但是有些情况下,必须要在 http
中访问,那就需要对浏览器进行特殊配置,让浏览器支持。
如谷歌浏览器
中,手动指定某些域名安全,如下图所示配置:
chrome://flags/#unsafely-treat-insecure-origin-as-secure
ip 访问处理
在开发测试阶段时,如果没有申请好域名时,可以用 IP 地址进行临时访问。
但是第一次访问时,需要添加安全例外
,进行授信处理。
之后每次清除缓存后重新访问,也需要处理,添加安全例外
。
申请授权处理
初次访问网站使用 getUserMedia
,会弹出禁止允许的授权弹框
,选择了之后,浏览器会记住用户选择
。
下次再请求时,就不用弹出授权弹框了,直接用之前用户的选择。如下图所示:
注意:如果要再次弹出授权框的话,需要清除浏览器缓存中的记录
在 iframe 中的使用
- 在 iframe 中使用媒体功能,需要进行如下配置
iframe.allow = "midi;encrypted-media;microphone *;camera *;";
- 在 iframe 中使用屏幕共享功能,需要进行如下配置:
iframe.allow = "display-capture *;";
- 可以封装一个通用的函数,处理 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) => {});
注意:
- 如果没有授予媒体(麦克风、摄像头)权限时,
deviceId
会为空字符串 - 申请媒体权限后,关闭了摄像头流,会导致
label
值为空,正常情况下手机端浏览器是有值的
APP 通过 webview 嵌入 H5 使用
oppo find x5 一直报错,提示 NotAllowedError、permission denied,但其实已经授权了
部分手机调用没有弹出是否允许摄像头麦克风权限,问题机型为 OPPO find X5 pro、华为 nova 5z SPN-AL00
排查问题:
- 是否给了应用麦克风、摄像头权限
- 用官网 demo 链接试试:webrtc.github.io/samples/src…,点击 open camera
- 原生开发集成 webview 不能用 Android 自带的 webview,摄像头麦克风可能会用不了,需要集成第三方 webview 内核
- 内核用腾讯 x5,理论上是可以的