WebRTC在浏览器中失败主因非代码错误,而是信令未通、STUN未配或运行于非安全上下文(仅localhost/https允许getUserMedia与RTCPeerConnection)。

WebRTC 在浏览器里跑不起来,基本不是代码写错了,而是信令没通、STUN 没配、或者根本不在安全上下文里——getUserMedia 和 RTCPeerConnection 在 http:// 下直接拒绝工作,localhost 或 https 是硬门槛。
为什么本地 localhost 能通,一部署就黑屏?
核心原因是 WebRTC 对运行环境有强制安全约束:
-
getUserMedia、RTCPeerConnection必须在安全上下文(https://或localhost)中调用,http://xxx.com会静默失败或抛NotAllowedError - Chrome 117+ 起,非安全上下文连摄像头图标都不显示,更不会触发权限弹窗
- 某些 Android WebView(尤其旧版系统)不支持
RTCRtpSender.replaceTrack(),也不完整支持 H.264,需提前检测getCapabilities('video')并 fallback 到 VP8 - 自动播放策略会拦截未绑定用户手势的
video.play(),即使srcObject已设好,也大概率静音或暂停
RTCPeerConnection 构造时必须传 iceServers
不配 STUN,onicecandidate 基本收不到有效候选,ICE 收集直接卡在 gathering 状态,连接永远停在 checking。
- 至少加一个公共 STUN:{ urls: "stun:stun.l.google.com:19302" } —— 这是最低可用配置
- 如果双方都在对称 NAT 后(如企业网络、部分 4G),STUN 返回的
srflx候选不可达,必须配 TURN 服务器(如 Coturn),并传入username和credential -
iceTransportPolicy: "relay"可强制只走中继,适合调试连通性,但会牺牲延迟和带宽 - 检查是否真生效:
pc.getConfiguration().iceServers应返回你传入的数组,有些封装库(如 simple-peer)会覆盖原始配置
信令顺序错一帧,ICE 就卡死
WebRTC 不关心你怎么传信令,但严格依赖 JSEP 流程时序。offer/answer/candidate 发送顺序、时机、完整性,任一环节出问题,连接就无法推进。
立即学习“前端免费学习笔记(深入)”;
- A 发
createOffer()→ setLocalDescription → 发 offer 给 B;B 收到后必须先setRemoteDescription(offer),再createAnswer(),不能反着来 - B 的 answer 必须等 A 的所有 ICE candidate 都发完再发?不,但 B 必须在
setRemoteDescription后立即开始收集自己的 candidate,并尽快发出;A 收到 answer 后也得立刻setRemoteDescription -
addIceCandidate()调用前,确保远端已执行setRemoteDescription,否则会报InvalidStateError - candidate 事件可能触发多次,每次都要单独发送,不能合并或节流;丢一个,连接大概率失败
怎么确认真走了点对点?
别信控制台日志里写的 “connected”,看实际路径:
- 打开
chrome://webrtc-internals,查remote-candidate-type字段:出现host或srflx才代表有 P2P 成分;全是relay就说明全程走 TURN - 在
pc.onicecandidate里打日志:e.candidate?.type,观察生成的 candidate 类型分布 -
pc.getStats()中找candidate-pair,看state是否为succeeded,以及transportId对应的dtlsState是否为connected - Firefox 开发者工具 → 网络面板 → 过滤
webrtc,能看到真实传输协议(UDP/TCP)和目标地址
真正卡住的地方往往不是 API 调用本身,而是信令通道的可靠性、ICE 候选的完整送达、以及 STUN/TURN 服务器的实际可达性——这些没法靠 console.log 排查,得进 chrome://webrtc-internals 看实时统计,或抓包验证 UDP 是否被防火墙拦截。



















