28、WebRTC与直播低延迟:LL-HLS与WebRTC的对比、WebRTC推流到CDN、延迟控制在1秒以内

低延迟直播,这几年被炒得很热。

我经常被问到:「WebRTC 能不能直接替代 HLS?」

我的回答是:能,但要看场景

HLS 有它的生态优势,CDN 分发成熟,播放器遍地都是。但延迟嘛……传统 HLS 动辄十几秒,哪怕 LL-HLS(Low-Latency HLS)优化到 3-5 秒,跟 WebRTC 的 1 秒以内 比起来,还是差了一个量级。

这一章,我们就把 LL-HLS 和 WebRTC 放在台面上,掰开揉碎对比。然后重点聊聊:怎么用 WebRTC 推流到 CDN,把延迟压到 1 秒以内


LL-HLS 与 WebRTC:核心差异在哪?

先看一张对比表,心里有个底。

维度 LL-HLS WebRTC
传输协议 HTTP/2 + CMAF 分块 UDP (SRTP/SCTP)
端到端延迟 3-8 秒(优化后) 200ms - 1 秒
播放器兼容性 原生 HLS 播放器 + 少量改动 需集成 WebRTC SDK
CDN 支持 天然支持,复用现有架构 需额外部署转码/转协议节点
抗丢包能力 依赖 TCP 重传,弱网易卡顿 FEC + NACK,弱网表现更好
大规模分发 成熟,百万级并发轻松 需做 SFU 或转 CDN,成本较高

说白了,LL-HLS 是在「兼容旧生态」的前提下,尽量压低延迟。而 WebRTC 从一开始就是为实时互动设计的,延迟是它的基因。

但这里有个坑——WebRTC 的 UDP 传输,在 CDN 上并不友好。传统 CDN 节点是为 HTTP 缓存设计的,你突然丢一堆 UDP 包过去,它不会处理。

所以,WebRTC 推流到 CDN,就成了一个必须解决的关键问题。


WebRTC 推流到 CDN:架构怎么搭?

我早期做过一个项目,客户要求「主播用 WebRTC 推流,观众用 HLS 看,延迟控制在 1 秒以内」。当时市面上还没有成熟的方案,我们硬是搭了一套转码链路。

核心思路其实不复杂:

  1. WebRTC 推流端:主播通过浏览器或 App,用 WebRTC 发送音视频流到服务器。
  2. 媒体服务器:接收 WebRTC 流,解码后重新编码为 CMAF 分块(或直接转封装)。
  3. CDN 分发:将 CMAF 分块推送到 CDN 边缘节点。
  4. LL-HLS 播放:观众端使用支持 LL-HLS 的播放器拉流。

嗯,这里要注意:延迟能不能压到 1 秒,关键在第二步

如果媒体服务器做「全解码再编码」,延迟至少增加 500ms。我建议的做法是:尽量做转封装,不做转码。如果编码格式一致(比如都是 H.264 + AAC),直接解包后重新封装成 CMAF 分块,延迟可以控制在 200ms 以内。

核心原则:WebRTC 推流到 CDN,延迟瓶颈不在网络,而在媒体服务器的处理流水线。


延迟控制在 1 秒以内:具体怎么做?

我总结了一套「三板斧」,你在实际项目中可以直接套用。

第一板斧:推流端优化

  • 编码器设置:使用硬件编码器,开启低延迟模式。x264 的 preset 用 ultrafast 或 superfast。
  • GOP 大小:固定为 1 秒(比如 30fps 下 GOP=30)。太长会导致关键帧等待延迟。
  • 码率控制:用 CBR(恒定码率),避免 VBR 导致的码率波动。
// 推流端编码参数示例(基于浏览器)
const videoEncoderConfig = {
  codec: 'video/H264',
  width: 1280,
  height: 720,
  bitrate: 2_000_000,
  framerate: 30,
  latencyMode: 'realtime',  // 低延迟模式
  scalabilityMode: 'L1T1'   // 单层时间分层
};

第二板斧:媒体服务器流水线

我推荐使用 JanusMediaSoup 作为接收端,配合 FFmpeg 做转封装。

流水线设计如下:

WebRTC (SRTP) → Janus (解码) → RTP 裸流 → FFmpeg (转封装为 CMAF) → CDN 上传

这里有个小技巧:不要等完整的分块生成再上传。CMAF 支持「分块传输」,你可以每收到一个 MP4 片段(比如 1 秒),立即推送到 CDN。CDN 边缘节点收到后,立刻就能被播放器拉走。

避坑指南:我曾经遇到 CDN 节点缓存策略导致延迟增加。解决方案是:在 HTTP 响应头中设置 Cache-Control: no-cache,并启用 chunked transfer encoding。

第三板斧:播放端适配

播放器必须支持 LL-HLS。hls.js 从 v1.0 开始支持,但需要开启低延迟模式。

// 播放器配置示例
const config = {
  lowLatencyMode: true,
  liveSyncDurationCount: 1,   // 追赶直播边缘
  liveMaxLatencyDurationCount: 3,
  fragLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 500,
      maxLoadTimeMs: 2000
    }
  }
};
const hls = new Hls(config);
hls.loadSource('https://cdn.example.com/live/stream.m3u8');

你想想看,如果播放器还在用传统的「下载完整分片再播放」逻辑,延迟肯定下不来。必须让它 边下载边播放,也就是分块级别(fragment-level)的流式加载。


SVG 架构图:WebRTC 推流到 CDN 的完整链路

下面这张图,是我在实际项目中画的架构图。你可以看到数据从主播到观众的完整路径。

WebRTC 推流到 CDN 架构图 主播端 WebRTC 推流 H.264 + Opus 媒体服务器 Janus / MediaSoup 转封装为 CMAF CDN 边缘节点 缓存 CMAF 分块 HTTP/2 分发 观众 LL-HLS 延迟:编码 + 网络 延迟:转封装 ~200ms 延迟:CDN 分发 ~300ms 延迟:播放缓冲 ~200ms 总延迟 ≈ 700ms - 1s 关键优化点: 1. 推流端 GOP 固定 1 秒,开启低延迟编码 2. 媒体服务器做转封装,不做转码 3. CDN 开启 chunked transfer,播放器启用 lowLatencyMode 推流端 服务器处理 CDN 分发 播放端 时间轴 →

实际项目中的避坑指南

我踩过不少坑,挑几个典型的说说。

坑 1:CDN 不支持 HTTP/2 分块传输

有些老牌 CDN 节点还在用 HTTP/1.1,分块传输效率极低。我建议在选 CDN 时,明确要求支持 HTTP/2 和 chunked transfer。否则延迟会直接飙到 3 秒以上。

坑 2:播放器缓冲策略太保守

默认的 hls.js 配置,为了稳定性会缓冲 3-5 秒。你必须手动调低 liveSyncDurationCountmaxBufferLength。我曾经调了一整天,才找到「不卡顿 + 低延迟」的平衡点。

我的经验值:在弱网环境下,WebRTC 推流到 CDN 的延迟会波动。我建议在媒体服务器端做 动态码率调整,根据推流端的网络状况,自动降低编码码率。这样虽然画质会下降,但延迟能稳定在 1 秒以内。


总结一下

LL-HLS 和 WebRTC,不是谁替代谁的关系。它们是 互补的

  • 如果你需要 大规模分发 + 兼容现有播放器,LL-HLS 是更稳妥的选择。
  • 如果你需要 极致低延迟 + 互动性,WebRTC 是唯一答案。
  • WebRTC 推流到 CDN,则是把两者的优势结合起来——主播端享受 WebRTC 的低延迟推流,观众端享受 CDN 的大规模分发能力。

延迟控制在 1 秒以内,不是玄学。只要把编码、转封装、CDN 分发、播放缓冲这四个环节的延迟都压到 300ms 以内,总延迟自然就下来了。

嗯,这一章就到这里。下一章我们聊聊更进阶的话题——WebRTC 的带宽估计与拥塞控制,这才是真正拉开差距的地方。


公众号:蓝海资料掘金营,微信 deep3321