无纸化会议系统实战:WebRTC与RTSP流媒体接入的全流程解析
做过会议系统的人都知道,真正让会议室“无纸化”的难点,从来不是省那几包A4纸,而是怎么把桌面上的文件、白板上的笔迹、摄像头里的画面,在所有人的屏幕前保持同步、低延迟地流转起来。我最近用SmartMediaKit做了一套轻量级的无纸化会议系统,核心功能就三块:跨平台同屏、摄像头接入采集、全程录像留存。整套方案跑下来,效果超出预期,所以把从选型到落地的完整过程整理出来。如果你是做会议系统、远程协作工具、或者想把传统会议室改造成数字化场景,这篇文章应该能帮你少走不少弯路。
1. 项目整体设计与技术路线选型
1.1 无纸化会议系统的需求清单
接手这个项目时,客户提的需求看着很简单:开会不用打印材料、屏幕上能同时看到主讲人的电脑画面和会议室摄像头画面、会议结束之后能回看录像。但真开始设计系统,才发现每一条背后都藏着不少细节。
先梳理一下真实场景下的需求清单。会前阶段,需要把会议材料(PPT、Word、PDF)统一上传并分发到所有参会端,替代纸质打印。会中阶段,主讲人要把自己的电脑屏幕共享给所有参会者,这个过程要跨平台——Windows笔记本、Mac、Android平板、iOS手机,甚至会议室里那台老掉牙的Windows触控一体机都得能看。同时还要把会议室顶部的摄像头画面接入进来,让远程参会的人能看到现场情况。会后阶段,系统要把整个会议过程录制下来,按会议ID归档,支持回放。
这里特别容易踩的坑是把“同屏”理解成简单的“投屏”。投屏是一对一的镜像显示,而会议同屏本质上是一对多的实时媒体分发,还伴随互动需求,比如主持人可以切换当前展示的屏幕、参会者可以标注重点。所以底层不能只靠Miracast或者AirPlay这种点对点协议,必须自己搭一套媒体分发链路。
1.2 为什么选SmartMediaKit:选型对比与取舍
媒体服务器是这套系统的核心,我先后对比了几种方案。
自研流媒体服务这条路最重。要用Go或C++从零实现RTSP推拉流、HLS切片、WebRTC转发,没有两三个月出不来稳定版本,而且后面维护成本极高,不适合这种中短期交付项目。
SRS和ZLMediaKit都是很成熟的流媒体服务器,功能全面,社区活跃。但在这个项目里有个共同问题:它们更侧重“流媒体分发”本身,对WebRTC的接入支持要么需要单独打通,要么配置比较繁琐。而我们的会议终端以Web页面为主,同屏和摄像头画面最终都要通过WebRTC推给浏览器,这中间的协议转换和信令衔接工作量不小。
最后选了SmartMediaKit(SMS),主要是看中它把多种能力集成在了一个服务里:支持RTSP/RTMP拉流,支持把流转成WebRTC、HTTP-FLV、HLS等输出协议,同时自带录制功能,还提供了Web管理接口,方便做二次开发。这意味着我可以让摄像头推RTSP流进来,由SmartMediaKit统一转成WebRTC分发给浏览器端,同时开启录制把流落盘,整个媒体链路由一个中间件扛下来,我只需要关注业务逻辑。
选型这件事我的经验是:不要迷信功能最全的平台,而要选“刚好覆盖你核心链路”的方案。我们的核心链路是“摄像头RTSP入流 → 转WebRTC出流 → 服务端录制”,SmartMediaKit在这条链路上几乎零成本打通,这就是它胜出的原因。
1.3 整体架构与数据流设计
系统采用前后端分离的架构,整体分四层。
接入层负责各种终端的Web页面加载和信令交互,通过HTTPS/WSS提供安全连接。业务层部署在主控Node.js服务上,负责会议管理、参会者权限控制、同屏画面切换指令、摄像头云台控制指令转发。媒体层就是SmartMediaKit,负责所有音视频流的接入、转发、录制。存储层用MySQL存会议和用户信息,用本地磁盘或NAS做录像文件的持久化。
会议过程中的数据流是这样的:主讲人浏览器通过WebRTC将屏幕流推送到SmartMediaKit,SmartMediaKit作为SFU角色把流转发给房间内其他参会者;会议摄像头的RTSP流由SmartMediaKit主动拉取,拉回来后统一转成WebRTC分发给所有在线端;同时SmartMediaKit把进入服务的每路流都做一份录制切片,写入存储层。
这个架构的好处是信令和媒体彻底分离。业务服务器只做控制和状态同步,所有高并发的媒体流量都在SmartMediaKit内部消化,不容易把业务服务拖垮。后续如果会上几百号人,也可以横向扩展多个SmartMediaKit实例,用负载均衡把不同会议房间分散到不同实例上。
2. 跨平台同屏方案:从屏幕捕获到多端观看
2.1 同屏技术路线:WebRTC为主,协议转译为辅
同屏功能我选了WebRTC作为核心技术。原因很简单:WebRTC是浏览器原生支持的实时通信方案,不需要观众端安装任何插件,Windows、macOS、Android、iOS的主流浏览器统统覆盖,正好契合“跨平台”这个硬性要求。
具体流程是:主讲人打开同屏页面,浏览器调用getDisplayMedia接口捕获整个屏幕或指定窗口,然后通过RTCPeerConnection将视频流推送到SmartMediaKit。SmartMediaKit接收到WebRTC流之后,把它转换成其他协议分发给不同终端,或者直接通过WebRTC转发给其他Web端。
这中间要处理好几个关键点。一是分辨率控制,屏幕捕获的分辨率可能高达4K,直接推流会占用大量带宽和CPU。我在前端做了约束,默认采集1080p,如果检测到CPU占用过高,自动降到720p。二是码率自适应,SmartMediaKit支持WebRTC的带宽估计机制,网络抖动时自动降码率,避免画面卡死。三是声道处理,屏幕共享的音频如果没做回声消除,远程端会听到刺耳的啸叫,我在采集约束里强制开启了echoCancellation和noiseSuppression。
如果遇到浏览器不兼容getDisplayMedia的情况——比如某些定制版Chromium内核的会议平板——我配置了降级方案:SmartMediaKit把主讲人的WebRTC流转成HTTP-FLV,平板端通过flv.js播放。这样即使设备不支持标准屏幕捕获,也能通过拉流方式看到共享画面。
2.2 SmartMediaKit在同屏链路中的角色配置
SmartMediaKit在同屏链路中承担两个角色:接收端和转发端。接收端接收主讲人的WebRTC推流,转发端负责把流做成多协议输出给不同终端。这里分享几个关键的配置经验。
首先是WebRTC推流的鉴权配置。SmartMediaKit默认允许任意客户端推流,这在内网测试没问题,一放到公网就是灾难,任何人都能往你的服务上推垃圾流。我通过配置文件开启了推流鉴权,在WebRTC推流的URL后面附加token参数,SmartMediaKit校验通过才接受推流。token由业务服务颁发,和会议ID绑定,过期时间设置成会议时长。
其次是流的命名规范。同屏流的ID我用
meeting_{会议ID}_screen_{主讲人ID}
这种格式,这样在SmartMediaKit的管理接口里一眼就能分辨出是哪场会议的哪路流,排查问题的时候效率极高。摄像头流的命名类似,用
meeting_{会议ID}_camera_{摄像头编号}
。
然后是端口和防火墙的配置。SmartMediaKit处理WebRTC需要开放UDP端口范围,我一开始只开了TCP端口,结果始终收不到视频流,检查日志发现是ICE协商失败。后来在防火墙上放行了10000到20000的UDP端口范围,这个问题立刻解决。如果部署在云服务器上,记得同步在安全组里配置对应规则。
2.3 多端适配与体验调优
多端适配是这种跨平台系统最磨人的部分。我实测覆盖了Chrome、Edge、Safari、Firefox,以及Android端的微信内置浏览器和iOS端的Safari,逐个踩了一遍坑。
最大的坑在iOS端。iOS的Safari对WebRTC的支持相对保守,同一个页面在PC上一切正常,到iPhone上就是只闻其声不见其人。排查下来发现是iOS Safari对视频轨道的分辨率切换支持不够好,当屏幕共享流和摄像头流同时渲染时,偶尔会导致画面黑屏。我的解决方案是把两路视频放在独立的video元素里渲染,不共用同一个播放容器,同时对视频轨道做降级处理——在iOS上把默认分辨率降到720p,果然稳定了很多。
Android端的问题主要是浏览器碎片化。某些国产浏览器内核老旧,不支持标准的RTCPeerConnection,我通过特性检测做了统一降级:检测到window.RTCPeerConnection不存在时,直接走HTTP-FLV播放路径。这样虽然失去了WebRTC的低延迟优势,但至少能正常看到画面。
参会者体验上还有一个细节容易被忽略:画面布局权限。主持人、主讲人、普通参会者的界面布局是分开的,主持人可以拖拽窗口布局、切换主讲人画面到大屏,普通参会者只能看不能操作。这个权限控制通过业务层在下发配置时做区分,媒体层不关心业务权限,只负责按需拉流和转发,整个体系才能保持简洁。
3. 摄像头接入:把RTSP摄像头变成Web会议画面
3.1 摄像头接入的协议选型与前置处理
无纸化会议系统里的摄像头接入,和一般的视频监控项目还不一样。监控只需要把RTSP流拉到NVR存储就行,但会议系统要求的是“实时的、低延迟的、可供浏览器直接播放的画面”。这就带来一个基础问题:浏览器不能直接播放RTSP流。
摄像头的主流协议是RTSP,传输H.264/H.265编码的视频。如果让每个Web客户端都直接去拉RTSP流,一方面浏览器不支持,另一方面也不安全——RTSP的鉴权机制简单,摄像头容易被暴力破解。所以标准的做法是把摄像头RTSP流先推到SmartMediaKit,由它转成WebRTC或者HTTP-FLV分发给客户端。
我用的摄像头主要是海康和萤石,都在ONVIF协议框架下。接入前需要先通过ONVIF获取摄像头的媒体配置文件,确认分辨率、帧率、编码格式。这里有个关键点:会议场景建议用子码流预览、主码流录制。我配置了两路拉流,预览走子码流(720p)保证低延迟,录像走主码流(1080p)保证清晰度。SmartMediaKit支持对同一路摄像头配置多个拉流任务,分别指向主码流和子码流的RTSP地址即可。
3.2 SmartMediaKit拉转推配置详解
摄像头的RTSP拉流在SmartMediaKit里配置并不复杂,关键是几个参数的调优。
参考配置大致是这样的:在SmartMediaKit的配置文件中添加拉流代理,指定RTSP源地址、流ID,以及超时重连参数。RTSP地址的格式需要注意,海康的一般是
rtsp://用户名:密码@IP:554/Streaming/Channels/101
,其中101是主码流,102是子码流;萤石的地址格式略有不同,要按设备说明书来。密码里如果含有特殊字符,记得做URL编码,否则拉流一直报401认证失败。
重连参数一定不能省。会议室摄像头偶尔会因为网络波动断流,如果没有自动重连,整个会议画面就卡死在最后一帧。我设置了断流后3秒自动重连,同时开启了rtp超时检测,平时一切正常,摄像头被误拔电源后重新插上,画面能在10秒内自动恢复。
拉流成功之后,SmartMediaKit会自动把这路RTSP流转成多种输出协议。Web端通过WebRTC方式拉流,延迟在300毫秒左右;如果某些老设备不支持WebRTC,走HTTP-FLV延迟也就一两秒,完全够用。这份自动转协议的能力是这套方案的核心价值——你只管把RTSP塞进去,剩下的分发问题SMS帮你解决。
3.3 多摄像头管理与权限控制
会议室通常不止一路摄像头,我接了两路:一路对着主讲人,一路对着参会席。这就涉及到多路摄像头的管理和在客户端的呈现方式。
管理端我在业务层做了一版简单的摄像头管理页面,支持添加摄像头名称、RTSP地址、安装位置备注。添加完成后自动生成摄像头流ID,并在SmartMediaKit中创建对应的拉流任务。在会议进行中,主持人可以在客户端看到所有摄像头的预览画面,点击某一路即可切换到主画面。这个交互走的是WebRTC的重新协商流程,SmartMediaKit不需要做任何额外处理,只负责同时把多路流转发出去即可。
权限这块要注意:摄像头画面属于会议敏感信息,不能所有参会者都能随意切换和查看。我按角色做了三级权限:主持人能看所有摄像头画面、能够切换主画面;主讲人只能看到和他当前讲解区域相关的摄像头;普通参会者只能看主持人指定的画面。这套权限在业务层完成后,前端按权限渲染对应的视频元素,做到“看不到的流就不拉”,避免不必要的带宽消耗。
还有一个小经验:长时间开会时,摄像头发热会导致画面偶尔出现花屏或马赛克。这种问题很难通过软件层面彻底解决,我在系统里加了“画面异常自动切换备用码流”的兜底逻辑——如果检测到视频帧连续异常,就切换到备用摄像头的子码流。虽然不是常用功能,但在关键会议中救过一次场。
4. 会议录像方案:服务端录制与回放系统搭建
4.1 录像模式选型:服务端录制优先
会议录像有两条技术路线:客户端录制和服务端录制。客户端录制是让主讲人的浏览器通过MediaRecorder把屏幕流录下来,好处是录制的是原始高清画面,坏处是主讲人一关页面录像就断了,而且录制文件分散在个人电脑上,没法统一管理。服务端录制则是让SmartMediaKit在转发流的过程中把流原样落盘,不受任何客户端影响,文件统一汇总。
我毫不犹豫选了服务端录制。理由很实际:无纸化会议系统的录像必须作为正式会议记录留存,可靠性和统一管理是第一位的。服务端录制正好满足这两个要求,而且SmartMediaKit的录制功能是原生自带的,不需要额外开发。
这里再补充一个判断依据:如果你的录像用途是“留证”而非“做精美视频”,服务端录制是唯一靠谱的选项。因为客户端录制的文件可能因为浏览器后台标签被回收、系统休眠等原因中断,你根本没法控制。而服务端录制的文件完整性可以做到接口可查,就算网络闪断导致录制中断,也能从报错信息里看出来。
4.2 SmartMediaKit录制配置与文件切片
SmartMediaKit的录制支持按需开启和全局开启两种模式。我选择了按需开启:只有状态为“进行中”的会议才启用录制,会议结束后自动关闭录制并归档文件。这样可以避免无效录像占满硬盘。
具体配置上,我对每路流设置了独立的录像目录,目录名用
会议ID/流类型
来组织。举个例子,会议编号20230522-01的录像目录结构是:
/record/20230522-01/
screen_zhangsan/
20230522100000.mp4
20230522100015.mp4
camera_01/
20230522100000.mp4
SmartMediaKit录制时默认按时间切片生成文件。我把切片时长设置成15秒——太长会导致回放seek精度差,太短会产生过多小文件增加磁盘IO压力。15秒是比较均衡的选择,回放时几乎可以精确定位到某一时刻的画面。
还要特别提醒:录制文件的保存格式建议直接选MP4。虽然TS格式对切片更友好,但MP4通用性极强,Windows、Mac、手机播放器直接双击就能打开,省去了播放器兼容性的麻烦。SmartMediaKit的MP4录制还支持moov box前置,即使文件没有完整走完结束流程(比如会议中断),也能正常播放,大大降低了录像文件损坏的风险。
4.3 回放、索引与存储运维
录像文件落盘只是第一步,会议系统还需要提供回放功能。我在业务层实现了简单的录像列表页:按会议维度展示所有录像文件,支持按时间范围筛选,点击某个时间段直接调起播放器。
回放播放器我用的是Video.js加HLS协议。SmartMediaKit在录制的同时会生成对应的HLS索引文件,回放时前端通过HLS拉取切片播放。虽然HLS延迟比WebRTC高,但回放场景完全不在乎延迟,HLS的优势是稳定性好、拖拽进度条流畅、兼容性广,一套方案搞定PC和移动端。
存储运维这块容易被忽略,但长期使用一定会遇到。我踩过的坑是录像文件膨胀导致磁盘写满,会议系统直接罢工。后来加了两道保险:一是定时任务每天检查录像目录大小,超过阈值自动清理旧文件;二是录制定时维护策略,只保留最近90天的会议录像,更古老的视频转存到冷存储NAS。如果你负责的是经常开长会的企业,建议把保留周期和清理策略在项目交付时就定清楚,否则后期运维会很难受。
还有录像文件的安全问题。会议录像可能包含商业机密,不能随便访问。我在业务层做了播放鉴权——播放URL动态生成签名链接,过期时间5分钟。这样即使录像回放地址被泄露,别人也只能在看5分钟内播放,过期作废,兼顾了便捷性和安全性。
5. 实战中踩过的坑与排查技巧
5.1 高负载下推流中断:排查与配置调优
同屏功能上线后,第一次遇到的问题是多人同时推流时,部分主讲人频繁出现推流中断。刚开始怀疑是SmartMediaKit承受不了并发,后来翻日志发现是webSocket信令连接被服务端强制断开。
排查过程很曲折,最后查到根因:业务服务器的WebSocket服务默认空闲超时是60秒,而会议主讲人有时候会长时间不动屏幕,导致信令链接处于空闲状态被服务端回收。推流虽然还在继续,但信令断开后SmartMediaKit认为推流会话结束,就把流断了。
解决办法是双管齐下:一是把WebSocket空闲超时调到30分钟,并且每30秒发一次心跳保活;二是在业务层增加断线自动重推逻辑,检测到上行的RTCPeerConnection断开后,自动重新获取屏幕流并恢复推流。处理后连续跑了一周,再没出现推流中断的情况。
5.2 摄像头接入延迟偏高:从链路找瓶颈
摄像头接入后远程端普遍反映画面延迟大,体感有3到4秒。这个延迟在监控场景没问题,但会议场景下很影响体验——摄像头这边人已经说完话了,远端要等两三秒才听到。
我沿着链路逐个排查。首先怀疑的是摄像头自身的延迟,通过ONVIF工具直接拉流测试,发现摄像头到SmartMediaKit的延迟其实只有300毫秒左右。然后测试SmartMediaKit转WebRTC的转发延迟,单独用本机推流本机拉流,延迟在200毫秒内,也不是瓶颈。
最后定位到问题出在拉流参数上。摄像头的RTSP拉流默认开启了TCP传输模式,TCP在弱网环境虽然稳定,但会有重传机制导致延迟积累;而会议场景的网络是可控的,改用UDP模式后延迟直接降到了1秒以内。而且我还把子码流的帧率从25fps降到了15fps,降低编码压力的同时优化了延迟。摄像头延迟问题是一个典型的端到端问题,排查时必须分段测量,否则很难定位到具体环节。
5.3 同屏花屏与音频不同步的处理
上线后收到一个奇怪的问题反馈:某Windows电脑同屏时,远程端看到的画面偶尔会出现花屏和马赛克,同时声音和画面不同步。我一开始以为是网络丢包造成的,但检查了SmartMediaKit的转发统计,丢包率很低,完全在正常范围内。
后来在主讲人的电脑上做了代码分析,发现是他的浏览器开启了硬件加速,GPU解码过程中出现异常导致编码帧损坏。解决办法是在getDisplayMedia采集时增加一个可选项,让用户可以关闭硬件编码加速,改用软件编码。加上这个开关后,花屏问题不再出现。
音频不同步的问题则出在音频采样率不匹配上。Windows上某些声卡驱动上报的采样率是44100Hz,而SmartMediaKit默认按48000Hz处理,导致音频被拉伸,声音比画面慢半拍。我在推流前对音频轨道做了标准化处理,统一转成48000Hz双声道,问题解决。这两个问题都有个共同规律:同屏问题不要总想着是网络和服务端的锅,前端采集端的兼容性往往是最大的变量。
写在最后的一些体会
整套系统从设计到落地,最大的感受是选对中间件能让项目进度快一倍。SmartMediaKit把摄像头拉流、协议转换、WebRTC分发、服务端录制这些通用能力做成了标准化服务,让我能集中精力解决业务逻辑和跨端体验问题。如果你也要做类似的会议系统,我建议先想清楚你的核心链路是什么,然后找一个能在核心链路上“顺手接上”的媒体服务器,不要一上来就堆砌重型组件。
最后分享一个调试技巧:SmartMediaKit的管理接口非常实用,可以实时查看每一路流的推流状态、拉流客户端数量和码率曲线。遇到问题先看管理接口的数据,再决定从哪里排查,比我以前瞎猜日志要高效得多。这套无纸化会议系统目前已经稳定运行了好几个月,后续我还打算加上智能纪要、人脸签到这类功能,到时候再来分享新一轮的实践经验。
更多推荐

所有评论(0)