入门和学习WebRTC

96 min read

WebRTC 概念:

WebRTC(Web Real-Time Communications)是一种支持网页浏览器之间直接进行实时音视频通信和数据共享的技术。

WebRTC 的发展历史

  1. 初始阶段:WebRTC 最初是由 Google 在 2011 年提出,并随后开源。这一技术的出现是为了解决网络通信中存在的复杂性问题。
  2. 标准化和发展:自推出以来,WebRTC 经历了持续的发展和标准化。W3C 和 IETF 是主要负责其标准化的组织。在这个过程中,许多浏览器厂商开始支持 WebRTC,包括 Google Chrome、Mozilla Firefox 和 Microsoft Edge。
  3. 广泛应用:随着技术的成熟和普及,WebRTC 已被广泛应用于多种网络通信场景,成为了在线通信领域的关键技术之一。

WebRTC 的应用场景

  1. 视频会议:WebRTC 可以实现浏览器之间的视频通话,是许多在线会议和视频聊天服务的基础。

  2. 文件共享:WebRTC 支持点对点的数据通道,使得文件和数据的直接传输成为可能,提高了文件共享的效率。

  3. 在线教育:在在线教育领域,WebRTC 支持实时的音视频交流,使得远程教学更加互动和实时。

  4. 游戏:在网络游戏中,WebRTC 可用于实现玩家之间的实时语音通讯。

  5. 远程协助:在远程工作和协助场景中,WebRTC 可以用于实现实时屏幕共享和协作。

WebRTC 的架构

===================== 图片

图中所示的 WebRTC 架构可以分为以下几个类别:

  1. 应用层

    • 视频会议
    • 视频通话
    • 远程教育
  2. 接口层

    • Web API(W3C 标准)
    • WebRTC C++ API(PeerConnection)
  3. 会话管理和信令层

    • 会话管理/抽象信令(Session)
  4. 传输层

    • SRTP(安全的实时传输协议)
    • 多路复用技术
    • P2P STUN+TURN+ICE(点对点通信及网络穿透技术)
  5. 音频处理组件

    • 音频引擎
      • iSAC/iLBC 编解码器
      • NetEQ for Voice(网络音质增强)
      • 回声消除
      • 噪声抑制
    • 音频捕获/渲染
  6. 视频处理组件

    • 视频引擎
      • VP8 编解码器
      • 视频抖动缓冲
      • 图像增强
    • 视频捕获

这个架构图给出了 WebRTC 在实时通信中的应用,以及其涵盖的关键技术和组件。从顶层的直接应用到底层的网络传输,每个层次都包含了确保通信效率和质量所需的元素。

从上到下展示了从具体应用场景到底层网络传输的各个层级。在顶部,我们看到了三种典型的使用 WebRTC 技术的应用场景:视频会议、视频通话和远程教育。这些应用场景直接利用 WebRTC 提供的功能,如实时的视频和音频通信。

中间的层级是 WebRTC 的 API 接口,包括由 W3C 标准化的 Web API,以及 WebRTC C++ API(主要是 PeerConnection 接口)。这些 API 为开发者提供了建立和管理实时通信连接的手段。

接下来是会话管理和抽象信令(Session Management/Abstract Signaling),它是一个抽象层,处理如何建立、协调和结束通信会话。这个层级不包括具体的信令协议,因为 WebRTC 不指定信令协议,开发者需要根据应用需求自行实现。

在更底层,我们看到了传输层,包括了:

  • SRTP(Secure Real-Time Protocol):用于加密和传输实时的音频和视频数据。
  • Multiplexing:多路复用,可以在同一个连接上处理多个数据流。
  • P2P STUN+TURN+ICE:这些是网络穿透技术,帮助在不同网络环境下建立点对点连接。

最底层是网络 I/O,它是基础的输入输出处理层,负责音频和视频数据的捕获和渲染。

图中的两个“VideoEngine”和“Audio Capture/Render”模块指的是音视频处理的具体实现。在音频方面,包括了 iSAC/iLBC 编解码器、NetEQ for Voice(网络音频质量增强)、回声消除和噪声抑制等。在视频方面,包括了 VP8 编解码器、视频抖动缓冲和图像增强功能。这些组件共同工作,以确保通信的清晰度和稳定性。

WebRTC通讯流程的建立

WebRTC 是 P2P 的,意味着数据可以直接在用户之间传输,无需通过服务器中转。

虽然 WebRTC 设计为 P2P 通信,但实际上在建立连接之前通常需要中间服务器的辅助,主要是因为以下两个原因:

  1. NAT 穿越:

    • 处于局域网中的设备往往无法直接被互联网上的其他设备访问,因为它们拥有的是私有 IP 地址。NAT 技术允许多个设备共享一个公共 IP 地址,进行互联网通信,但这也使得直接的 P2P 连接变得复杂。
  2. 媒体格式协商:

    • 不同的设备可能支持不同的音视频编解码格式,因此在建立连接之前,需要确定双方都支持的媒体格式。

简单而言: 双方进行媒体数据的传输的前提需要知道对方的地址和传输的格式

WebRTC 的主要组件

为了解决这些问题,WebRTC 使用了以下几个组件:

  • 信令服务器:

    • 用于在建立连接之前,双方交换媒体协商信息(SDP)和网络信息(candidates)。
  • STUN 服务器:

    • STUN(Session Traversal Utilities for NAT)协议帮助设备发现它们的公共 IP 地址和端口,这对于实现 NAT 穿越至关重要。
  • TURN 服务器:

    • 当 STUN 无法解决 NAT 穿越问题时,TURN(Traversal Using Relays around NAT)协议提供了一种备选方案,通过中继服务器转发数据。
  • ICE 框架:

    • ICE(Interactive Connectivity Establishment)是一个框架,它尝试所有可能的方法(包括 STUN 和 TURN)来找到双方之间的最佳通信路径。

假设你和你的朋友(Peer-A 和 Peer-B)想要在各自的家里玩一款在线游戏,而游戏就是通过 WebRTC 进行的视频通话。但是你们需要先知道对方的地址和怎么过去,这就是信令服务器的作用,它就像是你们之间的邮递员。

  1. 准备邮件:首先,你(Peer-A)写了一封邮件(SDP offer),上面写着你家的地址和你想要玩游戏的方式。然后,你把这封邮件交给邮递员(信令服务器)。

  2. 邮递员的工作:邮递员拿到你的邮件后,会送到你朋友的家(Peer-B),告诉他你的游戏计划和地址。

  3. 回信:你的朋友收到邮件后,也写了一封回信(SDP answer),里面写着他的地址和他同意的游戏方式,然后同样交给邮递员。

  4. 确定路线:邮件中还包含了如何到达彼此家的多条路线(ICE candidates),就像给对方的地图。

  5. 直接联系:邮件往来几次后,你们就知道了对方的地址和如何到达,现在可以直接打电话(P2P 连接),开始游戏了。

  6. 如果路线不通:如果你们发现直接的路线走不通,邮递员会告诉你们一个中间地点(TURN 服务器),你们可以都先到这个地点集合,然后一起开始游戏。

所以,在这个比喻中,邮递员(信令服务器)帮助你们交换了地址和路线,但真正的游戏是你们直接进行的,邮递员不会参与游戏本身,就像在 WebRTC 中,媒体流是直接在对等方之间传输的。

WebRTC通话原理

WebRTC 通话的原理涉及到浏览器之间的直接连接,音视频数据的获取、传输和播放,以及必要的信令过程。以下是 WebRTC 通话的主要步骤和组成部分,它们共同作用以实现端到端的实时通信:

  1. 权限获取和媒体流捕获

    • 使用 getUserMedia API 获取用户的音频和视频权限,并捕获媒体流。
  2. 信令交换

    • 尽管 WebRTC 协议没有指定信令机制,但它是必须的。信令是用于交换信息的过程,以便建立和维护通信会话。通常,这包括交换两个对等端点的元数据,如网络配置信息、媒体格式等。
    • 信息交换通常包括 SDP(Session Description Protocol)数据,描述了媒体的详细信息。
  3. 网络穿越

    • 使用 ICE(Interactive Connectivity Establishment)框架来发现最佳的网络路径,包括处理 NAT 和防火墙。
    • 在这个过程中,STUN(Session Traversal Utilities for NAT)服务器被用来发现公网 IP 地址,TURN(Traversal Using Relays around NAT)服务器作为备用,以便在 P2P 失败时中继数据。
  4. 建立连接

    • RTCPeerConnection API 用于在两个端点之间建立一个连接。在这个连接上,ICE 候选(网络地址和端口)会被收集并交换。
    • 连接一旦建立,就可以开始传输数据。
  5. 媒体传输

    • 实时音视频流通过 RTP(Real-time Transport Protocol)传输。为了安全性,通常会使用 SRTP(Secure Real-time Transport Protocol)来加密传输。
    • RTCPeerConnection 管理音视频数据流的编码和解码。
  6. 数据通道

    • 如果需要,RTCDataChannel API 可以建立用于传输任意数据的通道,如文本消息或文件传输。
  7. 媒体播放

    • 在接收端,传入的媒体流将被解码,并通过 HTML 的 <video><audio> 标签播放。

这个过程涉及了复杂的后台操作,但对于开发者和用户来说,这些都被 WebRTC API 的抽象层所隐藏,使得建立一个实时通话应用变得相对简单。需要注意的是,信令过程需要通过某种外部机制来完成,这可能是 WebSocket、SIP 或其他任何可靠的消息传输服务。

如何实现一对一通信:

在实际应用中,用户 A 和用户 B 会通过信令服务器交换信息,然后创建各自的 PeerConnection 实例,并通过 getUserMedia 获取本地媒体流。在交换了必要的 SDP 和 ICE 信息后,WebRTC 会尝试建立一个 P2P 连接。一旦连接建立,双方就可以通过此连接直接传输媒体数据。

扩展到一对多/多对多通信:

对于一对多或多对多通信,可以使用 Mesh、MCU 或 SFU 架构:

  • Mesh:

    • 每个客户端都与其他所有客户端直接连接。随着参与者人数的增加,所需的连接数和带宽需求呈指数增长。
  • MCU:

    • 中央单元处理和混合所有传入的媒体流,然后将合成的流发送给所有参与者。这种方法在服务器端需要大量处理,但减少了客户端的带宽需求。
  • SFU:

    • 作为中继站,SFU 转发每个参与者的媒体流给所有其他参与者。与 MCU 相比,它不进行混流处理,客户端的带宽需求随着参与者的增加而线性增长。

在实现过程中,您可能需要搭建 STUN/TURN 服务(如使用开源的 coturn 服务器),并实现一个信令服务器来协调通信(可以使用 Node.js 和 WebSocket技术)。实现过程中,您可以使用 WebRTC API 来创建连接,获取和传输媒体流,以及进行必要的媒体和网络信息交换。

WebRTC 的具体实现和服务搭建过程

WebRTC 的具体实现和服务搭建过程可以分为几个主要部分:

搭建 STUN/TURN 服务器、搭建信令服务器,以及客户端代码实现。

1. 搭建 STUN/TURN 服务器

使用 coturn 可以同时搭建 STUN 和 TURN 服务。以下是一个简化的安装和配置过程:

安装 coturn(以 Ubuntu 为例):

sudo apt-get update
sudo apt-get install coturn

配置 coturn:
编辑配置文件 /etc/turnserver.conf,添加或修改以下配置:

listening-port=3478
fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=YOUR_SECRET
realm=your_realm
total-quota=100
bps-capacity=0
stale-nonce
no-loopback-peers
no-multicast-peers

启动 coturn 服务:

turnserver -c /etc/turnserver.conf -o

2. 搭建信令服务器

信令服务器通常使用 WebSocket 来实现。以下是使用 Node.js 和 ws 库的一个简单示例:

安装 ws:

npm install ws

创建 WebSocket 信令服务器 (server.js):

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);

    // 解析消息并根据消息类型处理
    // 这里只是简单地将接收到的消息发送给所有客户端
    wss.clients.forEach(function each(client) {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });
});

运行服务器:

node server.js

3. 客户端代码实现

客户端代码需要使用 WebRTC API 来建立连接和处理媒体流。

HTML (index.html):

<!DOCTYPE html>
<html>
<head>
  <title>WebRTC Example</title>
</head>
<body>
  <video id="localVideo" autoplay muted></video>
  <video id="remoteVideo" autoplay></video>

  <script src="client.js"></script>
</body>
</html>

JavaScript (client.js):

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');

let localStream;
let remoteStream;
let peerConnection;

// Specify STUN/TURN servers
const configuration = {
  iceServers: [
    {
      urls: 'stun:stun.example.com' // Replace with your STUN server URL
    },
    {
      urls: 'turn:turn.example.com', // Replace with your TURN server URL
      username: 'turn_username',
      credential: 'turn_password'
    }
  ]
};

// Create WebSocket connection to signaling server
const socket = new WebSocket('ws://localhost:3000');

socket.onmessage = function(event) {
  const message = JSON.parse(event.data);
  // Handle incoming messages...
};

// Get local media stream
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  .then(stream => {
    localVideo.srcObject = stream;
    localStream = stream;
  });

function call() {
  peerConnection = new RTCPeerConnection(configuration);
  localStream.getTracks().forEach(track => {
    peerConnection.addTrack(track, localStream);
  });

  // Send any ice candidates to the other peer
  peerConnection.onicecandidate = function(event) {
    if (event.candidate) {
      socket.send(JSON.stringify({'candidate': event.candidate}));
    }
  };

  // Once remote stream arrives, show it in the remote video element
  peerConnection.ontrack = function(event) {
    remoteVideo.srcObject = event.streams[0];
  };

  // Create offer
  peerConnection.createOffer().then(offer => {
    peerConnection.setLocalDescription(offer);
    socket.send(JSON.stringify({'offer': offer}));
  });
}

// Call this function to start a call
call();

确保在客户端代码中处理信令服务器发送的消息,例如交换 SDP 信息和 ICE 候选。