4. 推流协议选择:RTMP vs SRT vs WebRTC

推流卡顿,很多时候不是带宽不够,也不是编码器不行,而是协议选错了。我见过太多团队,一卡顿就砸钱升级服务器,结果问题出在协议本身。今天咱们就把 RTMP、SRT、WebRTC 这三兄弟掰开揉碎,看看它们到底差在哪。

4.1 三款协议的核心差异

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

特性 RTMP SRT WebRTC
传输层 TCP UDP + 自定义重传 UDP + ICE/SRTP
延迟典型值 3-10 秒 0.5-2 秒 0.1-0.5 秒
抗丢包能力 弱(TCP 队头阻塞) 强(ARQ + FEC) 中(FEC + NACK)
防火墙穿透 差(依赖 1935 端口) 中(可配置端口) 强(ICE/STUN/TURN)
适用场景 传统直播、CDN 分发 弱网推流、远程制作 实时互动、视频会议

说白了,RTMP 是「老黄牛」,稳但慢;SRT 是「越野车」,路况差也能跑;WebRTC 是「跑车」,快但挑路。

4.2 协议对卡顿的影响机制

4.2.1 RTMP 的队头阻塞

RTMP 跑在 TCP 上。TCP 有个毛病:一个包丢了,后面的包都得等着重传。这在直播里很要命。你想想看,视频帧是连续的,一个关键帧丢了,后面几十帧都解不出来。

我在项目中遇到过,某客户用 RTMP 推 4K 流,网络抖动 5% 就开始卡。后来抓包一看,TCP 窗口一直在 0 和满之间来回跳。这就是典型的队头阻塞。

避坑指南: 我曾经在弱网环境下用 RTMP 推流,延迟从 3 秒飙到 30 秒。后来发现是 TCP 重传队列堆积,播放器缓冲越积越多。如果你必须用 RTMP,建议把播放器缓冲设为 5 秒以上,否则画面会频繁卡住。

4.2.2 SRT 的智能重传

SRT 用 UDP,但加了自定义的可靠传输。它有个「丢包重传 + 前向纠错」的组合拳。丢包少的时候用重传,丢包多的时候用 FEC 直接恢复。

我个人习惯在 SRT 里把 latency 参数设成 1000ms。为什么?因为 SRT 的延迟参数其实是「容忍等待时间」。设太短,重传来不及;设太长,延迟又上去了。1000ms 是个平衡点。

# SRT 推流示例(FFmpeg)
ffmpeg -i input.mp4 \
  -c:v libx264 -preset ultrafast -tune zerolatency \
  -f mpegts 'srt://push.example.com:10080?latency=1000&maxbw=5000000&pbkeylen=16&passphrase=yourkey'
小技巧: SRT 的 maxbw 参数建议设为实际带宽的 80%。设太高会导致 UDP 丢包激增,设太低又浪费带宽。我一般先用 iperf 测一下链路带宽,再反推这个值。

4.2.3 WebRTC 的实时博弈

WebRTC 追求的是「毫秒级延迟」。它用 UDP 传音视频,用 FEC 和 NACK 处理丢包。但有个问题:FEC 会占用额外带宽,NACK 又会引入延迟。

我记得有一次做远程手术示教,要求延迟低于 200ms。WebRTC 默认配置下,丢包 2% 就开始花屏。后来我把 googCpuOveruseDetection 关掉,手动调了码率分配,才稳住。

// WebRTC 码率控制示例(JavaScript)
const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});

// 设置视频编码器参数
const params = {
  minBitrate: 1000000,   // 1 Mbps
  maxBitrate: 4000000,   // 4 Mbps
  initialBitrate: 2000000 // 2 Mbps
};

// 通过 SDP 注入参数
const sdp = pc.localDescription.sdp;
const modifiedSdp = sdp.replace(
  /(a=fmtp:\d+ .*)/g,
  '$1;x-google-min-bitrate=' + params.minBitrate +
  ';x-google-max-bitrate=' + params.maxBitrate +
  ';x-google-start-bitrate=' + params.initialBitrate
);
pc.setLocalDescription({ type: pc.localDescription.type, sdp: modifiedSdp });
核心要点: WebRTC 的拥塞控制算法(GCC)在弱网下会主动降码率。如果你发现画面突然变模糊,不是编码器问题,是 GCC 在「保延迟弃画质」。这时候可以尝试固定码率,但要做好丢帧的准备。

4.3 低延迟协议的最佳实践

选协议不是非黑即白。我总结了一套「场景匹配法」:

  1. 传统直播(延迟容忍 5 秒以上):用 RTMP + CDN。成本低,生态成熟。
  2. 弱网推流(丢包率 > 5%):用 SRT。我曾在 20% 丢包率的卫星链路上推 1080p,SRT 依然能跑。
  3. 实时互动(延迟 < 1 秒):用 WebRTC。但要做好带宽预估和码率自适应。
  4. 混合场景:推流用 SRT,分发用 WebRTC。比如远程制作,现场用 SRT 推回机房,再转 WebRTC 给观众。

下面这张图是我自己画的协议选择流程,帮你快速决策:

推流协议选择决策流程 开始选协议 延迟要求 < 1秒? 是 WebRTC 实时互动 否 网络丢包 > 5%? 是 SRT 弱网推流 否 RTMP 传统直播 延迟 > 5 秒时,RTMP 性价比最高 中间场景可考虑 SRT + CDN 混合

4.4 实战中的协议调优

选对协议只是第一步。调优才是见功夫的地方。

4.4.1 RTMP 调优三板斧

  • 增大发送缓冲区:rtmp_buffer_size 10000(单位 KB),避免 TCP 发送窗口满。
  • 关闭 Nagle 算法:tcp_nodelay 1,减少小包延迟。
  • 使用 GOP 对齐:固定关键帧间隔,方便播放器快速恢复。

4.4.2 SRT 参数调优清单

参数 推荐值 说明
latency 1000-2000 ms 网络抖动越大,值越大
maxbw 带宽的 80% 避免 UDP 风暴
pbkeylen 16 加密强度,16 字节够用
passphrase 自定义 必设,防止盗推

4.4.3 WebRTC 的「降级」策略

WebRTC 默认配置偏保守。如果你需要更低延迟,可以尝试:

  1. 关闭 Simulcast:只推单流,减少编码开销。
  2. 降低分辨率:从 1080p 降到 720p,码率省一半。
  3. 使用 VP9:同等画质下码率比 H.264 低 30%,但编码器负载高。
我的经验: 在移动端推流,WebRTC 的硬件编码器支持度参差不齐。我建议先用 getCapabilities() 检测设备能力,再动态选择编码器。否则,有些手机推 1080p 直接过热降频,卡成幻灯片。

嗯,协议这块就聊到这。记住一句话:没有最好的协议,只有最合适的场景。下次遇到卡顿,先别急着调码率,看看协议选对了没。