Skip to content

HTML5 - Web RTC

HTML5 - WebRTC (Web Real-Time Communication)

Section titled “HTML5 - WebRTC (Web Real-Time Communication)”

WebRTC(Web Real-Time Communication,网页实时通信)是一个开源项目和一套标准,由 W3C 和 IETF 支持,它通过简单的 JavaScript API 使实时通信(RTC)功能直接在网页浏览器和移动应用中成为可能。

它允许应用程序(如语音通话、视频聊天、屏幕共享以及直接数据/文件传输)进行点对点(P2P)通信,而无需用户安装插件或原生应用程序。

WebRTC 在现代浏览器中得到了广泛支持,包括桌面和移动平台上的 Chrome、Firefox、Safari 和 Edge。

WebRTC 功能主要通过三个核心 JavaScript API 暴露:

  • navigator.mediaDevices.getUserMedia(): 访问用户的摄像头和麦克风,以捕获音频和视频流(MediaStream)。
  • RTCPeerConnection: 在两个客户端之间建立和管理点对点连接,用于流式传输音频、视频和数据。
  • RTCDataChannel: 允许通过已建立的 RTCPeerConnection 在对等端之间直接发送任意数据。

MediaStream(访问摄像头和麦克风)

Section titled “MediaStream(访问摄像头和麦克风)”

getUserMedia() 方法(属于 MediaDevices API)会提示用户允许访问他们的媒体输入设备(如网络摄像头和麦克风)。如果获得许可,它会返回一个 Promise,该 Promise 解析后会得到一个 MediaStream 对象。

一个 MediaStream 代表一个媒体内容流,由一个或多个轨道(MediaStreamTrack)组成,例如来自麦克风的音频轨道或来自网络摄像头的视频轨道。

示例:访问和显示网络摄像头视频

Section titled “示例:访问和显示网络摄像头视频”
<!DOCTYPE html>
<html>
<head>
<title>getUserMedia Example</title>
<style>
video { max-width: 100%; border: 1px solid black; }
</style>
</head>
<body>
<h1>Webcam Feed</h1>
<video id="webcamVideo" autoplay playsinline></video>
<div id="status">Requesting camera access...</div>
<script>
const videoElement = document.getElementById('webcamVideo');
const statusDiv = document.getElementById('status');
async function startCamera() {
// Check if getUserMedia is supported
// 检查浏览器是否支持 getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
statusDiv.textContent = 'getUserMedia is not supported by your browser.';
return;
}
try {
// Define constraints: request video, audio is optional
// 定义约束条件:请求视频流,音频流可选
const constraints = {
video: true,
audio: false // Set to true to request microphone access too
// 设置为 true 也可以请求麦克风访问
};
// Request media stream
// 请求媒体流
const stream = await navigator.mediaDevices.getUserMedia(constraints);
statusDiv.textContent = 'Camera access granted.';
console.log('Received MediaStream:', stream);
// console.log('已接收 MediaStream:', stream);
// Attach the stream to the video element
// 将媒体流绑定到 video 元素上
videoElement.srcObject = stream;
// Optional: Log track information
// 可选:打印轨道信息
stream.getVideoTracks().forEach(track => {
console.log('Video Track:', track.label, track.getSettings());
// console.log('视频轨道:', track.label, track.getSettings());
});
stream.getAudioTracks().forEach(track => {
console.log('Audio Track:', track.label);
// console.log('音频轨道:', track.label);
});
} catch (error) {
statusDiv.textContent = `Error accessing media devices: ${error.name} - ${error.message}`;
// statusDiv.textContent = `访问媒体设备出错: ${error.name} - ${error.message}`;
console.error('Error accessing media devices.', error);
// console.error('访问媒体设备出错.', error);
}
}
startCamera();
</script>
</body>
</html>

RTCPeerConnection 是 WebRTC 的核心,它处理在两个对等端之间建立直接连接的复杂过程,即使它们位于 NAT 或防火墙之后。这涉及:

  • 信令(Signaling): 在对等端之间交换元数据以协调连接设置。这包括会话控制消息、网络配置(IP 地址、端口)和媒体能力(编解码器、分辨率)。WebRTC 没有定义信令协议。 开发者必须使用现有技术(如 WebSockets、SSE 甚至信鸽,尽管不推荐!)来实现自己的信令机制。
  • ICE (Interactive Connectivity Establishment,交互式连接建立): 一个框架,用于寻找连接对等端的最佳可能路径。这涉及收集连接候选者(本地 IP、通过 STUN 服务器获取的公共 IP、通过 TURN 服务器中继的 IP)并测试连通性。
  • STUN/TURN 服务器: 在 ICE 过程中使用的公共服务器。STUN (Session Traversal Utilities for NAT) 帮助发现公共 IP 地址。TURN (Traversal Using Relays around NAT) 在无法建立直接 P2P 连接时充当中继服务器。
  • SDP (Session Description Protocol,会话描述协议): 一种标准格式,用于描述在信令过程中交换的多媒体会话参数(编解码器、网络地址等)。

简化的连接流程(Offer/Answer 模型)

Section titled “简化的连接流程(Offer/Answer 模型)”
  1. 对等端 A(呼叫方): 创建一个 RTCPeerConnection 对象。
  2. 对等端 A: 使用 createOffer() 创建一个“提议”(Offer,描述其能力和期望会话的 SDP)。
  3. 对等端 A: 使用 setLocalDescription() 将提议设置为其本地描述。
  4. 对等端 A: 通过选择的信令通道将提议发送给对等端 B。
  5. 对等端 B(被呼叫方): 接收提议。
  6. 对等端 B: 创建一个 RTCPeerConnection 对象。
  7. 对等端 B: 使用 setRemoteDescription() 将接收到的提议设置为其远程描述。
  8. 对等端 B: 使用 createAnswer() 创建一个“应答”(Answer,响应提议的 SDP)。
  9. 对等端 B: 使用 setLocalDescription() 将应答设置为其本地描述。
  10. 对等端 B: 通过信令通道将应答发送回对等端 A。
  11. 对等端 A: 接收应答。
  12. 对等端 A: 使用 setRemoteDescription() 将接收到的应答设置为其远程描述。
  13. ICE 候选者交换: 在此过程中,双方对等端生成 ICE 候选者(潜在的连接路径),并使用 onicecandidate 事件处理程序和 addIceCandidate() 方法通过信令通道交换它们。
  14. 连接建立: 一旦找到并交换了兼容的 ICE 候选者,就会建立 P2P 连接,并且媒体/数据可以开始直接流动。

使用 addTrack() 或旧的 addStream() 方法可以将媒体流(来自 getUserMedia)添加到连接中。

一旦 RTCPeerConnection 建立,您就可以创建 RTCDataChannel 来在对等端之间直接发送任意数据(字符串、二进制数据、文件)。这绕过了服务器,提供了低延迟通信。

数据通道可以配置为可靠的(类似于 TCP)或不可靠且无序的(类似于 UDP),使其适用于从文件传输到实时游戏状态同步等各种用例。

创建和使用数据通道(概念示例)

Section titled “创建和使用数据通道(概念示例)”
// --- On the initiating peer (e.g., Peer A) ---
// --- 在发起连接的对等端(例如:对等端 A) ---
const peerConnectionA = new RTCPeerConnection(/* configuration */);
// Create a data channel BEFORE creating the offer
// 在创建提议(Offer)之前创建数据通道
const dataChannelA = peerConnectionA.createDataChannel("myDataChannel", { reliable: true });
dataChannelA.onopen = () => console.log("Data channel A open");
// dataChannelA.onopen = () => console.log("数据通道 A 已打开");
dataChannelA.onmessage = (event) => console.log("Msg from B:", event.data);
// dataChannelA.onmessage = (event) => console.log("来自 B 的消息:", event.data);
dataChannelA.onclose = () => console.log("Data channel A closed");
// dataChannelA.onclose = () => console.log("数据通道 A 已关闭");
// ... proceed with createOffer, setLocalDescription, signaling ...
// ... 继续执行 createOffer, setLocalDescription, 信令过程 ...
// --- On the receiving peer (e.g., Peer B) ---
// --- 在接收连接的对等端(例如:对等端 B) ---
const peerConnectionB = new RTCPeerConnection(/* configuration */);
// Listen for the data channel established by the other peer
// 监听对方建立的数据通道
peerConnectionB.ondatachannel = (event) => {
const dataChannelB = event.channel;
console.log("Data channel B received:", dataChannelB.label);
// console.log("数据通道 B 已接收:", dataChannelB.label);
dataChannelB.onopen = () => console.log("Data channel B open");
// dataChannelB.onopen = () => console.log("数据通道 B 已打开");
dataChannelB.onmessage = (event) => console.log("Msg from A:", event.data);
// dataChannelB.onmessage = (event) => console.log("来自 A 的消息:", event.data);
dataChannelB.onclose = () => console.log("Data channel B closed");
// dataChannelB.onclose = () => console.log("数据通道 B 已关闭");
// Example: Send a message back when open
// 示例:当通道打开时发送一条消息回去
dataChannelB.onopen = () => {
console.log("Data channel B open");
// console.log("数据通道 B 已打开");
dataChannelB.send("Hello from Peer B!");
// dataChannelB.send("来自对等端 B 的问候!");
};
};
// ... proceed with setRemoteDescription (offer), createAnswer, setLocalDescription, signaling ...
// ... 继续执行 setRemoteDescription (接收提议), createAnswer, setLocalDescription, 信令过程 ...

WebRTC 是一项复杂的技术。要深入了解和获取实际示例,请参考: