什么是 WebRTC
WebRTC项目的官网 ,有兴趣可以细细浏览。按MDN官网中文页面的介绍:
WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和(或)音频流或者其他任意数据的传输。WebRTC包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和电话会议成为可能。 API文档
简而言之,WebRTC就是W3C定的一个Web标准,用来做二进制数据传输用的,各浏览器只要支持WebRTC,就可以不通过插件,直接在Web端实现诸如音频、视频、文件等二进制数据的传输。方便之处在于:不需要再使用Flash、Silverlight之类的插件了。but!理想很丰满,现实往往很残酷,WebRTC从诞生之初到现在仍然还有很多地方布满了坑。
有兴趣的可以到这里看看有关WebRTC的前世今生
WebRTC使用要求
1. 支持WebRTC的Web运行环境
越简单越容易忽略,必须要强调说明,并不是所有的Web运行环境都支持WebRTC,支持WebRTC的Web运行环境也不一定会支持所有的API及特性,因此,先要确定Web运行环境是否能支持,可以通过检测页面看一下(需要访问外国网站,覆盖了WebRTC几乎所有API方法)或腾讯云WebRTCAPI.js的检查。
2. HTTPS
出于安全考虑,WebRTC只允许在HTTPS环境下使用,否则浏览器将不允许页面获取音频或视频设备,调试时请使用HTTPS或localhost。
3. 数据对象
数据对象指的是浏览器能够管理的输入/输出设备,因为 WebRTC 的设备管理方式是浏览器先将输入/输出设备抽象为 MediaStream 对象,再通过 API 将 MediaStream 对象提供给 Web 页面使用,如果浏览器无法识别到设备,那么 Web 页面必然是获取不到数据的。
WebRTC 流程
WebRTC 的工作流程,如下图。详见WebRTC通信流程
看上去比较复杂,可以花时间理解一下交互过程。简单来说分两部分:
- 信令通道 什么叫信令?比如说发起通话、结束通话、响铃、占线、挂断、成员新增、成员减少等等通话相关的信息同步,这些就是信令。 WebRTC 标准并没有强制规定如何传递信令,只是建议使用 SIP 和 SDP 协议,SIP 和 SDP 具体是什么这里先不多作介绍,先知道它们可以做通话信令就行。通常,WebRTC 服务商使用的是 websocket 来做信令通道。
- 数据通道 数据通道就是用于传输 MediaStream 对象的通道,MediaStream 对象可以是音频、视频、文件、消息等等二进制数据。数据通道方面 WebRTC 标准规定了使用 RTCPeerConnection 来实现,即通过 P2P 直连的方式进行数据传输。 P2P 直连过程,打洞是肯定要做的事情,一般打洞的方式都是使用ICE。
这里以腾讯云当前提供的 WebRTC SDK 即 WebRTCAPI.js 为例 ,简述一下其工作过程如下:
1、 SDK 封装的 getLocalStream
是通过浏览器的 MediaDevice
和 getUserMedia
标准方法来得到设备的数据( MediaStream
对象),通过 getLocalStream
拿到 MediaStream
对象后将其赋值给页面的 <video>
标签元素的 srcObject
属性即可显示播放,在本地进行预览;
2、SDK 与服务端通过 websocket 方式建立信令通道,SDK 封装的方法为 enterRoom
;
3、wss 连接建立后,SDK 会与服务端进行 SDP 协商,交换双方的音视频支持能力及相关参数;
4、 SDP 协商成功后,SDK 与服务端节点建立 RTCPeerConnection
,这个过程会有 P2P 打洞(ICE),为了提高打洞成功率,服务端提供了 STUN 服务器,SDK 会与服务器 P2P 直连后进行数据收发,SDK 封装的的方法是 startRTC
;
5、每个加入房间的用户,都会先按照 1-4 步,与服务端建立 wss 连接传递信令,同时通过 RTCPeerConnection
将数据发送到服务端。新进入用户后,已经在房间里的用户(包括新加入成功的用户)将会通过 wss 接收到其他用户进入房间的通知;
6、然后每个用户会与服务端协商 SDP 信息,协商成功后,服务端会将远端的用户数据通过 RTCPeerConnection.onTrack
传输给每个在房间的用户,SDK 将其封装在 onRemoteStreamUpdate
事件;用户通过 onRemoteStreamUpdate
获取到远端的 MediaStream
对象,然后将其赋值给页面的 <video>
标签元素,展示远端用户的画面;
7、上面的 1-6 步即从开始到通话的过程,在通话过程中间,如果有人员退出,SDK 会通过 wss 收到服务端的通知,并通过 onRemoteStreamRemove
得知变化,在本地移除 <video> 标签,直至最终所有人停止推流(stopRTC
)退出(quit
)。
WebRTC 坑点
- 不是所有浏览器都支持WebRTC
- 不是所有支持WebRTC的浏览器都支持全部的标准方法
- 同一款浏览器的桌面版与移动版对WebRTC支持不同
- 同一款、同一端的浏览器不同版本号对WebRTC支持不同
- 不能被浏览器识别的输入设备Web页面必然拿不到
- 不能被浏览器支持的编解格式必然显示不了,浏览器支持的格式视频有H.264/VP8/VP9,音频有Opus/iSAC(理解一下,现在的WebRTC不适合做直播的原因之一)
- WebRTC必然要打洞,不支持P2P直连的网络不支持。我们后面会做免打洞方案,虽然本质上也是P2P,不过不是直连
- WebRTC标准带了安全校验,必须走https,调试可以走localhost
- 画面不显示,有可能是没有拿到数据,也可能是 <video> 标签没有处理好
- 常见问题看 腾讯文档
排查手段
设备拿不到
- 根据原理,直接在页面打开控制台,输入
navigator.mediaDevices.enumerateDevices()
看看能不能获取到设备列表,拿不到说明浏览器都没有识别到设备,检查浏览器或检查设备; - 能拿到设备列表的情况下,输入
navigator.mediaDevices.getUserMedia({"audio":true,"video":true})
看能不能正常返回 MediaStream 对象,不能正常返回说明浏览器没有拿到数据,检查一下浏览器的配置。
画面显示不了
- 先确认的确有拿到数据
- 根据原理,检查 <video> 元素的 srcObject 属性是否正常赋值了正确的 mediastream 对象,不对的话肯定显示不了;
- 有部分情况是浏览器的问题,检查一下浏览器是不是编解码器挂了,一般重启浏览器,或重启系统(设备被其他进程占用时就不能用),chrome如果硬件编解码不行的时候,可以在 chrome://flags 里面把 #enable-webrtc-h264-with-openh264-ffmpeg 这个标记设置为 enable ,强制软件编解码,不过看标记名字也知道,使用的是ffmpeg调用openh264库,性能消耗会有点大。
回声
- 同一房间收发的人隔得太近的时候,请离远一点;
- 有可能是把自己的声音播放出来了,检查本地音频,静音muted。
- 某些设备上有回声,没办法,编解码是浏览器控制的,SDK没办法控制,需要等浏览器厂商来解决。
网络
- 网络连接不上的,先检查防火墙。有些P2P直连不成功的网络(对称型NAT的),这种建议先换网络;
- 网络抗抖动能力觉得不好的,WebRTC 的网络传输是浏览器控制,其自带的QoS是NetEQ,这个东西已经上了年纪,而且算法简单粗暴,能够抵抗一定程度的网络抖动,但抗抖动能力有限,稍微多一点就不行了,加上浏览器的 <video>在渲染和显示机制上有点坑,所以最终画面显示效果受很容易受网络影响,因此在当前WebRTC标准还不是特别完善的情况下,网络一定要好一点。
画面模糊
设备采集、编解码、网络都在浏览器手里攥着,SDK没办法控制,所以,尽量保持设备好、浏览器好、网络好。