4、浏览器兼容性:支持WebRTC的浏览器、getUserMedia API兼容性、适配库(adapter.js)的使用

做WebRTC开发,最头疼的问题是什么?

我个人觉得,不是信令怎么设计,也不是媒体流怎么控制——而是浏览器兼容性。你想想看,同一个getUserMedia调用,在Chrome上跑得好好的,换到Safari就报错,再换到Firefox又换了一套参数写法。嗯,这确实让人头大。

我记得刚接触WebRTC那会儿,花了一整天调试一个摄像头权限问题。最后发现,只是因为我没加适配库。从那以后,我养成了一个习惯:任何WebRTC项目,第一件事就是引入adapter.js。

核心观点:浏览器兼容性不是「要不要处理」的问题,而是「怎么处理更优雅」的问题。adapter.js就是官方给出的标准答案。

4.1 哪些浏览器支持WebRTC?

先说结论:目前主流浏览器基本都支持WebRTC了。但支持的版本和程度有差异。

浏览器 支持起始版本 WebRTC 1.0 支持情况 备注
Chrome 28+ 完整支持 最早支持,最稳定
Firefox 22+ 完整支持 与Chrome实现略有差异
Safari 11+ 基本支持 早期版本问题较多
Edge 12+(旧版)
79+(Chromium版)
旧版部分支持
新版完整支持
建议使用Chromium版
Opera 20+ 完整支持 与Chrome同内核
Samsung Internet 4+ 基本支持 移动端常见

这里有个坑要注意:Safari在11到13版本之间,对WebRTC的支持是「半残」的。我在项目中遇到过,用户用Safari 12打开页面,视频通话直接黑屏。查了半天,发现是SDP协商格式不兼容。后来强制升级到Safari 14才解决。

避坑指南:如果你要支持iOS用户,建议最低要求Safari 14+。iOS上的第三方浏览器(如微信内置浏览器)也用的是Safari内核,所以同样受限。

4.2 getUserMedia API的兼容性

getUserMedia是WebRTC的入口。说白了,就是让浏览器帮你打开摄像头和麦克风。但这个API在不同浏览器上的表现,差别还挺大。

为什么会这样?因为getUserMedia经历了两次大的变化:

  • 旧版:navigator.getUserMedia() —— 带回调函数,各浏览器前缀不同
  • 新版:navigator.mediaDevices.getUserMedia() —— 返回Promise,标准写法

我建议你直接使用新版API。但要注意,Safari 11之前不支持mediaDevices对象。Firefox和Chrome虽然支持,但参数格式也有细微差别。

来看一个兼容性写法:

// 不推荐:直接调用旧版API
navigator.getUserMedia({ video: true }, success, error);

// 推荐:使用新版API + 降级处理
const constraints = {
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  },
  audio: true
};

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  navigator.mediaDevices.getUserMedia(constraints)
    .then(stream => {
      // 使用stream
    })
    .catch(err => {
      console.error('获取媒体流失败:', err);
    });
} else {
  // 降级处理:提示用户升级浏览器
  alert('您的浏览器不支持WebRTC,请升级到最新版本');
}

个人经验:constraints参数在不同浏览器上的支持度也不一样。比如Chrome支持width/height的ideal/exact/min/max,但Safari只支持ideal。我一般只用ideal,兼容性最好。

4.3 适配库 adapter.js 的使用

adapter.js是Google官方维护的一个垫片库。它的作用很简单:把不同浏览器的WebRTC实现差异抹平,让你写一套代码,到处运行。

说白了,adapter.js做了三件事:

  • 前缀统一:把webkitRTCPeerConnection、mozRTCPeerConnection等统一成RTCPeerConnection
  • API垫片:给不支持新版API的浏览器添加mediaDevices对象
  • 行为修正:修复各浏览器在SDP协商、ICE候选等环节的差异

怎么用?很简单:

<!-- 方式一:CDN引入 -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

<!-- 方式二:npm安装 -->
<!-- npm install webrtc-adapter -->
import adapter from 'webrtc-adapter';

// 引入后,直接使用标准API即可
const pc = new RTCPeerConnection();
// 不需要判断前缀,adapter会自动处理

引入adapter.js之后,你就不用再写那些繁琐的浏览器判断了。比如:

// 没有adapter.js时,你需要这样写
const RTCPeerConnection = window.RTCPeerConnection ||
  window.webkitRTCPeerConnection ||
  window.mozRTCPeerConnection;

// 有adapter.js后,直接写
const pc = new RTCPeerConnection();

我曾经在一个项目中,因为没有用adapter.js,导致Firefox上ICE连接一直失败。后来发现是Firefox的STUN服务器配置格式跟Chrome不一样。加上adapter.js后,一行代码都没改,问题就解决了。

重要提醒:adapter.js不是万能的。它只处理WebRTC核心API的差异,不处理媒体设备枚举(enumerateDevices)、屏幕共享等扩展API的差异。这些还是需要你自己做兼容处理。

4.4 知识体系总览

下面这张图,帮你理清浏览器兼容性的核心脉络:

WebRTC 浏览器兼容性知识体系 浏览器兼容性 支持WebRTC的浏览器 Chrome / Firefox Safari / Edge Opera / Samsung getUserMedia API 旧版(回调) 新版(Promise) adapter.js 适配库 前缀统一 API垫片 行为修正 核心原则:写一套代码,到处运行 adapter.js + 新版API = 最佳实践

4.5 实战建议

说了这么多,总结几条我个人的实战经验:

  1. 永远引入adapter.js。不管你的目标浏览器是什么,加上它只有好处没有坏处。文件才几十KB,不值得省。
  2. 使用新版getUserMedia API。不要再用回调写法了。Promise写法更清晰,也更容易做错误处理。
  3. 测试要覆盖主流浏览器。Chrome、Firefox、Safari至少各测一遍。我见过太多「Chrome上完美,Safari上崩了」的案例。
  4. 注意移动端。Android上的Chrome和iOS上的Safari,行为跟桌面端不完全一样。特别是权限弹窗的交互,差异很大。
  5. 做好降级提示。如果浏览器不支持WebRTC,不要直接白屏。给用户一个友好的提示,告诉他怎么升级。

一个小技巧:可以用navigator.mediaDevices.enumerateDevices()来检测设备是否可用。如果返回空数组,说明浏览器虽然支持API,但没有摄像头或麦克风。这种情况在PC上很少见,但在平板上偶尔会遇到。

好了,浏览器兼容性这块就聊到这儿。记住一句话:别跟浏览器较劲,用adapter.js就对了。


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