跳到主要内容

浏览器兼容矩阵

浏览器兼容矩阵

WebRTC 在 spec 上看起来一统江湖,落到具体浏览器版本就是另一回事。同一段代码在 Chrome 跑得好的,Safari 可能 codec 不通,Firefox 可能 stats 字段缺失,Android WebView 可能行为更接近上一代 Chromium。本篇给出 2026-06 的实际矩阵和处理策略。

1. 为什么要单独维护一份矩阵

  • W3C spec 与浏览器实际实现存在 6~18 个月的滞后窗口。
  • Safari 与 Chromium 的实现节奏不同,重大特性(VP9、AV1、WebCodecs、Insertable Streams)落地差异大。
  • Android WebView 与 Chrome 看似同源,实际版本受厂商系统更新节奏制约,不能假设它等同于最新 Chrome
  • iOS WKWebView 不是 Safari,但共享 WebKit 内核,bug 表现与版本对齐策略不完全一致。

什么时候适用:在做能力探测、SDK 兼容、客户端版本下限决策时翻这份表。 什么时候不适用:业务代码里不要直接 if (isSafari) 这种 UA 判定,应该用特性检测(RTCRtpSender.getCapabilities / RTCPeerConnection.generateCertificate 等)。

2. 版本下限建议(生产)

浏览器生产最低版本理由
Chrome / Edge(Chromium)100+TWCC、统一 Plan B 已彻底移除,WebCodecs 稳定
Firefox115+(ESR)Unified Plan、Simulcast 稳定
Safari macOS16+VP9 解码、WebRTC 1.0 完整
Safari iOS15.4+getDisplayMedia 部分支持,PiP 改善
iOS WKWebViewiOS 14.3+WebRTC 才进入 WKWebView,14.3 之前必须原生 SDK
Android WebView90+等同于 Chromium 90,许多老设备卡在 80

低于上面版本的,要么走降级(H.264-only、关闭 Simulcast),要么直接拒绝接入。

3. 全特性矩阵

下表是写业务前必看的速查表。 表示生产可用,⚠️ 表示有坑或部分可用, 表示不可用。

3.1 视频 Codec

CodecChrome / EdgeFirefoxSafari macOSSafari iOSAndroid WebViewiOS WKWebView
H.264 (Constrained Baseline)✅ 软+硬✅ 软+硬✅ 硬解优先✅ 硬✅ 硬✅ 硬
H.264 High Profile⚠️ 编码器限制⚠️⚠️
VP8✅ 14.3+✅ 14.3+
VP9✅ 16+ 解码✅ 16+ 解码(部分机型)⚠️ 仅解码
AV1✅ 90+ 解码,113+ 硬编✅ 解码,编码受限⚠️ 17+ 解码⚠️ 17+ 解码(A17 起硬解)⚠️ 部分机型⚠️
H.265 / HEVC⚠️ 107+ 桌面解码⚠️ 厂商相关

最大公约数永远是 H.264 Constrained Baseline。要做跨 H5/iOS/Android/小程序 互通,SDP 里必须给 H.264 一个明确 payload。

3.2 音频 Codec

CodecChromeFirefoxSafariiOS WKWebView
Opus
Opus DTX / FEC⚠️ FEC 默认关闭⚠️
Opus stereo⚠️
Opus RED (RFC 2198)✅ 105+
G.711 (PCMU/PCMA)
L16 / 16k 采样⚠️⚠️

3.3 PeerConnection 与协商

特性ChromeFirefoxSafariiOS WKWebView
Unified Plan(默认)
Plan B(已废弃)❌ 96+ 移除
RTCRtpTransceiver✅ 14+✅ 14.3+
addTransceiver direction
Trickle ICE
ICE Restart
Bundle Policy max-bundle
RTCDtlsTransport✅ 14+✅ 14.3+
RTCSctpTransport
RTCDataChannel

3.4 Simulcast 与 SVC

特性ChromeFirefoxSafariiOS WKWebView
Simulcast (VP8/H.264)✅ 13+(H.264 only 早期)
Simulcast (VP9)⚠️ 实现差异⚠️ 16+⚠️
SVC (VP9 / AV1)⚠️⚠️ 17+⚠️
setParameters 动态调整
scaleResolutionDownBy
maxBitrate

3.5 拥塞与传输

特性ChromeFirefoxSafariiOS WKWebView
TWCC transport-cc✅ 110+ 稳定✅ 14+
REMB(旧)⚠️ 仅作为兜底⚠️⚠️⚠️
ABS-Send-Time
RTX (重传)
FlexFEC✅(默认关)
ULPFEC⚠️⚠️
Opus FEC / inband-FEC⚠️ 默认关⚠️
QUIC datagram (RTC over QUIC)⚠️ Origin Trial

3.6 采集与设备

特性ChromeFirefoxSafariiOS WKWebView
getUserMedia
getDisplayMedia✅ macOS❌ iOS
屏幕系统声音采集✅(用户勾选)⚠️
enumerateDevices 完整 label授权后授权后授权后授权后
applyConstraints 切 facingMode⚠️ 部分版本失效⚠️ 直接重新 getUserMedia
selectAudioOutput
setSinkId 选扬声器✅ 116+
BackgroundBlur 原生⚠️ 实验

3.7 高阶 API

特性ChromeFirefoxSafariiOS WKWebView
Insertable Streams (E2EE)✅ 94+⚠️ 116+ 部分⚠️ 17+⚠️
WebCodecs✅ 130+✅ 16.4+
WebTransport⚠️ 114+
WHIP / WHEP(HTTP 标准化)✅ 应用层✅ 应用层✅ 应用层✅ 应用层
RTCRtpScriptTransform⚠️ Origin Trial
RTCRtpSender.getStats 完整⚠️ 字段少⚠️
Picture-in-Picture⚠️ iOS 限制多
requestVideoFrameCallback✅ 122+✅ 15.4+

4. 关键差异详解

4.1 Safari 的 codec 解锁节奏

  • iOS 14.3 之前 WebRTC 完全不可用,包括 WKWebView。
  • iOS 14.3 ~ 15:H.264 + VP8 编解码。
  • iOS 16+:VP9 解码,但编码仍然只有 H.264 / VP8
  • iOS 17+:AV1 解码(A17 Pro 起硬解,老机型软解)。
  • 任何时候 Safari 主动发布的视频都优先 H.264

结论:要让 Safari 既能发又能收,SDP 里必须有 H.264。offerToReceiveVideo 包含 VP9/AV1 没问题,但 Safari 这一端发的还是 H.264。

4.2 Firefox 与 Chrome 的 stats 字段差异

RTCStatsReport 在两家实现的 type 名一致,但字段细节差很多:

interface RtcStatsCommon {
bytesSent?: number;
bytesReceived?: number;
packetsLost?: number;
jitter?: number;
// Firefox 没有 totalRoundTripTime,只有 roundTripTime
// Chrome 105+ 才有 jitterBufferDelay
}

写监控时要包一层适配:

function readRtt(stats: RTCStatsReport): number | undefined {
for (const report of stats.values()) {
if (report.type === 'remote-inbound-rtp') {
return report.roundTripTime; // Chrome / Firefox 都有
}
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
return report.currentRoundTripTime; // Chrome 主用这个
}
}
return undefined;
}

4.3 Android WebView 的版本碎片

Android WebView 由系统组件 com.google.android.webview 提供,理论上随 Chrome 同步,但:

  • 国产 ROM(MIUI / EMUI / ColorOS)可能锁定某个旧版本不更新。
  • 鸿蒙 NEXT 不带 GMS,WebView 由厂商自己提供,行为偏离 Chromium。
  • 企业 MDM 设备会锁版本到上线时的 baseline。

生产应对

async function detectWebRTCSupport(): Promise<{
supported: boolean;
reason?: string;
chromiumVersion?: number;
}> {
if (!window.RTCPeerConnection) {
return { supported: false, reason: 'no RTCPeerConnection' };
}
const ua = navigator.userAgent;
const chrome = ua.match(/Chrome\/(\d+)/);
const version = chrome ? parseInt(chrome[1], 10) : 0;

if (version > 0 && version < 90) {
return { supported: false, reason: 'chromium too old', chromiumVersion: version };
}

// 真正能跑 simulcast 还要进一步看
const caps = RTCRtpSender.getCapabilities?.('video');
if (!caps) return { supported: false, reason: 'no getCapabilities' };

return { supported: true, chromiumVersion: version };
}

4.4 WKWebView 与 Safari 的细微差异

虽然共享 WebKit,但有以下不同:

  • 自动播放策略更严:必须 playsinline + muted,再加上用户手势触发,远端音频才能自动出声。
  • 后台挂起更激进:App 切到后台 ~30s 后页面进入 frozen 状态,PeerConnection 通常断。
  • getUserMedia 权限:需要在 Info.plist 加 NSCameraUsageDescriptionNSMicrophoneUsageDescription,否则直接拒绝。
  • RTCAudioSession 冲突:原生侧若启用了 RTCAudioSession,再让 WKWebView 拉 getUserMedia 会争抢音频通道。

详见 iOS-WKWebView差异.md

4.5 Edge 的"Chromium 化"残留

新 Edge 自 2020 起完全基于 Chromium,行为和 Chrome 一致;旧 EdgeHTML 早就退役,不必再考虑。 注意:企业 IE Mode(Internet Explorer 模式)的页面不能用 WebRTC——业务页面进 IE Mode 一律拒绝接入。

5. 能力探测代码

5.1 通用 codec 探测

function getSupportedVideoCodecs(): string[] {
const caps = RTCRtpSender.getCapabilities('video');
if (!caps) return [];
return caps.codecs
.filter(c => !c.mimeType.includes('rtx') && !c.mimeType.includes('red'))
.map(c => c.mimeType.toLowerCase().replace('video/', ''));
}

function preferCodec(transceiver: RTCRtpTransceiver, codec: 'h264' | 'vp9' | 'av1') {
const caps = RTCRtpSender.getCapabilities('video');
if (!caps) return;
const ordered = [
...caps.codecs.filter(c => c.mimeType.toLowerCase().includes(codec)),
...caps.codecs.filter(c => !c.mimeType.toLowerCase().includes(codec)),
];
transceiver.setCodecPreferences(ordered);
}

5.2 浏览器 + WebView 真值矩阵

type Runtime =
| 'chrome' | 'edge' | 'firefox'
| 'safari' | 'wkwebview'
| 'android-webview' | 'unknown';

export function detectRuntime(): Runtime {
const ua = navigator.userAgent;
// iOS WKWebView: 没有 Safari/ 标识但是 AppleWebKit
if (/iPhone|iPad|iPod/.test(ua) && !/Safari\//.test(ua)) return 'wkwebview';
if (/Edg\//.test(ua)) return 'edge';
if (/Chrome\//.test(ua) && /; wv\)/.test(ua)) return 'android-webview';
if (/Chrome\//.test(ua)) return 'chrome';
if (/Firefox\//.test(ua)) return 'firefox';
if (/Safari\//.test(ua)) return 'safari';
return 'unknown';
}

UA 不是用来做能力判断的——它只用来打日志、做兼容白名单的辅助。

6. 失败时的样子

现象大概原因排查
Safari 收得到 Chrome,反向无图像Chrome 端 SDP 没列 H.264setCodecPreferences 强制把 H.264 提到第一
Firefox 互通后 simulcast 只剩一层Firefox 早期版本对 a=simulcast 解析问题升级 Firefox 或退化为单层
iOS Safari 黑屏,但 Android Chrome 正常&lt;video>playsinline&lt;video playsinline muted autoplay>
Android WebView 卡顿软件渲染兜底&lt;meta name="theme-color">、确认硬解、降分辨率
Edge 公司账号下白屏落到 IE Mode把域名加入站点策略黑名单(避免 IE Mode)
iOS 14.3 以下完全不工作没有 WebRTC强制升级或走原生 SDK

7. 反模式

反模式后果
用 UA 判定来开关能力国产浏览器 UA 各种伪装,结果误判
假设所有 Chromium 行为一致Android WebView 旧版本会爆出 bug
SDP 里只放 VP9/AV1 不放 H.264Safari、iOS、Android 互通失败
写死 RTCRtpSender.getCapabilities 不存在分支抛错老 WebView 直接白屏
applyConstraints 切 facingMode 不写降级iOS Safari 部分版本无效,黑屏
只测最新 Chrome 上线iOS 用户进来基本必坏
通过硬编码版本号判断特性Safari 17.4 修了某 bug,业务还在拒绝 17.x

8. 跨浏览器最小可行配置

const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
bundlePolicy: 'max-bundle',
rtcpMuxPolicy: 'require',
iceTransportPolicy: 'all',
});

const sender = pc.addTransceiver('video', {
direction: 'sendrecv',
sendEncodings: [
{ rid: 'h', maxBitrate: 1_500_000 },
{ rid: 'm', maxBitrate: 600_000, scaleResolutionDownBy: 2 },
{ rid: 'l', maxBitrate: 200_000, scaleResolutionDownBy: 4 },
],
});

// 强制 H.264 优先(保证跨浏览器)
const caps = RTCRtpSender.getCapabilities('video');
if (caps) {
const ordered = [
...caps.codecs.filter(c => c.mimeType === 'video/H264'),
...caps.codecs.filter(c => c.mimeType !== 'video/H264'),
];
sender.setCodecPreferences(ordered);
}

这套配置在 Chrome / Edge / Firefox / Safari 16+ / iOS 14.3+ 都能稳定建连。

9. 权威资料