第 21 期 - WebRTC 前端实战:1v1 通话与多路通信架构
摘要
本文是 WebRTC 探索系列文章的下篇,首先展示 1v1 音视频通话 demo 效果并详细解析其功能,包括呼叫应答、媒体流调整、类 IM 功能等,还阐述了实际问题与解决方案,接着探讨 WebRTC 多路通信的三种架构及其优缺点、应用场景、注意事项等内容,最后总结了整个 WebRTC 探索之旅的成果与意义。
一、文章整体结构
文章围绕 WebRTC 技术展开,主要分为三大部分:1v1 音视频通话 demo 相关内容、WebRTC 多路通信架构以及对整个 WebRTC 探索之旅的总结。
(一)1v1 音视频通话 Demo
1. 演示效果
- 首先介绍了演示背景,在本地搭建信令服务器,用 Vue 框架开发用户页面,通过两个本地浏览器窗口模拟用户“转转”和“采货侠”进行视频通话。
- 展示了发起视频通话时的操作和画面,如“转转”发起通话,“采货侠”弹出授权弹窗,允许后进入通话,分别展示了本地和远端的画面。
- 还展示了通话中的消息发送与接收以及调整视频模式的操作和效果。
// 调整视频轨道的启用状态示例代码
senders.find((s) => s.track.kind === 'video').track.enabled =!send.track.enabled
2. 功能详解
- 呼叫端:
- 初始化
PeerConnection
对象,获取本地媒体流并添加到PeerConnection
,然后渲染到本地预览中。 - 创建并设置
offer
信令,通过信令服务器发送给被呼叫端。 - 以下是相关代码概要:
- 初始化
async initCallerInfo(callerId, calleeId) {
// 初始化 PeerConnection 对象
this.localRtcPc = new PeerConnection();
// 获取本地媒体流(音频和视频)
const localStream = await this.getLocalUserMedia({ audio: true, video: true });
// 将本地媒体流中的每个轨道添加到 PeerConnection
localStream.getTracks().forEach(track => this.localRtcPc.addTrack(track));
// 将本地媒体流渲染到预览 DOM 元素
await this.setDomVideoStream("localdemo", localStream);
// 初始化回调函数来处理各种事件
this.onPcEvent(this.localRtcPc, callerId, calleeId);
// 创建并设置 offer 信令
const offer = await this.localRtcPc.createOffer();
await this.localRtcPc.setLocalDescription(offer);
// 通过信令服务器将 offer 发送给被呼叫端
this.linkSocket.emit("offer", { targetUid: calleeId, userId: callerId, offer });
}
- 回调监听函数:
- 主要处理
PeerConnection
的各种事件,如创建数据通道、监听远程媒体流轨道、重新协商事件、ICE
候选的生成等。 - 代码示例:
- 主要处理
onPcEvent(pc, localUid, remoteUid) {
// 创建一个数据通道
that.channel = pc.createDataChannel('chat');
// 监听远程媒体流的轨道
pc.ontrack = function(event) {
that.setRemoteDomVideoStream('remoteVideo', event.track);
};
// 监听重新协商事件
pc.onnegotiationneeded = function(e) {
console.log('重新协商', e);
};
// 监听 ICE 候选的生成
pc.onicecandidate = function(event) {
if (event.candidate) {
// 通过信令服务器发送 ICE 候选信息
that.linkSocket.emit('candidate', {
targetUid: remoteUid,
userId: localUid,
candidate: event.candidate,
});
} else {
console.log('在此次协商中,没有更多的候选了');
}
};
}
- 被呼叫端:
- 接受呼叫端的
offer
并设置为remote desc
,创建应答信令,设置为local desc
后发送给呼叫端。 - 代码如下:
- 接受呼叫端的
async onRemoteOffer(fromUid, offer) {
// 接受呼叫端的 offer 并设置为 remote desc
await this.localRtcPc.setRemoteDescription(offer);
// 创建应答信令
const answer = await this.localRtcPc.createAnswer();
// 设置为 local desc
await this.localRtcPc.setLocalDescription(answer);
// 通过信令服务器将 answer 发送给呼叫端
this.linkSocket.emit("answer", { targetUid: fromUid, userId: getParams("userId"), answer });
}
- 通话过程中的媒体流变更:
- 可以使用
RTCRtpSender
对象控制音视频流的显示与否以及切换视频源。 - 代码示例:
- 可以使用
// 切换视频模式(音频模式/视频模式)
const senders = this.localRtcPc.getSenders();
// 找到视频发送方的信息
const send = senders.find(s => s.track.kind === 'video');
// 切换视频轨道的启用状态
send.track.enabled =!send.track.enabled;
// 屏幕分享/摄像头切换
const stream = await this.getShareMedia();
// 获取视频轨道
const [videoTrack] = stream.getVideoTracks();
// 找到视频类型发送方的信息
const send = senders.find(s => s.track.kind === 'video');
// 替换视频轨道
send.replaceTrack(videoTrack);
- 类 IM 实现:
- 使用
datachannel
实现P2P
文本传输,包括创建和监听datachannel
。 - 代码如下:
- 使用
// 创建数据通道
this.channel = pc.createDataChannel("my channel", {
protocol: "json",
ordered: true,
});
// 监听数据通道的事件
pc.ondatachannel = function(ev) {
console.log('Data channel is created!');
ev.channel.onopen = function() {
console.log('Data channel ------------open----------------');
};
ev.channel.onmessage = function(ev) {
console.log('Data channel ------------msg----------------');
};
};
3. 实际问题与解决方案
- 网络问题:
- 网络抖动和延迟:采用自适应比特率、丢包重传机制,利用
STUN/TURN
服务器提升NAT
穿透能力。
- 网络抖动和延迟:采用自适应比特率、丢包重传机制,利用
- 设备兼容性:
- 设备和浏览器多样性:进行广泛测试与优化,根据设备能力和网络状况动态调整视频分辨率和帧率,采用渐进增强的方式。
- 安全性:
- 采用端到端加密(
E2EE
)、强加密算法(如AES
和DTLS
),实现用户身份验证和权限控制。
- 采用端到端加密(
- 数据存储:
- 使用分布式数据库存储聊天记录,加密存储数据,定期备份数据并提供恢复机制。
(二)WebRTC 多路通信架构
1. 架构概述
- 在多对多通信场景中,常用的架构包括
Mesh
、SFU
和MCU
三种方式,每种架构在不同应用场景中有各自的优缺点,选择需综合考虑实际需求。
2. Mesh 架构
- 结构:每个参与者直接与其他所有参与者建立
P2P
连接,形成网状结构。 - 优点:无需服务器处理媒体流,节省带宽和服务器资源,实现相对简单。
- 缺点:随着参与者数量增加,带宽和处理负荷急剧增加,不适合大规模会议。
- 使用场景:小规模会议。
- 实际应用注意点:带宽限制和网络延迟,随着参与者增加带宽需求呈指数级增长,每个参与者的网络延迟会累积。
3. SFU 架构
- 结构:
SFU
架构的服务器仅作为媒体流的路由器,接收终端的音视频流后,根据需要转发给其他终端,不进行混流处理。 - 优点:节约带宽,扩展性强,适合大规模会议。
- 缺点:服务器压力大,所有媒体流的转发都在服务端进行,需要较高的服务器性能。
- 使用场景:中型会议、在线教育、直播等场景。
- 实际应用注意点:服务器性能和网络延迟,高并发情况下服务端可能成为瓶颈,服务器转发可能引入延迟。
- 常用工具与服务:
Mediasoup
(高性能的SFU
服务器,适合中大型会议)、SRS
(支持多种协议,适合灵活部署)。
4. MCU 架构
- 结构:
MCU
是一个中心化的服务器,接收来自各个终端的音视频流,在服务器端进行解码、混合和重新编码,生成统一的音视频流,再发送给所有参与通信的终端。 - 优点:客户端负载轻,适合大规模会议,提供统一视图。
- 缺点:服务器压力大,对服务器性能要求高,带宽需求高。
- 使用场景:大型会议、需要复杂处理的场景,如在线教育或企业培训。
- 实际应用注意点:服务器成本和处理延迟,高性能服务器成本较高,解码和混流过程可能引入延迟。
- 常用工具与服务:
Jitsi
(开源的MCU
解决方案,支持多方视频会议)、SRS
(适合需要灵活部署的小型MCU
场景)。
5. 实际应用案例分析
- 针对直播、多人会议、在线教育、远程协作等不同应用场景,分析了各自的技术点、需要注意的问题以及常用的工具与服务。
(三)总结
- 回顾整个系列文章,从前端开发者视角深入探讨了 WebRTC 的核心概念与应用场景,从 1v1 音视频通话扩展到多路通信架构,包括架构的应用与对比。
- 不仅了解了 WebRTC 的基本实现原理,还挖掘了实际生产环境中的挑战与解决方案,展示了 WebRTC 在不同场景下的广泛适用性,为实际项目中架构的选择和实施提供了指导。
扩展阅读
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有