18、浏览器兼容性调试:在不同浏览器(Chrome/Firefox/Safari)中对比 webrtc-internals 输出差异

做 WebRTC 开发,最头疼的问题之一就是:Chrome 上跑得好好的,换到 Safari 就黑屏了

我遇到过不止一次。有一次线上会议系统,用户反馈说「我用 Mac 的 Safari 看不到对方画面」,我本地 Chrome 测了 N 遍都没问题。最后打开 webrtc-internals 一对比,才发现是 rtcp-mux 协商出了问题。

所以这一章,咱们就来聊聊怎么用 webrtc-internals 这把「手术刀」,解剖不同浏览器下的信令和媒体流差异。

18.1 为什么浏览器之间会有差异?

说白了,WebRTC 是一套规范,但各家浏览器实现细节不一样。Chrome 用 libwebrtc,Firefox 有自己的实现,Safari 更是「特立独行」。你想想看,ICE 候选人的优先级算法、SDP 的格式、甚至统计信息的字段名,都可能不同。

我个人习惯是:先统一场景,再对比差异。比如同一个房间、同一个网络环境,分别用三个浏览器打开,然后导出 webrtc-internals 的日志。

核心原则: 不要假设「Chrome 的行为就是标准」。Safari 和 Firefox 在某些场景下可能更严格。

18.2 打开 webrtc-internals 的正确姿势

每个浏览器打开调试工具的方式略有不同,我列个表给你参考:

浏览器 打开方式 注意事项
Chrome 地址栏输入 chrome://webrtc-internals 支持实时刷新,数据最全
Firefox 地址栏输入 about:webrtc 界面风格不同,但核心信息都有
Safari 开发者菜单 → WebRTC → 勾选「启用日志」 需要先开启「开发」菜单,数据导出较麻烦
小技巧: 我建议你在测试前,先清空浏览器缓存和所有站点数据。否则历史连接信息会干扰当前会话的统计。

18.3 对比三大关键模块

我们不可能把几千行日志全看一遍。我一般只盯三个地方:SDP 协商ICE 连接RTP 统计

18.3.1 SDP 协商差异

这是最常出问题的地方。Chrome 和 Firefox 在生成 SDP 时,a=ice-optionsa=extmap 的排列顺序可能不同。Safari 则可能缺少某些扩展头。

举个例子,我曾经在 Safari 上发现它没有发送 a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id,导致接收端无法关联音视频流。

// Chrome 的 SDP 片段(正常)
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id

// Safari 的 SDP 片段(缺少 extmap:3)
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
注意: 如果 Safari 不发送 rtp-stream-id,接收端可能无法正确将 RTP 包映射到对应的 MediaStreamTrack。这时候你需要手动在接收端做 fallback 处理。

18.3.2 ICE 候选人差异

Firefox 和 Chrome 在 ICE 候选人优先级计算上略有不同。Firefox 更倾向于使用 host 类型的候选人,而 Chrome 会尝试更多 srflxrelay 候选人。

我记得有一次,用户反馈「Firefox 连不上,Chrome 可以」。我对比了 webrtc-internals 的 ICE 状态,发现 Firefox 的 srflx 候选人一直处于 checking 状态,而 Chrome 已经切换到 relay 成功了。

// Chrome ICE 候选人列表(部分)
candidate:1 1 UDP 2122252543 192.168.1.10 54321 typ host
candidate:2 1 UDP 1686052607 203.0.113.5 54322 typ srflx
candidate:3 1 UDP 41819903 203.0.113.5 54323 typ relay

// Firefox ICE 候选人列表(部分)
candidate:0 1 UDP 2130706431 192.168.1.10 54321 typ host
candidate:1 1 UDP 1694498815 203.0.113.5 54322 typ srflx
// 注意:Firefox 的 relay 候选人优先级较低,可能不会主动尝试
避坑指南: 我曾经遇到 Firefox 在 NAT 穿透时,srflx 候选人一直失败,但又不切换到 relay。后来发现是 TURN 服务器的 transport 字段没配好。Firefox 对 TURN 的 UDP/TCP 协商更严格。

18.3.3 RTP 统计信息差异

webrtc-internals 里的 googTrackIdgoogFrameRateReceived 这些字段,其实是 Chrome 特有的。Firefox 和 Safari 的统计字段名完全不同。

比如,Chrome 用 googRtt 表示往返时延,Firefox 用 roundTripTime,Safari 则可能直接不暴露这个字段。你想想看,如果你写了一个自动化测试脚本,只解析 Chrome 的字段,那在 Safari 上肯定报错。

统计指标 Chrome 字段名 Firefox 字段名 Safari 字段名
往返时延 (RTT) googRtt roundTripTime 可能缺失
丢包率 googPacketsLost packetsLost packetsLost
帧率 googFrameRateReceived frameRate frameRate
码率 googFrameWidthReceived frameWidth frameWidth

18.4 实战:三步对比法

我总结了一套「三步对比法」,你可以直接套用:

  1. 导出日志:在三个浏览器中分别打开 webrtc-internals,建立相同的 PeerConnection,然后点击「Export」或「Save」保存为 JSON 文件。
  2. 对齐时间轴:用文本编辑器打开 JSON,找到 startTimetimestamp 字段,把三个文件的时间轴对齐到同一个起点。
  3. 逐项对比:重点关注 SDP 中的 a=extmap、ICE 候选人列表、以及 googRtt / packetsLost 等关键统计。
我的经验: 如果三个浏览器的 SDP 中 a=extmap 数量不一致,大概率是 Safari 或 Firefox 不支持某些 RTP 头部扩展。这时候你需要在 RTCRtpSenderRTCRtpReceiver 中手动设置 degradationPreferenceheaderExtensions

18.5 核心知识体系

下面这张图,帮你理清本章的核心逻辑:

浏览器兼容性调试核心流程 1. 统一测试场景 相同房间、相同网络 2. 导出 webrtc-internals Chrome/Firefox/Safari 3. 对比三大模块 SDP / ICE / RTP SDP 协商差异 • extmap 扩展头缺失 • rtcp-mux 协商失败 • 编解码器优先级不同 ICE 候选人差异 • 候选人优先级不同 • srflx 切换 relay 策略 • TURN 传输协议偏好 RTP 统计字段差异 • 字段命名完全不同 • googRtt vs roundTripTime • 部分字段可能缺失 结论:不要假设浏览器行为一致 用 webrtc-internals 数据说话,逐项对比定位问题

18.6 常见问题与避坑

最后,分享几个我踩过的坑:

  • Safari 的 webrtc-internals 没有「Export」按钮:你需要手动复制日志窗口的内容,或者用 console.log 输出 RTCPeerConnection.getStats() 的结果。
  • Firefox 的 about:webrtc 页面不会自动刷新:每次建立新连接后,需要手动刷新页面才能看到最新数据。我一开始没注意,浪费了半天时间。
  • Chrome 的 webrtc-internals 数据量太大:如果连接时间很长,导出的 JSON 可能几十 MB。建议只保留关键时间段的日志,或者用脚本过滤掉无关的 ssrc 条目。
重要提醒: 不要在生产环境长时间开启 webrtc-internals。它会持续收集统计信息,占用内存和 CPU。我建议只在调试阶段开启,调试完立即关闭。

嗯,这一章的内容就到这里。记住,浏览器兼容性调试没有银弹,但 webrtc-internals 是你最可靠的「照妖镜」。多对比、多记录,慢慢你就能一眼看出问题出在哪个环节。


公众号:蓝海资料掘金营,微信deep3321