WebRTC 入门指南:实时通信完全解析
WebRTC 入门指南:实时通信完全解析
简介
WebRTC(Web 实时通信)是一项强大的技术,支持浏览器和移动应用实时交换音视频与数据——无需中间服务器中转。它是现代视频通话、屏幕共享工具及实时协作平台的核心底层技术。
本文将完整覆盖 WebRTC 技术流程:从获取用户媒体到建立安全的点对点(P2P)连接,并提供基于 TypeScript 风格的 JavaScript 实战示例。
捕获媒体流
什么是媒体流(Media Stream)?
流(Stream)是连续的数据传输流——在 WebRTC 中,特指实时传输的音频或视频数据。
使用 getUserMedia 捕获音视频
通过 navigator.mediaDevices.getUserMedia() 方法可请求访问用户的麦克风和摄像头,示例如下:
const constraints = { audio: true, video: true }; // 配置:同时捕获音频和视频
navigator.mediaDevices
.getUserMedia(constraints)
.then((mediaStream) => {
console.log('成功获取媒体流:', mediaStream);
})
.catch((err) => {
console.error('获取媒体流失败:', err);
});
注意:浏览器会先弹出权限请求,仅在用户允许后才会提供音视频访问权限。
在 <video> 元素中显示视频
首先在 HTML 中定义用于显示视频的元素:
<video autoplay playsinline id="local-video"></video>
<!-- autoplay:自动播放;playsinline:在页面内播放(避免全屏) -->
再通过 JavaScript 将捕获到的媒体流绑定到视频元素:
const videoElement = document.getElementById('local-video') as HTMLVideoElement;
navigator.mediaDevices.getUserMedia({ video: true }) // 仅捕获视频
.then((stream) => {
videoElement.srcObject = stream; // 将媒体流赋值给视频元素
});
枚举与选择设备
枚举所有设备
通过以下方法可列出设备上所有可用的音视频输入/输出设备(如麦克风、摄像头、扬声器):
navigator.mediaDevices.enumerateDevices()
.then((devices) => {
devices.forEach((device) => {
console.log(`${device.kind}:${device.label}`);
// 示例输出:videoinput:USB 摄像头、audioinput:内置麦克风
});
});
监听设备变化
当有新设备(如外接摄像头)连接或设备断开时,可通过事件监听实时更新设备列表:
navigator.mediaDevices.addEventListener('devicechange', () => {
console.log('设备列表已更新!');
// 可在此处重新调用 enumerateDevices() 刷新设备列表
});
使用媒体约束(Media Constraints)
媒体约束允许精细化配置音视频参数,例如指定使用某台摄像头、设置视频分辨率或帧率:
const preferredDeviceId = 'abc123'; // 从 enumerateDevices() 获取的目标设备 ID
const constraints = {
video: {
deviceId: { exact: preferredDeviceId }, // 精确指定使用某台设备
width: { ideal: 1280 }, // 理想宽度:1280px
height: { ideal: 720 }, // 理想高度:720px(720P)
frameRate: { ideal: 30 }, // 理想帧率:30fps
},
audio: {
echoCancellation: true, // 开启回声消除
},
};
// 按约束条件获取媒体流
navigator.mediaDevices.getUserMedia(constraints);
捕获屏幕
通过 getDisplayMedia() 方法可捕获屏幕内容(如整个屏幕、特定窗口或应用),示例如下:
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true, // 屏幕捕获仅支持视频(无音频)
});
// 将屏幕流绑定到视频元素显示
document.querySelector('video').srcObject = screenStream;
注意:浏览器会弹出选择窗口,要求用户指定要捕获的屏幕、窗口或应用。
管理媒体轨道(Media Tracks)
每个媒体流(Stream)包含一个或多个轨道(Track),分别对应音频轨道或视频轨道。可对轨道进行单独禁用、停止等操作:
// 获取仅含视频的媒体流
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
// 提取流中的所有轨道
const tracks = stream.getTracks();
// 1. 禁用轨道(临时关闭,可重新启用)
tracks[0].enabled = false; // 禁用第一个轨道(此处为视频轨道)
// 2. 完全停止轨道(释放设备资源,无法恢复)
tracks.forEach((track) => track.stop());
建立对等连接(Peer Connection)
WebRTC 的核心是 RTCPeerConnection 接口,它负责在两个对等端(如两台设备)之间建立直接连接:
// 配置 ICE 服务器(用于穿透 NAT,建立 P2P 连接)
const config = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' } // 谷歌公共 STUN 服务器
],
};
// 创建对等连接实例
const peerConnection = new RTCPeerConnection(config);
ICE、STUN、TURN 概念解析
WebRTC 依赖以下三种技术实现对等端之间的网络连接:
- ICE(Interactive Connectivity Establishment,交互式连接建立):核心框架,负责寻找对等端之间可用的网络路径。
- STUN(Session Traversal Utilities for NAT,NAT 会话穿越工具):帮助设备发现自身在公网中的 IP 地址和端口(解决 NAT 遮挡问题)。
- TURN(Traversal Using Relays around NAT,通过中继穿越 NAT):当 P2P 直接连接失败时,作为中继服务器转发音视频数据(确保通信不中断)。
含 TURN 服务器的配置示例
const config = {
iceServers: [
// 优先使用 STUN 尝试直接连接
{ urls: ['stun:stun.l.google.com:19302'] },
// 备用 TURN 服务器(需自行部署或使用商业服务)
{
urls: 'turn:turn.example.com', // TURN 服务器地址
username: 'user', // 认证用户名
credential: 'pass' // 认证密码
},
],
};
ICE 候选者交换
ICE 候选者(ICE Candidate)是包含设备网络信息(如 IP、端口、传输协议)的数据,对等端需交换候选者才能找到可通信的路径。交换需通过信令服务器(如 WebSocket)完成:
发送 ICE 候选者(本地 → 远程)
// 监听本地 ICE 候选者生成事件
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
// 通过信令服务器将候选者发送给远程对等端
signalingServer.send('ice-candidate', event.candidate);
}
};
接收 ICE 候选者(远程 → 本地)
// 监听信令服务器的 ICE 候选者消息
signalingServer.on('ice-candidate', async (candidate) => {
// 将远程候选者添加到本地对等连接
await peerConnection.addIceCandidate(candidate);
});
提议/应答交换(SDP)
对等连接建立前,需交换 SDP(Session Description Protocol,会话描述协议)信息,用于协商媒体格式、编码方式等参数。交换过程分为“提议(Offer)”和“应答(Answer)”两步:
1. 发起方创建并发送提议
// 1. 创建提议(包含本地媒体配置)
const offer = await peerConnection.createOffer();
// 2. 将提议设置为本地描述(保存本地配置)
await peerConnection.setLocalDescription(offer);
// 3. 通过信令服务器发送提议给接收方
signalingServer.send('offer', offer);
2. 接收方处理提议并发送应答
// 1. 接收并设置远程描述(保存发起方的配置)
peerConnection.setRemoteDescription(offer).then(async () => {
// 2. 创建应答(包含接收方的媒体配置)
const answer = await peerConnection.createAnswer();
// 3. 将应答设置为本地描述(保存接收方配置)
await peerConnection.setLocalDescription(answer);
// 4. 通过信令服务器发送应答给发起方
signalingServer.send('answer', answer);
});
向连接添加本地媒体
将本地捕获的音视频流添加到对等连接,才能向远程对等端传输媒体:
// 1. 获取本地音视频流
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
// 2. 将流中的所有轨道添加到对等连接
stream.getTracks().forEach((track) => {
peerConnection.addTrack(track, stream);
});
接收远程媒体
监听 track 事件,获取远程对等端发送的媒体流并显示:
peerConnection.addEventListener('track', (event) => {
// 从事件中提取远程媒体流
const remoteStream = event.streams[0];
// 将远程流绑定到视频元素(显示对方画面)
document.getElementById('remote-video').srcObject = remoteStream;
});
动态管理轨道
连接建立后,可动态调整媒体轨道(如关闭麦克风、切换摄像头):
切换麦克风(禁用/启用音频轨道)
// 1. 找到音频轨道的发送器(Sender)
const audioSender = peerConnection
.getSenders()
.find((sender) => sender.track?.kind === 'audio');
// 2. 禁用/启用音频轨道
if (audioSender) audioSender.track.enabled = false; // 禁用(静音)
// if (audioSender) audioSender.track.enabled = true; // 启用(取消静音)
连接后添加新轨道
// 假设已获取新的视频轨道(如切换摄像头后的轨道)
const newVideoTrack = stream.getVideoTracks()[0];
// 将新轨道添加到对等连接
peerConnection.addTrack(newVideoTrack, stream);
关闭 WebRTC 连接
结束通信时,需停止所有媒体轨道并关闭对等连接,释放资源:
// 1. 停止所有发送器的轨道
peerConnection.getSenders().forEach((sender) => sender.track.stop());
// 2. 关闭对等连接
peerConnection.close();
群组通话:Mesh、SFU 与 MCU 对比
当通话参与者超过 2 人时,需选择合适的架构方案。以下是三种主流群组通话架构的对比:
Mesh 架构
- 原理:每个参与者直接与其他所有参与者建立 P2P 连接(如 3 人通话需建立 3 条连接)。
- 优点:架构简单,无需专用媒体服务器,延迟低。
- 缺点:参与者数量越多,CPU 占用和网络带宽消耗呈指数级增长(仅适合 4 人以下小规模通话)。
SFU(选择性转发单元)
- 原理:所有参与者将媒体流发送到 SFU 服务器,服务器不解码媒体,仅根据需求将流转发给其他参与者(如只转发说话者的流)。
- 优点:客户端负载低(仅需处理 1 条上传流和 N 条下载流),支持中等规模群组(20 人以内)。
- 主流方案:LiveKit、mediasoup、Mirotalk。
MCU(多点控制单元)
- 原理:所有参与者将媒体流发送到 MCU 服务器,服务器解码并混合所有流(如将多个人的画面合成一个分屏画面),再将混合后的单一流转发给所有参与者。
- 优点:客户端负载极低(仅需处理 1 条上传流和 1 条下载流)。
- 缺点:服务器解码/混合需大量计算资源,延迟较高,服务器成本高(适合大规模但对延迟不敏感的场景)。
总结
WebRTC 仅需几行 JavaScript 代码,就能实现实时音视频与数据传输。虽然入门简单,但它涉及 ICE、SDP、信令等深层技术概念。
无论你是想开发类似 Zoom 的视频会议工具,还是实时代码面试平台——理解如何捕获、发送和接收点对点媒体流,都是迈出的第一步。
扩展链接
SpreadJS+GCExcel全栈解决方案--协同编辑的框架搭建(三)
WebRTC 入门指南:实时通信完全解析的更多相关文章
- WebRTC 学习资源 电子书 WebRTC权威指南 Learning WebRTC
webRTC源码下载地址:https://pan.baidu.com/s/18CjClvAuz3B9oF33ngbJIw 提取码:wl1e 1.<WebRTC权威指南>第三版 中文版 本书 ...
- 【翻译】Fluent NHibernate介绍和入门指南
英文原文地址:https://github.com/jagregory/fluent-nhibernate/wiki/Getting-started 翻译原文地址:http://www.cnblogs ...
- 一起学微软Power BI系列-官方文档-入门指南(5)探索数据奥秘
我们几篇系列文章中,我们介绍了官方入门文档与获取数据等基本知识.今天继续给大家另外一个重点,探索数据奥秘.有了数据源,有了模型,下一步就是如何解析数据了.解析数据的过程需要很多综合技能,不仅仅是需要掌 ...
- Vue 入门指南 JS
Vue 入门指南 章节导航 英文:http://vuejs.org/guide/index.html 介绍 vue.js 是用来构建web应用接口的一个库 技术上,Vue.js 重点集中在MVVM模式 ...
- 【OpenCV入门指南】第一篇 安装OpenCV
http://blog.csdn.net/morewindows/article/details/8225783/ win10下vs2015配置Opencv3.1.0过程详解(转) http://ww ...
- Sublime Text 2入门指南
Sublime Text 2入门指南 一天在iteye上看到范凯介绍一个开发工具(TextMate ),看下面的评论时看到Sublime Text 2.其实我一直喜欢editplus.百度了一番才 ...
- 分布式消息系统Jafka入门指南之二
分布式消息系统Jafka入门指南之二 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 三.Jafka的文件夹结构 1.安装tree命令 $ sudo yu ...
- 【翻译Autofac的帮助文档】1.入门指南
[写在前面]尝试做完一件工作之外自我觉得有意义的一件事,那就从翻译Autofac的帮助文档吧. 入门指南 将Autofac集成你的应用程序的步骤通常很简单,一般是: 时刻以IOC(控制反转)的思想来规 ...
- OpenCASCADE入门指南
OpenCASCADE入门指南 eryar@163.com 一.概述 荀子说“君子性非异也,善假于物也”.当你会用英语,就可以与世界各国的人交流:当你会用编程语言,就可以与计算机交流:当你会用数学语言 ...
- dogse入门指南
dogse入门指南 Dogse作为游戏服务端引擎,目前只包含游戏服务端的核心部分,但这也是最核心的部分.它全部使用.net c#开发,充分兼顾了程序性能与代码编写的准确性与易用性,再加上以vs作为开发 ...
随机推荐
- 给Markdown渲染网页增加一个目录组件(Vite+Vditor+Handlebars)(上)
1 引言 在上一篇文章<解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)>中,我们通过设置域内CDN的方式解决Vditor加载Markdown网页很慢的问 ...
- 你应该懂的AI大模型(一) 之 浅知大模型
1.AI 大模型的训练过程 AI 大模型的训练就如同让一名孩童从不会说话一步步培养成高级知识分子或者专家的过程. 第一步:收集数据,将海量的知识与文章收集起来作为学习资料教给这个孩子: 第二步:预处理 ...
- Java编码小技巧
你在写一个方法的时候, 例如传入 两个数组,而你要写的方法代码块又恰好有一种判断方式会导致你要写两个相同代码块, 你就可以自己调用自己,并把传参顺序 换一下 public int[] intersec ...
- 三种AI人机交互系统的记忆模块对比:小智、OPEN-LLM-VTUBER和MaiBot
MaiBot 的记忆和情感系统 https://github.com/MaiM-with-u/MaiBot 记忆系统 MaiBot 拥有最复杂和完整的记忆系统: 海马体记忆系统 (Hippocampu ...
- SQL Server 链接服务器"XXXXXXX"的 OLE DB 访问接口 "SQLNCLI11" 返回了消息 "没有活动事务。"。
一.确保互联双方服务器MS DTC服务已启动并正确配置 打开"服务"管理控制台(services.msc) 找到"Distributed Transaction Coor ...
- windows10搭建gitlab服务器
gitlab服务器没有win运行环境,所以需要先搭建个虚拟机:windows10+VMware(Centos7) +gitlab+局域网其他电脑访问 一:VMware 安装,网上找找有安装教程 二:虚 ...
- pg 多列合并一列
下面是正常的查询多条数据,如果我要合并成列怎么办. 其实pg有自带的array_to_string函数 ,但是写法稍微有点麻烦,但是可以根据array_to_string函数自定义一个函数去简化写 ...
- 【闲话 No.6】 Lyndon 串与 runs 相关
约定 有些约定可能并没有被广泛认可,但是为了行文需要还是写在这里,请您理解. 在下文中,如无特殊说明,我们用 \(n\) 表示问题规模大小,比如任何字符串的大小.所有数组下标从 \(1\) 开始. 我 ...
- 100条常用SQL语句
一.基本查询语句 查询所有数据: SELECT * FROM 表名; 查询特定列: SELECT 列名1, 列名2 FROM 表名; 条件查询: SELECT * FROM 表名 WHERE 条件; ...
- bge-large-zh-v1.5 和 bge-reranker-large模型有什么区别和联系
BGE(BAAI General Embedding)系列模型是智源研究院开发的高性能语义表征工具,其中bge-large-zh-v1.5和bge-reranker-large是两类不同功能的模型.它 ...