资讯动态

WebRTC M66-M88 版本演进与兼容性适配实战指南

发布时间:2026/10/17 8:08:44 来源:尧图企业网站定制
1. 从 M66 到 M88WebRTC 到底经历了什么如果你在 2018 年到 2020 年之间维护过基于 WebRTC 的实时音视频项目大概率会有这么一种体验某天早上打开电脑发现昨天还跑得好好的通话功能今天在某个浏览器上就莫名其妙地黑屏了。排查半天代码一行没改最后发现是浏览器内核悄悄升级了WebRTC 的某个 API 行为变了。M66 到 M88 这个区间恰好是 WebRTC 从能用走向好用的关键阶段。这个阶段里浏览器厂商对 WebRTC 的推进力度非常大几乎每个大版本都会带来一些破坏性变更或者重要的新能力。对于做实时音视频、在线教育、视频会议、屏幕共享这类产品的开发者来说搞清楚这段时间里 WebRTC 发生了什么直接决定了你的兼容性策略怎么写、降级方案怎么做。这篇文章面向的是有一定 WebRTC 基础的开发者比如你已经写过RTCPeerConnection、处理过 SDP 协商、对接过信令服务器。如果你刚接触 WebRTC也能看懂因为我会把每个变更背后的为什么讲清楚。核心关键词就一个WebRTC 版本演进与兼容性适配。我会把 M66 到 M88 之间那些真正影响业务的关键更新挑出来逐个拆解它们解决了什么问题、带来了什么新问题、实际项目中该怎么应对。先说结论性的判断这个区间里最重要的三条主线是编解码器策略的调整、API 的标准化与废弃、以及安全与隐私要求的收紧。下面我按这三条线展开中间穿插具体的版本号和实操建议。2. 编解码器策略VP8、VP9、H.264 的三角关系2.1 为什么编解码器选择在 M66-M88 期间变得复杂WebRTC 最早默认只支持 VP8后来逐步加入 VP9 和 H.264。到了 M66 前后浏览器开始调整默认的编解码器优先级这直接影响到 SDP 里mvideo行的rtpmap顺序。很多开发者发现自己明明在RTCRtpTransceiver里设置了setCodecPreferences但实际协商出来的结果和预期不一致。原因在于这个阶段浏览器对编解码器的默认排序逻辑改了好几次。M66 附近VP8 仍然是默认首选但 VP9 的支持已经比较稳定。到了 M70 之后部分场景下 H.264 的硬件加速优势开始被重视尤其是在移动端。M88 前后AV1 开始进入实验阶段但还没有成为默认选项。这里的关键点是你不能假设浏览器的默认编解码器顺序是固定的。如果你的业务对编解码器有硬性要求比如必须用 H.264 来对接某些传统设备那必须在 SDP 层面做显式控制而不是依赖默认行为。2.2 setCodecPreferences 的正确用法与坑RTCRtpTransceiver.setCodecPreferences()是控制编解码器顺序的标准方法。它的用法看起来很简单const transceiver pc.addTransceiver(video, { direction: sendrecv }); const codecs RTCRtpSender.getCapabilities(video).codecs; // 把 H.264 排到最前面 const h264 codecs.filter(c c.mimeType video/H264); const others codecs.filter(c c.mimeType ! video/H264); transceiver.setCodecPreferences([...h264, ...others]);但实际用起来有几个坑。第一getCapabilities返回的列表在不同浏览器、不同版本里顺序不一样你不能直接拿这个列表去设置必须先做筛选和重排。第二某些浏览器在 M66-M88 期间对setCodecPreferences的支持是逐步完善的早期版本可能只支持部分编解码器传入了不支持的项会直接抛异常。第三如果你在setRemoteDescription之后再调用这个方法可能不会生效必须在创建 transceiver 之后、协商之前调用。我踩过的一个坑是在 M72 附近的某个版本里如果setCodecPreferences传入的列表里包含了浏览器不认识的 codec整个调用会静默失败不报错但也不生效。排查了很久才发现是列表里混入了一个实验性的编解码器。所以建议在调用前先做一次过滤只保留getCapabilities里确实存在的项。2.3 各版本编解码器支持对照下面这张表是我根据实际测试和文档整理的关键版本编解码器支持情况供你快速查阅版本区间VP8VP9H.264AV1默认首选M66-M69稳定稳定稳定不支持VP8M70-M75稳定稳定硬件加速优化实验性VP8M76-M80稳定稳定稳定实验性VP8/VP9M81-M88稳定稳定稳定实验性VP8/VP9/H.264注意这张表是趋势性总结具体到某个小版本可能有细微差异。生产环境一定要以实际测试为准不要直接照搬。从这张表能看出来VP8 在整个区间里都是最稳的选择。如果你的业务不需要特别高的压缩效率VP8 是最省心的。VP9 在 M70 之后逐渐成熟适合对带宽敏感的场景。H.264 的优势在于硬件编解码支持好移动端功耗低但专利授权问题需要你自己评估。3. API 标准化与废弃那些年我们追过的接口3.1 Plan B 到 Unified Plan 的迁移这是 M66-M88 期间对开发者影响最大的一次变更没有之一。早期的 WebRTC 实现里浏览器厂商分成了两派一派用 Plan B一派用 Unified Plan。Plan B 的特点是所有媒体流都塞在一个m行里SDP 结构简单但不符合标准Unified Plan 是标准方案每个 track 对应一个m行。M66 附近浏览器开始默认启用 Unified Plan但还保留了 Plan B 的兼容开关。到了 M72 左右Plan B 的支持开始被移除。这意味着如果你的代码是按 Plan B 的 SDP 结构来解析的升级后直接崩掉。迁移的核心改动在于 SDP 的处理逻辑。Plan B 下你可能习惯从单个m行里解析多个 SSRCUnified Plan 下每个 track 有独立的m行和独立的mid。信令服务器如果做了 SDP 的修改或转发必须同步更新解析逻辑。我当时的做法是先在测试环境强制启用 Unified Plan把所有 SDP 相关的日志打出来对比 Plan B 和 Unified Plan 的差异然后逐个修改解析代码。重点是amid、assrc、amsid这几个属性的处理。迁移完成后代码反而更清晰了因为 Unified Plan 的结构更符合直觉。3.2 废弃的 API 清单与替代方案M66-M88 期间废弃或移除的 API 不少下面列出对业务影响最大的几个废弃 API废弃时间替代方案影响范围RTCPeerConnection.addStreamM70 起不推荐addTrack所有媒体发送逻辑RTCPeerConnection.onaddstreamM70 起不推荐ontrack所有媒体接收逻辑RTCRtpSender.track直接赋值M75 后受限replaceTracktrack 切换场景createOffer的mandatory参数M68 后忽略OfferOptions协商参数配置RTCIceCandidate的candidate属性M80 后变化标准属性访问ICE 候选处理addStream和onaddstream的废弃是最典型的。很多老教程里还在用这两个 API新版本浏览器虽然可能还兼容但行为已经不一致了。比如addStream在 Unified Plan 下会把 stream 里的所有 track 都加进去但 track 的mid分配逻辑和addTrack不同可能导致协商失败。replaceTrack的引入解决了一个大问题以前切换摄像头或麦克风需要重新协商 SDP现在可以直接替换 track不触发重新协商。这在视频会议里切换设备时非常有用用户体验从卡一下变成了无缝切换。但要注意replaceTrack要求新 track 的 kind 和原来一致而且如果原来 track 是null替换行为会有所不同。3.3 新引入的实用 API有废弃就有新增。这个区间里加入的几个 API 对实际业务帮助很大RTCRtpSender.getCapabilities和RTCRtpReceiver.getCapabilities在 M68 前后稳定下来让开发者可以在运行时查询浏览器支持的编解码器能力而不是硬编码。RTCPeerConnection.getStats的返回结构在这个区间里也做了标准化虽然字段名还有浏览器差异但至少有了统一的框架。RTCDtlsTransport和RTCIceTransport的暴露让开发者可以更细粒度地监控传输层状态。以前只能通过iceConnectionState和connectionState粗略判断现在可以拿到 DTLS 和 ICE 的详细状态。这在排查连接问题时特别有用比如你能区分是 ICE 没打通还是 DTLS 握手失败。setParameters和getParameters的完善让码率控制更灵活。以前调整码率只能通过 SDP 的bAS行现在可以在运行时动态调整encodings里的maxBitrate。不过这个 API 在不同版本里的行为有差异M75 之前某些参数改了不生效M80 之后才比较稳定。4. 安全与隐私收紧HTTPS、权限与指纹防护4.1 HTTPS 强制要求与本地开发绕过WebRTC 从很早就要求 HTTPS但 M66-M88 期间执行得越来越严格。getUserMedia在非安全上下文下直接不可用RTCPeerConnection虽然在某些版本里还能创建但实际连接会失败。这个变化对本地开发影响很大因为localhost是例外但如果你用局域网 IP 访问开发服务器就会遇到权限被拒的问题。解决办法有几个。最正规的是给本地开发环境配一个自签名证书然后用https访问。但自签名证书浏览器会报警告需要手动信任。另一个办法是用localhost加端口转发把开发服务器映射到本地。还有一个取巧的办法是启动浏览器时加--unsafely-treat-insecure-origin-as-secure参数把特定 origin 标记为安全但这个只适合临时调试不能用于生产。我个人的习惯是在开发环境用自签名证书配合一个脚本自动生成和信任证书。虽然初次配置麻烦一点但后续省心而且和生产环境的行为一致不会出现本地好好的上线就挂的情况。4.2 权限模型的演进getUserMedia的权限提示在 M66-M88 期间经历了几次调整。早期版本里权限提示是模态的用户不点掉就没法继续。后来改成了非模态的地址栏提示用户体验好了很多但对开发者的错误处理要求更高了因为用户可能直接忽略提示导致 Promise 一直 pending。navigator.permissions.query在这个区间里对camera和microphone的支持逐渐完善你可以在请求权限之前先查询状态避免不必要的弹窗。但要注意permissions.query返回的prompt状态并不保证用户一定会看到弹窗实际行为还是以getUserMedia的结果为准。另一个变化是权限的持久化。早期版本里用户授权后同一 origin 下的后续请求可能还会弹窗。后来改成了记住授权除非用户手动清除。这对用户体验是好事但开发者要注意如果用户之前拒绝了权限后续请求会直接失败需要用permissions.query来区分没问过和问过被拒。4.3 设备枚举与指纹防护enumerateDevices的行为在这个区间里变了。早期版本里未授权时也能拿到设备列表只是label为空。后来为了防指纹未授权时返回的列表被限制为默认设备而且deviceId每次刷新都会变。这意味着你不能在未授权时就缓存deviceId必须在授权后重新枚举。这个变化对设备选择逻辑影响很大。以前可以在页面加载时就枚举设备填充下拉框用户选择后再请求权限。现在必须反过来先请求权限再枚举设备。如果用户拒绝权限你连设备数量都不知道。实际项目里的应对策略是在用户点击开始通话按钮时先请求权限拿到权限后再枚举设备并展示选择界面。如果用户拒绝给出明确的引导告诉用户需要在浏览器设置里手动开启。不要试图在页面加载时就偷偷请求权限现代浏览器对这种行为会直接拦截而且用户体验很差。5. 实操迁移从 M66 到 M88 的兼容性适配方案5.1 版本检测与特性降级策略面对这么大的版本跨度最稳妥的做法是运行时检测而不是版本号判断。因为浏览器版本号可以伪造而且不同厂商的版本号规则不一样。特性检测的写法很简单function supportsUnifiedPlan() { const pc new RTCPeerConnection(); const transceiver pc.addTransceiver(video); const hasMid transceiver.mid ! null; pc.close(); return hasMid; }这个检测的原理是Unified Plan 下addTransceiver会立即分配mid而 Plan B 下mid是null。通过这个判断你可以决定后续的 SDP 处理逻辑。对于编解码器用RTCRtpSender.getCapabilities(video)来检测支持情况而不是硬编码版本号。对于 API直接检查方法是否存在比如typeof pc.addTrack function。降级策略的核心是优先使用新 API新 API 不可用时回退到旧 API旧 API 也没有时给出明确的错误提示。不要试图用 try-catch 包裹所有调用那样会掩盖真正的问题。5.2 SDP 处理的兼容性改造SDP 处理是迁移中最麻烦的部分。Plan B 和 Unified Plan 的 SDP 结构差异很大如果你的信令服务器或客户端有 SDP 解析逻辑必须同时支持两种格式。我的做法是写一个 SDP 解析层对外暴露统一的接口内部根据检测结果选择解析策略。对于 Unified Plan按m行逐个解析每个m行对应一个 track。对于 Plan B把所有mvideo行合并处理按 SSRC 分组。关键字段的处理要注意amid在 Unified Plan 下是必需的Plan B 下可能没有。assrc在 Unified Plan 下每个 track 独立Plan B 下多个 track 共享。amsid的格式也有差异。建议在解析时把这些字段都归一化上层业务代码不感知差异。还有一个坑是aice-ufrag和aice-pwd的位置。某些版本里这些属性在 session 级别和 media 级别都可能出现解析时要按优先级取。我遇到过因为取错了级别导致 ICE 连接失败的情况排查了很久。5.3 实际项目中的迁移步骤如果你手头有一个从 M66 时代就开始维护的项目要升级到 M88 兼容我建议按以下步骤来第一步先在测试环境把浏览器升级到目标版本跑一遍现有功能记录所有失败点。不要急着改代码先摸清楚哪些功能受影响。第二步处理 Plan B 到 Unified Plan 的迁移。这是最底层的改动其他改动都依赖它。改完后跑一遍完整的通话流程确保 SDP 协商正常。第三步替换废弃的 API。把addStream换成addTrackonaddstream换成ontrackreplaceTrack替换直接赋值。这一步改动量大但机械可以借助静态检查工具。第四步调整编解码器策略。根据业务需求用setCodecPreferences显式设置优先级不要依赖默认行为。第五步处理权限和设备枚举的逻辑。确保先请求权限再枚举设备处理好用户拒绝的情况。第六步全面回归测试。重点测试边界情况设备切换、网络切换、多人通话、屏幕共享。这些场景最容易暴露兼容性问题。提示迁移过程中建议保留一份旧版本的代码分支万一新版本出现无法解决的问题可以快速回滚。同时在灰度发布时按用户比例逐步放量观察线上指标。6. 常见问题与排查技巧实录6.1 协商失败类问题速查现象可能原因排查方法解决方案createOffer 后 setLocalDescription 报错SDP 格式不兼容打印 SDP 对比 Unified Plan 格式检查 transceiver 的 mid 是否分配远端 setRemoteDescription 失败编解码器不匹配对比双方 SDP 的 rtpmap用 setCodecPreferences 对齐连接建立但无媒体track 未正确关联检查 ontrack 是否触发确认 addTrack 在 createOffer 之前ICE 一直 checking候选地址不可达检查 iceConnectionState 和候选日志确认 STUN/TURN 配置正确通话几秒后断开DTLS 握手失败检查 DTLS 状态和证书确认时间同步和证书有效这张表里的问题我在不同项目里都遇到过。最常见的是连接建立但无媒体很多时候是因为addTrack调用时机不对。在 Unified Plan 下addTrack必须在createOffer之前调用否则 offer 里不会包含对应的m行。如果先 createOffer 再 addTrack需要重新 createOffer 并重新协商。6.2 编解码器相关的疑难杂症有一个很隐蔽的问题是双方都支持 H.264但协商出来的却是 VP8。原因可能是双方的 H.264 profile 不一致比如一方是profile-level-id42e01f另一方是4d0032浏览器认为不匹配就回退到 VP8。排查方法是把双方 SDP 里的fmtp行打出来对比确保 profile-level-id 一致。另一个问题是 VP9 的 SVC 模式。M75 之后 VP9 支持了 SVC但默认可能不启用。如果你的业务需要 SVC 来做分层编码需要在 SDP 里显式声明afmtp里的profile-id和flexfec参数。这个配置在不同版本里写法有差异建议参考对应版本的文档。6.3 权限与设备枚举的坑用户拒绝权限后getUserMedia返回的NotAllowedError和NotFoundError要区分处理。NotAllowedError是用户拒绝需要引导用户去设置里开启NotFoundError是没有设备需要提示用户检查硬件。很多开发者把这两个混在一起处理导致用户看到错误的提示。设备枚举的另一个坑是deviceId的稳定性。前面提到未授权时deviceId会变但授权后deviceId在同一 origin 下是稳定的。如果你需要在多个页面间共享设备选择可以把deviceId存到 localStorage但要注意用户清除浏览器数据后会失效需要做兜底。6.4 性能与稳定性经验在 M66-M88 期间WebRTC 的性能优化主要集中在硬件加速和拥塞控制上。如果你发现通话质量不稳定可以先检查getStats里的packetLost和jitter指标。丢包率高的话检查网络路径和 TURN 服务器配置。抖动大的话可能是编码器码率设置不合理。我个人的经验是不要盲目追求高分辨率和高帧率。在带宽有限的情况下降低分辨率比降低帧率对主观体验的影响更小。另外maxBitrate的设置要留有余量不要贴着带宽上限设否则网络稍有波动就会丢包。还有一个容易被忽略的点是音频的echoCancellation和noiseSuppression。这两个在getUserMedia的 constraints 里默认可能是开启的但在某些设备上效果不好反而引入延迟。如果业务对延迟敏感可以尝试关闭这两个选项用外部算法处理。7. 我个人的迁移体会从 M66 到 M88 这一路跟下来最大的感受是WebRTC 的标准化程度越来越高但兼容性成本并没有降低只是从处理厂商差异变成了处理版本差异。Plan B 到 Unified Plan 的迁移是一次性的痛但编解码器和 API 的调整是持续性的。我的建议是不要等到浏览器强制升级了才被动适配。平时就关注版本更新日志在测试环境保持浏览器版本领先生产环境一两个大版本提前发现兼容性问题。另外把 WebRTC 相关的代码做好抽象层把版本差异隔离在底层上层业务逻辑尽量不感知。这样每次升级只需要改抽象层业务代码不动。最后分享一个小技巧如果你不确定某个 API 在目标版本里的行为最可靠的方法是写一个最小复现的 HTML 页面在目标浏览器里跑一遍把关键日志打出来。这比查文档快得多也更准确。文档可能滞后但实际行为不会骗人。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑