我给 QQ 群写了一个直播平台:PrismLive 开发手记

一台 8G 内存的旧服务器,一个 Node.js 进程,一个单文件媒体服务。这是我给自己社群做的直播平台。
ComfyUI_00000000_175574547621788_7588d9a0-54e3-485a-ae4b-95f054cc37a7_.png

起因:为什么非要自己造轮子

事情的起点特别朴素——我们 QQ 群想搞点直播,比如一起看球、讲题、开个线上分享会。

一开始也试过公共平台。但很快就发现不对劲:开播要审核、画面里不能出现这不能出现那、观众还得下载 App、弹幕里混进一堆陌生人。在群里几百个熟人的私域场景里,这些限制既多余又烦人。

我就想:直播这件事,拆开来看其实没多复杂——一个人推流,一堆人看,中间飘几条弹幕。凭这三样,我为什么不自己搭一个?

说干就干。现在回头复盘,整个项目踩过的坑、做过的取舍,都浓缩在下面这几关里。

第一关:怎么把画面"送"出去

主播电脑上要有个软件把摄像头画面推出来,这活儿几乎只有一个标准答案——OBS(免费、强大、人手一份)。它通过一种叫 RTMP 的协议,把画面像"连了一根话筒线"一样,实时推送到服务器。

那服务器端谁来接这根线?这里就是我做的第一个重要决定:不自己写媒体服务器

视频的转封装、切片、分发,是直播里最难啃的骨头,自己写等于重新发明轮子,还大概率写不过人家。所以我直接引进了 MediaMTX——一个只有十几兆的单文件程序,RTMP/RTSP/HLS 全支持,零配置就能跑。OBS 推给它,它帮我完成后面所有脏活累活。

于是链路的第一段就通了:

OBS 推流  ──RTMP──▶  MediaMTX  ──转封装──▶  观众浏览器
   (话筒线)            (专业接线员)

这条朴素的思路贯穿始终:媒体的事交给专业组件,业务逻辑自己写。剩下的,就都是我能掌控的 Node.js 代码了。

第二关:观众用什么姿势看

画面到了服务器,观众怎么拿?这里遇到直播领域最经典的抉择——HLS 还是 FLV?

用最直白的话解释这两个协议:

  • HLS 像是"把直播切成一片片面包,观众一片片取"。好处是几乎所有浏览器都吃这一套,微信里也能播,还天生适合 CDN 加速;坏处是慢——端到端往往有 10 秒以上的延迟。
  • FLV 像是"一根一直在流水的水管"。延迟能压到 3 秒以内,互动感极强;坏处是兼容性差,走代理隧道还可能断流。

小孩子才做选择,成年人……我两个都要。我把它做成了后台可切换的双引擎:默认 HLS 保底,需要低延迟互动就切 FLV。

更有意思的是自动回退这个细节:FLV 一旦播放出错,前端会悄悄降级回 HLS,而且整个会话只回退一次,不会反复横跳。还有个小判断——如果检测到观众是穿过隧道/代理来的,后端直接不返回 FLV 地址。为什么?因为 FLV 是长连接拉流,隧道转发必然出问题,与其让观众傻等,不如一开始就明确告诉他"这条路不通,走 HLS"。

连推流状态都是双保险:服务端每 5 秒同时探测 MediaMTX 和 SRS 两个引擎,哪个活跃都算"直播中",码率、观看人数、推流时长一起算好展示。

第三关:弹幕怎么才能不崩

直播的灵魂是弹幕。但弹幕也是被攻击的重灾区——刷屏、灌水、颜色注入。我做了三层防护,每一层都很轻:

  1. 单用户冷却:同一个人两条弹幕之间必须隔 0.8 秒,手速再快也没用;
  2. 全局限速:以 1 秒为窗口,每秒总弹幕量可配置上限,防止瞬间洪水;
  3. 速率统计:用一个固定 2048 长度的环形缓冲记录弹幕时间戳,像"汽车的里程表"一样 O(1) 算出每分钟弹幕数——时间再久,内存和计算量都不会无限膨胀。

弹幕传输用的是 Socket.IO,你可以理解成一个"全房间大喇叭":谁喊一嗓子,服务端立刻广播给所有人,不用观众反复刷新。

还有两个让我自己都挺得意的小心思:

  • 颜色白名单:弹幕颜色只允许白/红/粉/紫/青/绿/黄/橙这 8 种。别小看这个限制——它从根上堵死了"塞任意颜色值注入样式"的漏洞,攻击者连门都摸不到。
  • QQ 头像:观众填 QQ 号就能自动带出昵称和头像。为了这个接口不被刷爆,我加了 1 小时内存缓存 + 每 IP 每分钟限 10 次。

第四关:主播需要一个指挥台

直播进行时,主播/管理员需要的可不是普通观众页面,而是一个能"眼观六路"的中控后台

  • 实时看板:当前在线、峰值在线、今日观看、累计弹幕、每分钟弹幕速率、累计点赞。这些数字通过 Socket.IO 每 2 秒刷新一次,数字用等宽字体渲染,不会一跳一跳地晃眼睛;
  • 推流状态卡:实时码率、推流时长、累计上行流量、媒体层读者数、HLS/FLV 两个引擎各自的状态灯——哪个引擎在跑一目了然;
  • 链路诊断:每 15 秒自动探测各条分发线路(隧道、域名、端口)的延迟和 HTTP 状态。哪条路挂了,一眼就能看出来,不用抓瞎;
  • 数据曲线:用 ECharts 画在线人数和弹幕趋势,还能一键导出 CSV;
  • 黑名单:按昵称或 QQ 号拉黑,永久生效。

后台能实时看到"现在有几个人在看、画面码率多少、哪条链路不通",这种掌控感,是公共平台给不了的。

第五关:安全这根弦,松不得

自建服务被盯上是迟早的事,安全我从第一天就在做:

  • 登录防爆破:10 分钟窗口内密码错 5 次,锁定 10 分钟。暴力破解基本没戏;
  • 接口鉴权:后台所有接口(除了登录)都要带 Bearer 令牌,令牌 24 小时过期、自动清理;
  • 推流密钥:随机生成、可重置。OBS 必须带对密钥才收流,否则画面根本进不来;
  • 敏感词过滤:后台可配敏感词,命中即拦截;
  • 默认密码提醒:后台默认密码 admin123,登录页明晃晃写着"请及时修改"。

说白了,这一整套就像给直播间配了个尽职的门卫:该拦的拦,该放的放,还时不时提醒你"门锁该换了"。

数据:一本随身的记账本

存储上,我做了一个事后被证明特别对的选择——直接用 Node 22.5+ 内置的 SQLitenode:sqlite)。

好处是惊人的:零原生编译依赖。这意味着 npm install 再也不会因为缺 C++ 编译链而在 Windows 上翻车。数据就是 data/ 下一个单文件,里面三张表:

  • settings:标题、公告、开关、敏感词、推流密钥、密码(加盐哈希)、引擎配置;
  • blacklist:黑名单;
  • stats:统计(今日观看、累计弹幕、峰值在线,按天重置)。

备份?拷走一个文件 + 导一下库,完事。对个人项目来说,这比什么分布式数据库都香。

好看,真的重要

技术上跑通只是及格线,长得丑没人愿意看。所以前端我也花了心思——原生 HTML/CSS/JS,零框架零构建,但视觉规格统一:

  • 深蓝黑渐变背景(#0b0f1a → #141a2e),品牌紫 #7c5cff 点缀,偶尔用青 #22d3ee 做数据色;
  • 玻璃拟态卡片(半透明背景 + 毛玻璃模糊 + 细边框);
  • 深色模式、移动端横竖屏都适配。

哪怕是个"自己人用"的直播平台,打开第一眼也得像那么回事。
PrismLive 观众端:直播画面与弹幕

写在最后:几个小心得

回头看,PrismLive 代码量不大,但它教会我几件事:

  1. 别什么都自己写。把媒体交给 MediaMTX,把数据交给 SQLite,把精力花在"业务逻辑"这一亩三分地上,才是个人项目该有的姿势;

2. 细节即护城河。双引擎回退、环形缓冲限速、颜色白名单、隧道检测——每一个都只是几十行代码,但每一个都在真实场景里兜了底;

  1. 安全不是可选项。哪怕服务只给自己人用,防爆破、鉴权、敏感词这些也得从第一天就做,因为攻击者不会因为你"低调"就放过你。

一台 8G 内存的旧服务器,跑起一整套直播平台,稳稳当当。如果你也想给某个小圈子搭一套自己的直播,希望这篇手记能帮你少踩几个坑。

—— 欢迎交流,也欢迎去试试推一条流过来,直播间见。