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 API
Section titled “核心 WebRTC API”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(建立 P2P 连接)
Section titled “RTCPeerConnection(建立 P2P 连接)”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 模型)”- 对等端 A(呼叫方): 创建一个
RTCPeerConnection对象。 - 对等端 A: 使用
createOffer()创建一个“提议”(Offer,描述其能力和期望会话的 SDP)。 - 对等端 A: 使用
setLocalDescription()将提议设置为其本地描述。 - 对等端 A: 通过选择的信令通道将提议发送给对等端 B。
- 对等端 B(被呼叫方): 接收提议。
- 对等端 B: 创建一个
RTCPeerConnection对象。 - 对等端 B: 使用
setRemoteDescription()将接收到的提议设置为其远程描述。 - 对等端 B: 使用
createAnswer()创建一个“应答”(Answer,响应提议的 SDP)。 - 对等端 B: 使用
setLocalDescription()将应答设置为其本地描述。 - 对等端 B: 通过信令通道将应答发送回对等端 A。
- 对等端 A: 接收应答。
- 对等端 A: 使用
setRemoteDescription()将接收到的应答设置为其远程描述。 - ICE 候选者交换: 在此过程中,双方对等端生成 ICE 候选者(潜在的连接路径),并使用
onicecandidate事件处理程序和addIceCandidate()方法通过信令通道交换它们。 - 连接建立: 一旦找到并交换了兼容的 ICE 候选者,就会建立 P2P 连接,并且媒体/数据可以开始直接流动。
使用 addTrack() 或旧的 addStream() 方法可以将媒体流(来自 getUserMedia)添加到连接中。
RTCDataChannel(发送任意数据)
Section titled “RTCDataChannel(发送任意数据)”一旦 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 是一项复杂的技术。要深入了解和获取实际示例,请参考:
- MDN Web Docs - WebRTC API: (https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)
- WebRTC.org - Official Project Site: (https://webrtc.org/)
- Google WebRTC Codelab: (https://codelabs.developers.google.com/codelabs/webrtc-web)