谷歌屏幕共享功能别浪费:我把它改成了视频会议,结果人多就炸了
本文分享了将闲置的浏览器推流代码改造为视频会议功能的实践过程。作者最初采用WebRTC P2P Mesh架构,发现其在小规模会议(4人以内)具有零服务器成本、低延迟等优势,但随着人数增加会面临带宽和计算资源瓶颈(6人时上传带宽达10Mbps,10人时需解码10路视频)。通过对比Mesh与SFU架构的差异,作者最终保留Mesh模式但限制最大人数(6人),并为更大规模会议推荐SFU方案。文章揭示了技术
一、废物利用的灵感
上两篇文章讲了直播架构选型和浏览器推流的坑,过程中我手里攒了几个“废品”:
-
浏览器摄像头采集的代码(本来想用于推流,结果码率被卡死)
-
getDisplayMedia屏幕共享的代码(同样因为码率问题,推流画质拉胯) -
一个能跑通的 WebRTC 连接(虽然推流不行,但连接建立没问题)
扔了可惜,于是我冒出个想法:
不如把这些改成视频会议功能?
逻辑上好像完全说得通:
-
直播是“一对多”,一个主播推流给所有人看
-
会议是“多对多”,每个人都能发言,互相都能看到
-
getUserMedia和getDisplayMedia本来就更适合会议场景(共享PPT、代码演示)
说干就干。
二、架构选择:P2P Mesh,简单直接
既然不是直播了,而是小团队内部开会,我第一反应就是最简单粗暴的方案——WebRTC P2P Mesh 组网。
Mesh 是什么?
text
用户A
/ | \
/ | \
B C D
/ \ / \ / \
E F G H
每个参会者都和其余所有人建立一对一的 WebRTC 连接。A 把自己的音视频推给 B、C、D,同时接收 B、C、D 推过来的音视频。
为什么一开始选 Mesh?
-
零服务器成本:不需要流媒体转发服务器,STUN/TURN 打洞就行
-
逻辑简单:每个客户端维护 N-1 个 RTCPeerConnection,代码很好写
-
延迟极低:数据直传,不经过任何中转
代码实现也不复杂:
javascript
// 伪代码:Mesh 模式下,每个新加入的成员都和所有人建立连接
function onNewParticipant(newPeerId) {
const pc = new RTCPeerConnection(config);
// 把自己的流加到连接里
localStream.getTracks().forEach(track => pc.addTrack(track, localStream));
// 创建 offer,通过信令服务器发给对方
pc.createOffer().then(offer => {
pc.setLocalDescription(offer);
signaling.send(newPeerId, { type: 'offer', sdp: offer.sdp });
});
// 收到对方的音视频
pc.ontrack = (event) => {
addRemoteVideo(newPeerId, event.streams[0]);
};
}
两个人测试:完美。
三个人测试:没问题。
四个人的时候——
画面开始卡了,风扇开始转了。
三、Mesh 的带宽灾难
我以为写代码的时候已经理解了 Mesh 的问题,但真正跑起来,那个效果还是超出了预期。
算一笔账
假设4个人开会,每人推一路 720P 的视频,码率大约 2Mbps。
在 Mesh 模式下,每个人要处理:
| 方向 | 带宽计算 | 数值 |
|---|---|---|
| 上传 | 把自己的视频推给另外3个人 | 2Mbps × 3 = 6Mbps |
| 下载 | 接收另外3个人的视频 | 2Mbps × 3 = 6Mbps |
| 编解码 | 编码自己1路 + 解码对方3路 | 总共处理4路视频 |
4个人还能撑住。
6个人呢?
| 方向 | 带宽计算 | 数值 |
|---|---|---|
| 上传 | 推给另外5个人 | 2Mbps × 5 = 10Mbps |
| 下载 | 接收另外5个人的视频 | 2Mbps × 5 = 10Mbps |
| 编解码 | 编码1路 + 解码5路 | 总共处理6路视频 |
家用宽带的上行带宽通常在 20-50Mbps,10Mbps 还在范围内。
10个人呢?
| 方向 | 带宽计算 | 数值 |
|---|---|---|
| 上传 | 推给另外9个人 | 2Mbps × 9 = 18Mbps |
| 下载 | 接收另外9个人的视频 | 2Mbps × 9 = 18Mbps |
| 编解码 | 编码1路 + 解码9路 | 总共处理10路视频 |
部分用户的上行已经吃紧了。而且10路视频的解码,CPU 开始冒烟了。
30人呢?
| 方向 | 带宽计算 | 数值 |
|---|---|---|
| 上传 | 推给另外29个人 | 2Mbps × 29 = 58Mbps |
| 下载 | 接收另外29个人的视频 | 2Mbps × 29 = 58Mbps |
| 编解码 | 编码1路 + 解码29路 | 根本不现实 |
到这一步,别说带宽不够,光解码30路视频,大部分电脑就直接死机了。
这还没算屏幕共享
如果有人在分享屏幕,那更惨。屏幕共享的码率通常比摄像头高得多(静态内容还好,一旦有动画或者滚动,码率直接起飞)。
5个人开会,其中1个人分享屏幕,2Mbps摄像头 + 5Mbps屏幕共享 = 7Mbps上传,推给4个人就是 28Mbps。
参会者的电脑:解码4路摄像头 + 1路屏幕共享,桌面端勉强扛得住,笔记本风扇已经开始起飞了。
四、为什么 Zoom、腾讯会议不用 Mesh?
这时候我突然意识到一个问题:Zoom 支持1000人同时在线的大型会议,按 Mesh 的算法,每个人需要推 999 份视频流。这根本不可能。
它们用的其实不是 Mesh,而是一种叫 SFU(Selective Forwarding Unit,选择性转发单元)的架构。
text
用户A──┐
用户B──┼──→ SFU服务器 ──→ 用户A、B、C、D...
用户C──┤ (按需转发)
用户D──┘
在 SFU 模式下:
-
每个人只把自己的视频推1份给服务器
-
服务器根据每个人的需求(比如只看发言人、只看固定几个人),选择性转发给每个人
-
每个人只需要上传1份,下载也只接收自己需要的那几路
同样的30人会议,SFU模式下的带宽:
| 方向 | 带宽 | 说明 |
|---|---|---|
| 上传 | 2Mbps × 1 = 2Mbps | 只推1份给服务器 |
| 下载 | 服务器转发来的几路 | 取决于你同时看几个人 |
从58Mbps降到2Mbps,这就是 SFU 的威力。
Mesh vs SFU 对比
| 维度 | Mesh | SFU |
|---|---|---|
| 架构 | 端到端直连 | 服务器中转 |
| 服务器成本 | 几乎为零(只需信令) | 需要中转服务器 |
| 上行带宽 | N-1 倍 | 1 倍 |
| 下载带宽 | N-1 倍 | 可控 |
| 编解码压力 | 解码 N-1 路 | 可控 |
| 延迟 | 极低(直连) | 稍高(经服务器) |
| 适用人数 | ≤4人 | 几十到上千人 |
五、我把 Mesh 保留下来了,但加了人数限制
踩完坑之后,我没完全推倒重来。Mesh 有它的优点——简单、零成本、延迟极低——对于4人以内的小团队会议,其实完全够用。
于是我做了一个折中:
-
保留 Mesh 模式,但在页面上加了一个人数上限:最多6人
-
超过6人时,提示用户切换到直播模式(SRS转发),或者对接 SFU 方案
-
屏幕共享功能保留,但只在2-3人小会时推荐使用
代码里加了一个简单的检测:
javascript
const MESH_MAX_PARTICIPANTS = 6;
function onNewParticipant(peerId) {
const currentCount = Object.keys(peers).length;
if (currentCount >= MESH_MAX_PARTICIPANTS) {
console.warn(`Mesh 模式最多支持 ${MESH_MAX_PARTICIPANTS} 人,当前 ${currentCount} 人,建议切换模式`);
// 可以自动切换到仅接收模式,或者提示用户
return;
}
// 正常建立 Mesh 连接...
}
六、总结:Mesh 的正确打开方式
这次折腾让我搞清楚了一件事:Mesh 不是不能用,而是要知道它的边界。
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 2-4人小会 | Mesh | 零成本,延迟最低 |
| 5-10人会议 | SFU(自建或第三方) | 带宽可控 |
| 10人以上 | SFU 或直播模式 | Mesh 根本不现实 |
| 大型会议/发布会 | 直播模式(SRS + RTMP/FLV) | 一对多广播 |
技术选型这件事,永远没有绝对的“最好”,只有“在某个场景下最合适”。
我把屏幕共享的代码保下来没浪费,改成了小团队会议功能,顺便摸清了 Mesh 的极限在哪里——这个收获,比最初只想搞浏览器推流的时候大多了。
三篇文章串起来,刚好是一条完整的技术探索路线:
-
第一篇文章:直播架构选型,为什么用 SRS 而不是 WebRTC P2P
-
第二篇文章:浏览器推流踩坑,Windows 硬编码码率被卡的死死的
-
第三篇文章:把闲置的屏幕共享改成会议,踩了 Mesh 的带宽坑
从直播到会议,从推流到组网,这些坑踩了一遍,基本上把 WebRTC 在实际项目里的边界都摸清楚了。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)