WebRTC 踩坑记

762 阅读18分钟

我们懂得越多,我们不懂的就越多。

1、WebRTC是什么?

WebRTC 是 Web 实时通信(Real-Time Communication)的缩写,是由一家名为 Gobal IP Solutions(简称 GIPS)的瑞典公司开发的。Google 在 2011 年收购了 GIPS,并将其源代码开源。然后又与 IETF 和 W3C 的相关标准机构合作,以确保行业达成共识。其中:

  • W3C 组织:定义浏览器 API。
  • IETF 标准组织:定义其所需的协议,数据,安全性等手段。

简单来说,WebRTC(基于UDP进行数据传输) 是一个可以在 Web 应用程序中实现音频,视频和数据的 实时通信的开源项目。

在实时通信中,音视频的采集和处理是一个很复杂的过程。比如音视频流的编解码、降噪和回声消除等,但是在 WebRTC 中,这一切都交由浏览器的底层封装来完成。我们可以直接拿到优化后的媒体流,然后将其输出到本地屏幕和扬声器,或者转发给其对等端。

我们可以在不需要任何第三方插件的情况下,实现一个浏览器到浏览器的点对点(P2P)连接,从而进行音视频实时通信。

WEBRTC 所用的技术大多已经实现了,WEBRTC 是把这些技术组合了起来。也可以说 WebRTC 是一组其他技术的集合体。

2、WebRTC中常用的一些API

WebRTC 提供了一些 API 供我们使用,在实时音视频通信的过程中,我们主要用到以下三个:

  • getUserMedia:获取音频和视频流(MediaStream)
  • RTCPeerConnection:点对点通信
  • RTCDataChannel:数据通信

三个API的使用方式:

GetUserMedia

用来获取设备的媒体流(即 MediaStream)。它可以接受一个约束对象 constraints 作为参数,用来指定需要获取到什么样的媒体流。此要求可以非常宽泛(音频和/或视频),也可以非常具体(最低相机分辨率或确切设备 ID 等)。

navigator.mediaDevices.getUserMedia({ audio: true, video: true })  // 同时获取到音频和视频
    .then(stream => {
    	// 获取到优化后的媒体流
        video.srcObject = stream
    })
	.catch(err => {})

RTCPeerConnection

创建点对点连接的 API,是我们实现音视频实时通信的关键。创建时,需要传入一个配置对象,包含 iceServers 字段。

new RTCPeerConnection({
  iceServers: [
    { url: "stun:stun.l.google.com:19302" }, // 谷歌的公共服务
    {
      urls: "turn:***",
      credential: "***",
      username: "***"
    }
  ]
})

RTCDataChannel

创建数据通道,用来交换任意类型的数据

let dataChannel = pc.createDataChannel("MyApp Channel")  // 名称基本上没有用

dataChannel.addEventListener("open", (event) => {
  // 对回调进行处理
  beginTransmission(dataChannel)
})

3、WebRTC的工作流程

WebRTC的工作流程可以具体将其分为四个步骤:

  • 信令(Signaling)
  • 连接(Connecting)
  • 安全加密(Securing)
  • 通信(Communicating)

这四个步骤依次发生。上一个步骤必须 100% 成功,随后的步骤才能开始。

信令(Signaling)

信令的概念:

信令是在两个设备之间发送控制信息以确定通信协议、信道、媒体编解码器和格式以及数据传输方法以及任何所需的路由信息的过程。

为什么需要信令?

虽然 WEBRTC 是 P2P 的,但并不代表不需要服务端的参与。当一个 WebRTC Agent 被创建时,它对其他的对等节 一无所知。它不知道它将与谁联系,也不知道它们将发送些什么!所以在信令阶段需要双向通信服务辅助信息交换。 交换信令消息后,WebRTC Agent 才可以直接相互通信。

格式

信令 的格式基于 SDP (Session Description Protocol) 规范,例如:

v=0
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com
s=
c=IN IP4 host.anywhere.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000

-------------- 下面的内容为字段解释 ---------------
v - Version,版本,版本,应等于 0。
o - Origin,源,包含一个唯一 ID,用于重新协商。
s - Session Name,会话名称,应等于-。
t - Timing,时间,应等于 0 0。
m - Media Description(m=<media> <port> <proto> <fmt> ...),媒体描述,下面有详细说明。
a - Attribute,属性,一个自由文本字段,这是 WebRTC 中最常见的行。
c - Connection Data,连接数据,应等于 IN IP4 0.0.0.0。
  • v, o, s, c, t 虽然被定义,但他们不对 WebRTC 会话产生影响。
  • 上面的代码里有三个媒体描述。第一个是 audio 即音频类型,数据映射到 PCMU 编解码器。后两个是 video 即视频类型,分别映射到 H261 和 MPV 编解码器。
内容

信令 的内容包含以下几个方面:

  • 控制消息:用于设置、打开、关闭通信通道并处理错误。
  • 为了建立连接所需的信息:设备间能够彼此交谈所需的 IP 寻址和端口信息。
  • 媒体能力协商:交互双方可以理解哪些编解码器和媒体数据格式?这些都需要在 WebRTC 会议开始之前达成一致。
传输

信令 的传输没有规范,可以选择 Websocket 或者 xhr 甚至电子邮件。如果你愿意接受延迟,甚至可以把信令数据打印出来,飞鸽传书给另一个人,等对方手动输入,并用相同的方式返回给你信令数据。

传输流程

信令传递流程.png

信令的使用和传输流程如下图所示,假如 Amy 向 Bob 发送了一个音视频邀约:

  1. 呼叫端创建 Offer 信息,并存储到本地,再发送给信令服务器,信令服务器转发给接收端

  2. 接收端收到 Offer 后,先存储远端描述,然后创建 Answer 信息。同样,先保存 Answer 到本地,再返回给信令服务器,信令服务器转发给呼叫端。

  3. 呼叫端拿到 Answer 后,再设置到远端描述

什么是 Offer 和 Answer?

WebRTC 使用 Offer/Answer 模型。这指的是,一个 WebRTC Agent 发出 “Offer” 以开始呼叫,如果另一个 WebRTC Agent 愿意接受 “Offer” 的内容,它会响应 “Answer”。

这使得应答者有机会拒绝媒体描述中的某些不支持的编解码器,也是两个 peer 互相理解他们希望交换何种格式的方式。

当两个 WebRTC Agent 知道足够的详细信息可以尝试相互连接了。

连接(Connecting)

接下来,WebRTC 将使用另一种成熟的技术,称为 ICE(交互式连接建立)。

ICE 允许在两个 Agent 之间建立连接。这些 Agent 可以在同一网络上,也可以在世界的另一端。ICE 是无需中央服务器即可建立直接连接的解决方案。

既然要直接建立连接,就必然需要解决例如 NAT 地址转换、防火墙、网络协议不同、IP 版本不同等问题。ICE 使用STUN 协议以及 TURN 协议来进行穿越。

NAT穿越

现在的网络设备,很多是基于 NAT 技术使用同一个公网 IP 和不同的端口对外提供访问通道的。如果需要建立直接连接,就需要知道对方的公网 IP 和端口,以及对方的网络类型,比如是直接在公网可访问,还是经过了 NAT 设备。

  • 接受客户端的请求,并且把客户端的公网 IP、Port 封装到 ICE Candidate 中。
  • 通过一个复杂的机制,得到客户端的 NAT 类型。

简而言之,就是向公网的一个 STUN 服务器发送 Binding Request,公网的这个服务器能看到你的公网 IP 和端口,并测试你是否在 NAT 设备后面,以及 NAT 的类型。

NAT穿越流程.png

判断NAT类型

RFC3489 中将 NAT 的实现分为四大类:

Full Cone NAT完全锥形 NAT无论什么IP地址访问,都不会被NAT墙掉(这种基本很少)
Restricted Cone NAT限制锥形 NAT理解为 IP 限制,Port不限制
Port Restricted Cone NAT端口限制锥形 NAT,IP+Port 限制
Symmetric NAT对称 NATIP+Port 限制,同时对外的公网Port是不停的变化的比如A是一个对称NAT,那么A给B发信息,经过NAT映射到一个Port:10000,A给C发信息,经过NAT映射到一个Port:10001,这样会导致一个问题,我们服务器根本无法协调进行NAT打洞。

假设 B 是 客户端 ,C 是 STUN 服务器,C 有两个 IP 分别为 IP1 和 IP2:

第一步:判断 客户端 是否在 NAT

B 向 C 的 IP1:PORT1 端口发送一个 UDP 包。C 收到这个包后,会把它收到包的源 IP 和 PORT 写到 UDP 包中,然后把此包通过 IP1:PORT1 发还给 B。这个 IP 和 port 也就是 NAT 的外网 IP 和 PORT。

B 收到后,拿返回的 IP 和自己的 IP 对比一下,如果一致,说明 B 就是用的公网 IP,没有用 NAT,如果不一致则继续探测防火墙类型。

NAT类型判断流程step1.png

第二步:判断是否处于Full Cone Nat 下:

B 还是向 IP1:PORT1 端口发送请求,但是要求 C 用 IP2:PORT2 返回结果。

如果 B 收到了,说明 NAT 来者不拒,不对数据包进行任何过滤,这也就是STUN标准中的 Full Cone NAT。

NAT类型判断流程step2.png

第三步:判断是否处于对称 NAT

根据对称 NAT 的规则,当目的地址的 IP 和 PORT 有任何一个改变,那么 NAT 都会重新分配一个port使用。所以 B 再向 IP2:PORT2 请求,返回的端口和第一次如果不一致,说明就是对称 NAT。

NAT类型判断流程step3.png

第四步:判断是处于 Restrict Cone NAT 还是 Port Restrict NAT 之下

B 向 IP1:PORT1 发请求,要求 C 换一个端口返回。

如果 B 收到了,那也就意味着只要 IP 相同,即使 PORT 不同,NAT 也允许 UDP 包通过。显然这是 Restrict Cone NAT。如果没收到,意味着 IP 和 PORT 都必须相同,就是 Port Restrict NAT。

完成了这些 STUN Server 就会把这些基本信息发送回客户端,然后根据 NAT 类型,来判断是否需要 TURN 服务器协调进行下一步工作。

NAT类型判断流程step4.png

完成了这些 STUN Server 就会把这些基本信息发送回客户端,然后根据 NAT 类型,来判断是否需要 TURN 服务器协调进行下一步工作。

NAT穿越流程2.png

当你的两个 peer 的 NAT 类型不兼容,或者双方使用不同协议时,就需要使用 TURN server,过程如上图所示。TURN server 也可以被用于保护隐私的目的,如果通过 TURN server 进行所有通讯,客户的真实地址在对端是被隐藏的。

候选地址选择

ICE 通过上面讲到的流程来找出两个 peer 之间所有可能的路由,这些路由被称为 Candidate Pair(候选地址对),也就是本地地址和远程地址的配对。这就是 STUN 和 TURN 在 ICE 中发挥作用的地方。这些地址可以是你的本地 IP 地址,NAT 映射中继传输地址。通信双方需要收集它们要使用的所有地址,交换这些地址,然后尝试连接。

通常来说,发送 offer 的 peer 是控制中的一方。控制中的 Agent 和受控中的 Agent 都开始在每个候选地址对上发送流量数据。

每个收到流量数据的候选地址对,会被提升为有效候选地址对。

接下来,控制中的 Agent 将指定一个有效候选地址对,这就是提名候选地址对

然后,控制中的 Agent 和受控中的 Agent 再尝试进行一轮双向通信。如果成功,则提名候选地址对将成为选定的候选地址对!它将被用于后面的会话中。

安全加密(Securing)

现在我们有了双向通信(基于 ICE),我们需要建立安全的通信,这是基于 WebRTC 前已有的两种协议完成的。

第一个协议是 DTLS(数据报传输层安全性),即基于 UDP 的 TLS。

第二种协议是 SRTP(安全实时传输协议)。

首先,WebRTC 通过在 ICE 建立的连接上进行 DTLS 握手来进行连接。与 HTTPS 不同,WebRTC 不使用中央授权来颁发证书。相反,WebRTC 只是判断通过 DTLS 交换的证书是否与通过信令共享的签名相符。然后,此 DTLS 连接可以被用于传输 DataChannel 消息。

WebRTC 使用两个预先存在的协议,数据报传输层安全(Datagram Transport Layer Security / DTLS)和 安全实时传输协议(Secure Real-time Transport Protocol / SRTP)。

DTLS 与 TLS 的区别仅在与其使用 UDP 而不是 TCP 作为其传输层。这也意味着 DTLS 协议必须处理不可靠的数据传输。SRTP 是专为安全的交换媒体数据而设计的。相对于 DTLS 而言,使用 SRTP 对传输媒体数据有一些优化。

通信(Communicating)

现在,我们有了两个具有安全的双向通信功能的 WebRTC Agent。让我们开始通信!跟前面一样,我们使用两个现有的协议:RTP(实时传输协议)和 SCTP(流控制传输协议)。我们使用 RTP 来交换用 SRTP 加密过的媒体数据,使用 SCTP 发送和接收那些用 DTLS 加密过的 DataChannel 消息。

整体流程图

WebRTC整体流程图.png

4、WebRTC的应用场景

电话视频会议

WebRTC 为媒体提供拥塞控制和自适应比特率,随着网络条件的变化,用户仍将获得最佳体验,开发人员不必编写任何其他代码来处理这些情况。

由于可以对视频做处理,所以也支持例如添加滤镜、背景替换、AR 等功能。

Facebook Messenger、Discord、Amazon Chime、Google Meet/Hangout/Duo,都是基于 WebRTC 的视频通讯工具、视频会议应用。

音视频发布、消费平台

浏览器中的 WebRTC 使得用户可以轻松发布视频。这样用户不需要下载新的客户端。 任何具有 Web 浏览器的平台都可以发布视频。发布者可以发送多个音轨 / 视频流,并可以随时对其进行修改或删除。传统协议中每个连接只允许一个音频或一个视频流,与之相比,这是一个巨大的改进。

在线白板、投屏

利用 captureStream API 可以直接捕获 canvas 的画面,然后通过 WEBRTC 以流的形式传输给对方,就很容易的实现了在线白板功能。

this.localstream = canvasDom.captureStream()

利用 getDisplayMedia API 可以直接捕获其他浏览器 tab、窗口或屏幕的画面(例如飞书投屏时的选择),下面是核心代码:

navigator.mediaDevices.getDisplayMedia(options)
  .then(handleSuccess, handleError);

handleSuccess(stream) {
    video.srcObject = stream;
}

云游戏

2020 年,云游戏已经上线了。它的实现有赖于 WebRTC。 Stadia(Google 的云游戏平台)已于 2019 年底推出,但 2020 年初才正式在浏览器得以支持。其云游戏搭载 VP9,提供 4k、HDR 图像和环绕声体验。这些都会通过 WebRTC 进行传输。

5、WebRTC其他一些相关问题

主流浏览器兼容性

序号浏览器兼容性
1Google Chrome支持WebRTC的全部功能,并且是其原始实现者。
2Mozilla Firefox支持WebRTC的全部功能,Firefox是第一个支持WebRTC的主流浏览器之一。
3Microsoft EdgeEdge12版本起开始支持WebRTC,并且支持较好。
4SafariSafari 11版本起开始支持WebRTC,并且支持较好。不过在早期版本中某些特性可能不完整。
5Opera支持WebRTC,并且在大多数方面与Chrome相似。

WebRTC浏览器兼容性.png

操作系统兼容性

序号操作系统兼容性
1WindowsWebRTC可以在Windows操作系统上进行开发和部署。主流浏览器如Google ChromeMozilla FirefoxMicrosoft Edge都支持Windows,因此可以在这些浏览器上使用WebRTC。
2macOS支持在macOS操作系统上的开发和部署。Safari浏览器在较新的版本中支持WebRTC,而其他主流浏览器如Google ChromeMozilla Firefox也都可以在macOS上使用。
3LinuxWebRTC可以在多个Linux发行版上使用,而且主流浏览器如Google ChromeMozilla Firefox对Linux提供了良好的支持。
4AndroidWebRTC在Android操作系统上有广泛的支持,可以通过在Android应用程序中使用相应的API进行开发。
5iOSWebRTC也可以在iOS浏览器包括SafariGoogle Chrome,都支持WebRTC。

网络和硬件要求

WebRTC(Web实时通信)并没有严格的网络或硬件要求,但是一些要求和推荐可以帮助确保良好的使用体验和性能。

网络要求
  • 带宽:WebRTC使用实时音视频传输,因此需要足够的带宽来支持流畅的音视频通信。推荐的最低带宽为500 Kbps(上行和下行总带宽)以支持基本的音视频质量。对于高清视频质量和更高的帧率,需要更高的带宽。
  • 网络延迟和稳定性:WebRTC对于延迟较低和稳定的网络连接更为敏感。较高的延迟会导致音视频时延增加,而不稳定的网络连接可能导致音视频质量下降或中断。
硬件要求
  • 麦克风和摄像头:用于音频输入和视频输入的麦克风和摄像头是进行实时音视频通信的必需硬件。通常,计算机、手机和平板电脑等设备都内置了麦克风和摄像头。
  • 扬声器或耳机:用于音频输出的扬声器或耳机也是进行实时音频通信的必需硬件。它们使用户能够听到和与对方进行实时交流。

传输效率和性能

WebRTC(Web实时通信)在传输效率和性能方面具有一些优势,同时也受到一些因素的影响。

传输效率
  • 点对点传输:WebRTC支持点对点(peer-to-peer)的通信方式,直接在用户间建立连接进行音视频传输,而无需通过中间服务器。这种点对点传输可以减少传输路径和延迟,提高传输效率。
  • 结构化数据传输:除了音视频数据,WebRTC还支持传输结构化数据,如文本聊天、文件传输等。这些数据可以与音视频流并行传输,提高整体传输效率。
性能
  • 音视频编解码:WebRTC使用了媒体传输标准(如VP9和H.264),这些标准在保证良好视听质量的同时,也尽可能降低了传输带宽的需求和音视频编解码的性能开销。
  • 智能适应性:WebRTC具有智能适应性,可以根据网络状况和设备条件,自动调整传输参数和编码质量,以提供最佳的音视频性能和用户体验。
  • NAT穿越和防火墙遍历:WebRTC内置了NAT穿越和防火墙遍历的技术,可以克服网络限制,确保在不同网络环境下的良好连接和传输性能。

具体的传输效率和性能还受到网络带宽、延迟、设备性能、网络稳定性等因素的影响。较低的带宽、较高的延迟或不稳定的网络连接可能会影响音视频质量和传输性能。因此,在使用WebRTC时,确保网络稳定和设备良好性能,以获得最佳的传输效率和性能。

6、与WebRTC功能相似的产品

SIP(Session Initiation Protocol)和RTP(Real-time Transport Protocol)

这是一种传统的通信协议组合,用于实时音视频通信。与WebRTC相比,它们通常需要额外的服务器设备,以及更多的配置和管理工作。

音视频编解码库

这些是用于处理音视频编解码的库和框架,如FFmpeg、GStreamer、OpenTok等。它们提供了更底层的音视频处理功能,可以与其他通信技术结合使用。

专有实时通信平台

一些专有的实时通信平台,如Twilio、Agora、Zoom等,提供了更高级的功能和服务,如会议控制、屏幕共享、红外线等。

7、需要注意的问题

  • 在进行p2p的连接时,必须按照规定的流程来,先配置连接双方的设备等信息(Offer,Answer),然后配置的ICE网络等信息才能生效。
  • 当使用WebRTC的datachannel进行数据传输时,存在数据大小的限制(在Chrome浏览器中,最大发送字节为262144,即256kb),如果所传输的数据超过了WebRTC缓冲区的大小,就必须将数据分块进行传输。
  • WebRTC的datachannel可以传输多种类型的数据,比如文本数据、二进制数据、数组数据(Int8Array,ArrayBuffer等)、Blob数据等等。
  • 在不同的设备上要想进行WebRTC的P2P通信需要借助Stun和Turn服务器。
  • 在使用WebRTC时必须联网(内网外网都可以)。