Skip to content

WebRTC - 架构

WebRTC(Web Real-Time Communication,Web实时通信)的架构是多层次的,旨在无需插件即可在浏览器之间直接实现实时通信。理解其组成部分是有效利用其功能的关键。

原文教程包含一张图表,说明了WebRTC的层次。概念上,这可以分解如下:

  • Web开发者API层(Web Developer API Layer): 这是最顶层,向JavaScript开发者开放。它包括核心API,例如 RTCPeerConnection(对等连接)、RTCDataChannel(数据通道)和 MediaStream(媒体流,通过 navigator.mediaDevices.getUserMedia 获取)。
  • WebRTC C++ API层(浏览器内部实现 - Browser Internals): 在JavaScript API之下,浏览器实现了一个标准化的C++ API,用于将JavaScript调用桥接到下层的引擎。这一层主要供浏览器厂商使用。
  • 底层引擎和传输组件(Underlying Engines & Transport Components): 这一层包含几个关键组件:
  • * **语音引擎(Voice Engine)**: 处理音频,包括采集、编码/解码(Opus, G.711等)、回声消除、降噪和播放。
  • * **视频引擎(Video Engine)**: 管理视频处理,包括摄像头捕获、编码/解码(VP8, VP9, H.264, AV1等)、图像增强和渲染。
  • * **传输组件(Transport Components)**: 负责数据传输。这包括用于数据通道的SCTP(Stream Control Transmission Protocol,流控制传输协议)、用于媒体的SRTP(Secure Real-time Transport Protocol,安全实时传输协议)、用于保护数据通道和密钥交换的DTLS(Datagram Transport Layer Security,数据报传输层安全),以及用于NAT(Network Address Translation,网络地址转换)穿越和建立对等(peer-to-peer)连接的ICE(Interactive Connectivity Establishment,交互式连接建立)、STUN(Session Traversal Utilities for NAT,NAT会话穿越工具)和TURN(Traversal Using Relays around NAT,使用中继穿越NAT)。
  • * **会话管理(Session Management)**: 处理信令解释和连接的状态管理。

对于Web开发者而言,主要交互是与JavaScript API层进行。浏览器抽象了底层复杂性。

虽然WebRTC实现了对等(peer-to-peer)的媒体和数据交换,但要发起和管理连接,需要一个信令机制(通常涉及一个服务器)。两种常见的概念模型是:

1. SIP梯形模型(SIP Trapezoid Model,简单WebRTC中较少见)

Section titled “1. SIP梯形模型(SIP Trapezoid Model,简单WebRTC中较少见)”

原文教程展示了这种模型的图表。在这种模型中,两个对等方(用户A和用户B)可能使用由不同Web服务器(Web服务器A和Web服务器B)提供的Web应用。他们可能与一个中心信令服务器通信(或者甚至是可以互操作的独立信令服务器,例如使用SIP)。信令服务器帮助他们交换SDP(Session Description Protocol,会话描述协议)和ICE候选者(ICE candidates),以建立直接的 RTCPeerConnection。

这种模型更类似于传统的VoIP系统,但在基本的基于Web的WebRTC应用中较少见,因为用户通常连接到同一个应用服务器。

2. 三角模型(Triangle Model,WebRTC应用中常见)

Section titled “2. 三角模型(Triangle Model,WebRTC应用中常见)”

原文教程也描述了这种模型。在这里,两个对等方(用户A和用户B)连接到同一个Web应用服务器。该服务器也承载信令逻辑(例如,一个WebSocket服务器)。用户通过这个中心服务器互相发现并交换信令消息,然后服务器协助建立媒体/数据的直接对等 RTCPeerConnection。由于其简单性以及对用户管理和信令的集中控制,这在基于Web的WebRTC应用中是一种非常普遍的模式。

WebRTC JavaScript API由几个主要对象组成:

  • RTCPeerConnection: 用于建立和管理与对等方连接的核心组件。
  • MediaStream (和 MediaStreamTrack): 表示音频或视频数据流,通常通过 navigator.mediaDevices.getUserMedia() 从本地设备获取。
  • RTCDataChannel: 启用对等方之间任意数据的双向传输。

这个对象是WebRTC的主要入口点。它处理连接到对等方的复杂过程,包括NAT穿越、安全性、编解码器协商和媒体传输。它触发各种事件,通知应用程序连接的状态和生命周期(lifecycle)。

原文教程展示了一张图表,说明了关键的 RTCPeerConnection 事件,如 onicecandidate、ontrack(以前是 onaddstream),以及方法,如 createOffer、createAnswer、setLocalDescription、setRemoteDescription 和 addTrack(以前是 addStream)。

创建 RTCPeerConnection 非常直接:

const configuration = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
const peerConnection = new RTCPeerConnection(configuration);
// Example event handler for incoming tracks
peerConnection.ontrack = (event) => {
// const remoteVideo = document.getElementById('remoteVideo');
// remoteVideo.srcObject = event.streams[0];
console.log('Remote track received:', event.track);
};
// Example event handler for ICE candidates
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
// Send the candidate to the remote peer via signaling server
// signalingChannel.send({ candidate: event.candidate });
}
};

configuration 参数对于指定STUN/TURN服务器至关重要。

MediaStream API(navigator.mediaDevices.getUserMedia)

Section titled “MediaStream API(navigator.mediaDevices.getUserMedia)”

现代浏览器通过 navigator.mediaDevices.getUserMedia() 方法提供对本地摄像头和麦克风的访问。关键方面:

  • 它返回一个Promise(承诺),成功时解析为 MediaStream 对象。
  • 它需要用户权限才能访问设备。
  • 可以指定约束(constraints)以请求特定的分辨率、帧率或特定设备。

获取本地媒体流的示例:

async function getLocalMedia() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
// const localVideo = document.getElementById('localVideo');
// localVideo.srcObject = stream;
console.log('Local media stream obtained:', stream);
return stream;
} catch (error) {
console.error('Error accessing media devices.', error);
alert('Could not access camera/microphone: ' + error.message);
}
}
// To use it:
// getLocalMedia().then(localStream => {
// if (localStream) {
// // Add tracks to peerConnection, etc.
// localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
// }
// });

原文教程提供了类似的代码,但使用了较旧的、基于回调(callback)的 navigator.getUserMedia 和 video.src = window.URL.createObjectURL(stream)。现代方法使用Promise和 video.srcObject = stream。

请记住,getUserMedia(以及其他需要安全上下文的WebRTC API)通常仅适用于通过HTTPS或 localhost 提供的页面。

除了媒体,WebRTC还允许使用 RTCDataChannel 发送任意数据。这对于文本聊天、文件共享、游戏状态同步等非常有用。

数据通道是从现有的 RTCPeerConnection 创建的:

// Assuming 'peerConnection' is an existing RTCPeerConnection instance
// Initiator side:
const dataChannelOptions = { reliable: true, ordered: true };
const dataChannel = peerConnection.createDataChannel("myChannelLabel", dataChannelOptions);
dataChannel.onopen = () => console.log('Data channel open');
dataChannel.onmessage = (event) => console.log('Message received:', event.data);
dataChannel.onclose = () => console.log('Data channel closed');
dataChannel.onerror = (error) => console.error('Data channel error:', error);
// To send a message:
// if (dataChannel.readyState === 'open') {
// dataChannel.send('Hello from initiator!');
// }
// Receiver side (setup within peerConnection.ondatachannel event):
// peerConnection.ondatachannel = (event) => {
// const receiveChannel = event.channel;
// receiveChannel.onopen = () => console.log('Receive channel open');
// receiveChannel.onmessage = (event) => console.log('Message from peer:', event.data);
// // ... other handlers
// };

数据通道可以配置为可靠的(reliable)和有序的(ordered),模拟TCP的行为;或不可靠的(unreliable)和无序的(unordered),模拟UDP的行为。

本概述提供了对WebRTC架构及其核心JavaScript API的基础理解。这些组件在信令机制的协助下协同工作,使得在浏览器中直接构建强大的实时通信应用程序成为可能。随着WebRTC标准的不断发展,请始终参考最新的规范和浏览器文档,以获取最当前的信息。