如何搭多人会议系统(SFU 架构 / 多路流管理)

如何搭多人会议系统(SFU 架构 / 多路流管理)

💡 原文中文,约9700字,阅读约需23分钟。
📝

内容提要

多人会议系统搭建:3人以下用Mesh全互联,3人以上用SFU架构,每人上行1路,服务器只转发不重编码,下行按需订阅。服务端用Node.js+Socket.io实现信令、房间与流管理,处理断线重连、竞态和空房间回收。客户端管理多路流,按会议规模选择全订阅、活跃订阅或分页策略。大画面支持声控、固定、手动切换,声控需加防抖与迟滞。

🔎

延伸解读

架构选型:Mesh 与 SFU 的适用边界

文章指出,3 人以下会议适合 Mesh 全互联,因为每人上行 N-1 路,3 人时上行 2 路尚可接受;但 6 人时上行 5 路,手机带宽和 CPU 压力大,容易卡顿。3 人以上应选择 SFU,每人只推 1 路上行,服务器转发不重编码,下行按需订阅,扩展性更好。MCU 虽然下行只需 1 路,但服务器需解码重编码,成本极高,仅适合端侧能力极弱或需要服务端合成录制的场景。

服务端设计:信令与媒体分离,重点处理竞态和重连

服务端采用 Node.js + Socket.io 实现信令层,负责房间管理、流管理和转发管理,媒体数据则通过 RTP/SRTP 传输,两者分离是核心设计。代码示例中,加入房间时回传 room-state 包含所有参与者和已发布流,避免新人看不到老人后发布的流;断线重连时根据 room-state 重建订阅关系,防止订阅丢失;空房间自动回收,避免内存泄漏。这些细节是保证会议稳定性的关键。

客户端订阅策略:按会议规模动态调整

客户端需要管理 1 路上行和 N 路下行。文章给出三种订阅策略:小会议全订阅(SUBSCRIBE_ALL),适合 9 人以下;大会议只订阅活跃说话者加最近 N 人(SUBSCRIBE_ACTIVE),并在订阅新流时淘汰最不活跃的流,控制下行带宽;超大会议分页订阅(SUBSCRIBE_PAGED),每页 9 人,用户翻页才订阅。同时,未显示的流不渲染,避免 9 宫格卡顿和手机发烫。

大画面切换:声控需防抖与迟滞,避免频繁跳变

大画面切换有三种模式:声控、固定主讲人、手动切换。声控模式最复杂,需要处理音频能量波动。文章建议加入防抖和迟滞:至少持续说话 0.5 秒才切换,且新说话人能量需高于当前说话人 1.5 倍才触发切换。这样能避免画面每秒切换多次,提升会议体验。此外,多人会议回声问题需开启 AEC,扬声器场景强制使用耳机。

Q&A

多人会议系统应该选 Mesh 还是 SFU 架构?

3 人以下用 Mesh 全互联省成本,3 人以上用 SFU 是标准答案。Mesh 每人上行 N-1 路,6 人就崩;SFU 每人只推 1 路到服务器,服务器转发不重编码,下行按需订阅。MCU 只在端侧能力极弱或需要服务端录制合成画面时用。

SFU 服务端的信令层需要处理哪些关键问题?

需要处理:断线重连(重连后恢复订阅关系)、竞态(A 加入时 B 还没 publish,B 之后 publish 要广播给 A)、房间清理(空房间自动回收),以及状态同步(新加入者收到当前房间所有参与者列表)。

客户端如何管理多路流订阅以控制下行带宽?

根据会议规模选择策略:小会议全订阅(SUBSCRIBE_ALL),大会议只订阅说话的人加最近 N 人(SUBSCRIBE_ACTIVE),超大会议分页每页 9 人(SUBSCRIBE_PAGED)。说话触发订阅时,同时取消订阅最不活跃的一个,控制下行带宽。

大画面切换有哪几种模式?声控切换如何防抖?

三种模式:声控(谁说话谁放大)、固定(主讲人锁定)、手动切换。声控防抖需加最小说话时长(如 0.5 秒)和迟滞(新说话人能量需高于当前说话人 1.5 倍才切换),避免画面疯狂切换。

多人会议系统常见的踩坑有哪些?

常见问题:新人看不到老人后加入的流(join 时没回传 room-state);断线重连后订阅丢失(disconnect 只清 socket 没清订阅);大画面疯狂切换(能量检测无防抖/迟滞);9 宫格卡爆(默认全订阅+全渲染);房间泄漏(空房间不回收);回声严重(多人用扬声器外放,需开启 AEC 并强制耳机)。

SFU 架构中上行和下行带宽分别是多少?

SFU 架构中,每人上行固定 1 路,下行按需订阅(只订阅需要的几路)。相比 Mesh 的上行 N-1 路、下行 N-1 路,SFU 大幅节省带宽,扩展性更好。

🏷️

标签

➡️

继续阅读