Blog

直播板块构建之路

liveinfradeployment

直播板块构建之路:从 Screego 到 MediaMTX

本站在 /live 下集成了一个完整的低延迟直播系统。回头看这个过程,踩了不少坑才找到正确的路。

第一阶段:Screego(失败)

一开始想着屏幕共享,选了 Screego——一个 WebRTC 的点对点屏幕共享工具。把它跑起来不难,Screego 自带 TURN 服务器,浏览器之间通过 WebSocket 信令建连。

问题很快暴露:

- WebRTC 是 P2P 架构,需要 TURN 中继做 NAT 穿透
- TURN relay 每个观众分配一个随机端口
- 通过 SakuraFrp 对外暴露时,frpc 只能转发一个固定端口
- 手机流量有运营商级 NAT,P2P 打不通
- Screego 内置 TURN 要求在服务器上开放至少 40 个连续 UDP 端口

结论:P2P 不适合单主播多观众的直播场景。 Screego 适合"两个人互相看对方屏幕",不适合"一个人播一群人看"。

第二阶段:重新理解需求

退回一步重新想。我的需求本质上是:

- 一台电脑推画面(OBS)
- 多个观众浏览器看
- 永远只有一个主播

这是标准的广播模型,不是视频会议。广播模型的核心是"主播推流 → 服务器中转 → 观众拉流",所有人走同一条数据流,不存在 P2P 的 NAT 问题。

第三阶段:选型

调研了几个方案:

| 项目 | 语言 | Windows | 延迟 | 结论 |
|------|------|:--:|------|------|
| SRS | C++ | ❌ Linux only | 1-3s | 不支持 Windows |
| ZLMediaKit | C++ | ⚠️ 需编译 | <500ms | 部署复杂度高 |
| MediaMTX | Go | ✅ 官方 exe | ~1s | 最简方案 |

MediaMTX 胜在:一个 exe、零依赖、官方 Windows 编译、LL-HLS 低延迟、配置一个 YAML 就够。

第四阶段:落地架构

OBS (Arc A750 QSV H.264 1080p60)
  │ RTMP push
  ▼
MediaMTX (localhost:1935 → LL-HLS localhost:8888)
  │ HLS over TLS (Let's Encrypt)
  ▼
frpc 国内节点 TCP 隧道 (8888)
  │ sc.w-en.cc:27098
  ▼
观众浏览器 (hls.js 播放)
  延迟 ~7s (LL-HLS 协议天花板)

关键踩坑

1. LL-HLS 最少 7 段

Apple 规范要求 Low-Latency HLS 至少 7 个 segment。每段 1 秒就是最低 7 秒延迟。曾经想降到 3 段,MediaMTX 直接报错 Low-Latency HLS requires at least 7 segments

如果一定要亚秒延迟只能上 WebRTC,但 WebRTC 又绕回 TURN 的问题。权衡之后 7 秒够了——Twitch 也差不多这个数。

2. 韩国节点 vs 国内节点

页面(HTML/JS)走韩国 frpc 节点因为 HTTPS 域名绑定需要备案。但 HLS 视频流是纯 TCP,不需要备案,走国内节点延迟低得多。用 sc.w-en.cc 的备用域名走国内隧道,页面和视频走不同路径。

3. hls.js 的状态管理

MANIFEST_PARSED 才算真正连上流。之前用 Hls.Events.ERROR 做销毁重建会导致"加载-播放-离线"的闪烁。改成让 hls.js 自己重试,只在不可恢复的错误才标记离线。

4. 主播没开播时的体验

加了一个状态机:进入页面立即开始拉流,m3u8 不存在就显示"主播未开播,等待中",hls.js 自动后台重试。开播瞬间 MANIFEST_PARSED 触发,无缝切到播放状态。

最终配置

MediaMTX (mediamtx.yml):

rtmp: yes
rtmpAddress: :1935
hls: yes
hlsAddress: :8888
hlsVariant: lowLatency
hlsEncryption: yes
hlsSegmentDuration: 1s
hlsPartDuration: 200ms

前端 (page.tsx):

const hls = new Hls({
  lowLatencyMode: true,
  liveSyncDurationCount: 2,
  maxBufferLength: 20,
  testBandwidth: true,
});

OBS 设置速查

| 设置 | 值 |
|------|-----|
| 服务器 | rtmp://127.0.0.1:1935/live |
| 串流密钥 | stream |
| 编码器 | QuickSync H.264 |
| 输出 | 1920×1080, 60fps, 6000 Kbps CBR |

总结

一句话:直播就是直播,不是视频会议。 用广播模型(推流 → 中转 → 拉流)替代 P2P 模型后,所有 TURN/NAT/端口范围的问题都消失了。现在一条 TCP 隧道解决全部问题,不需要任何云服务器。