仪表盘嵌摄像头需区分本地设备与远端IP摄像头:本地用 getUserMedia({video:true, audio:false}) + video.srcObject + autoplay/muted/playsinline;远端需HTTP-FLV/HLS/MJPEG协议,禁用RTSP直连;移动端 facingMode 不可靠,应 enumerateDevices 动态选型;微信X5内核需降级 file capture;务必停流、限分辨率、强依赖HTTPS。

仪表盘里嵌摄像头,不是“加个标签就行”,而是要分清场景:是调用用户本地设备(如访客手机前置),还是接入远端IP摄像头(如机房里的海康球机)。两者技术路径完全不同,混用会导致白屏、404或权限拒绝。
本地摄像头用 getUserMedia(),但必须处理 autoplay 和 muted
直接写 navigator.mediaDevices.getUserMedia({ video: true }) 在仪表盘里大概率黑屏——尤其 iOS Safari 和部分安卓 WebView。根本原因是浏览器自动播放策略:带音轨的流默认被静音拦截,而 getUserMedia() 默认启用音频轨道(哪怕你没要麦克风)。
- 务必显式关闭音频:
{ video: true, audio: false } -
<video></video>标签必须同时带autoplay和muted属性,iOS 还得加playsinline - 别用
video.src = URL.createObjectURL(stream),改用video.srcObject = stream(后者不触发内存泄漏,且兼容性更好) - 错误没捕获?控制台可能只报
NotAllowedError或静默失败。建议在catch里判断err.name,比如NotAllowedError就提示用户手动点地址栏锁图标授予权限
IP 摄像头用
RTSP 地址(如 rtsp://admin:12345@192.168.1.100:554/stream1)不能直接塞进 <video src="..."></video>,浏览器根本不认。必须确认摄像头实际输出的是 Web 友好协议。
- MJPEG 流最省事:
http://192.168.1.100/videostream.cgi?user=admin&pwd=12345,所有浏览器都支持,但带宽高、延迟大 - HLS(
.m3u8)适合 iOS/Safari,但需摄像头固件支持;注意跨域:如果仪表盘域名和摄像头 IP 不同源,需后端代理或摄像头开启 CORS - HTTP-FLV 延迟低,但仅 Chrome/Edge 支持,Firefox 和 Safari 需额外封装(如 flv.js)
- 别把流地址硬编码进 JS —— 仪表盘通常多设备共用,应从配置项或 API 动态加载
移动端前后置切换不能只靠 facingMode
写 { video: { facingMode: "user" } } 想切前置,在 iOS 上基本无效。Safari 忽略 facingMode 约束,只按系统默认走;部分低端安卓机型也完全不支持该参数。
立即学习“前端免费学习笔记(深入)”;
- 稳妥做法:先尝试
facingMode: "user",catch到错误后 fallback 到空对象{ video: true } - 真要可靠切换?得用
navigator.mediaDevices.enumerateDevices()拿到设备列表,再根据label或deviceId显式指定(但注意:该 API 需用户已授权,首次访问时拿不到真实设备) - 微信内置浏览器(X5 内核)压根不支持
getUserMedia(),必须降级到<input type="file" accept="image/*" capture="user">,它调起的是原生相机 App,不走 WebRTC
仪表盘性能和稳定性容易被忽略的点
多个摄像头同时跑在同一个仪表盘页上,很容易卡顿或崩溃,尤其是低端安卓平板或旧版 iPad。这不是代码写得不好,而是浏览器媒体资源管理本身有瓶颈。
- 不用时立刻停掉流:
stream.getTracks().forEach(track => track.stop()),否则即使<video></video>元素被移除,摄像头仍常驻开启 - 分辨率别盲目设高:仪表盘通常不需要 1080p,
{ width: { ideal: 640 }, height: { ideal: 480 } }足够清晰,还能降低编解码压力 - 别依赖
video.videoWidth判断是否加载成功——它可能为 0 即使画面已出。更稳的方式是监听loadeddata事件 - HTTPS 是硬门槛:HTTP 页面下
getUserMedia()直接被拒,连错误都不抛;开发时用python3 -m http.server 8000启服务,别用 file:// 协议打开 HTML



















