4. H265推流卡顿问题排查与解决:推流协议选择与优化(RTMP/WebRTC/SRT)

推流卡顿,很多时候不是编码器的问题,而是协议没选对。我见过不少团队,H265编码参数调得炉火纯青,结果卡在传输环节。说白了,协议决定了你的数据包怎么走、走多快、丢了怎么办。

今天咱们就聊聊RTMP、WebRTC、SRT这三个主流协议。我会结合自己的踩坑经历,帮你理清什么时候该用谁,以及怎么优化。

核心观点:没有万能的协议,只有最适合场景的选择。H265的高压缩率对传输协议提出了更高要求——丢包容忍度更低,解码依赖更强。

H265 推流协议选型框架 H265 推流 RTMP WebRTC SRT 特性 • 延迟:1-3s • 丢包:重传弱 • H265:需扩展 特性 • 延迟:<500ms • 丢包:FEC+NACK • H265:原生支持 特性 • 延迟:0.1-2s • 丢包:ARQ • H265:原生支持 直播推流/CDN 实时互动/会议 弱网/公网传输

4.1 RTMP:老将的坚守与局限

RTMP是Adobe在2009年推出的协议,至今仍是国内直播推流的主流。我最早做直播时,RTMP几乎是唯一选择。但H265来了之后,RTMP有点力不从心。

RTMP的核心问题:

  • H265支持不原生:RTMP标准只定义了H264的Codec ID。H265需要自己扩展,比如用Codec ID=12,或者封装在FLV的VideoTag里。很多CDN不认。
  • 丢包重传弱:RTMP基于TCP,丢包会触发TCP重传,导致队头阻塞。一旦网络抖动,画面直接卡死几秒。
  • 延迟偏高:正常1-3秒,优化后能到0.5-1秒,但再低就难了。

我的经验:如果CDN只支持RTMP,H265推流时建议在FLV头部手动注入H265的Codec ID。我曾经对接某云厂商,他们不认标准扩展,最后用私有Codec ID+服务端映射才搞定。

RTMP优化要点:

  1. 调整Chunk Size:默认128字节,建议调到4096或8192,减少包数量,降低CPU开销。
  2. 关闭Nagle算法:TCP_NODELAY必须开,否则小包会延迟发送。
  3. 控制GOP大小:H265的IDR帧比H264大很多,GOP太大容易导致首帧延迟。建议2秒一个IDR。
  4. 使用Enhanced RTMP:部分CDN支持Enhanced RTMP扩展,原生支持H265和HEVC。
// RTMP推流H265的FLV扩展示例(C风格伪代码)
typedef struct {
    uint8_t  type;          // 0x12 表示H265
    uint8_t  packet_type;   // 0: AVC sequence header, 1: NALU
    uint32_t composition_time; // CTS偏移
    uint8_t  data[];        // H265 NALU数据
} FLVVideoTagH265;

// 设置Socket选项
int fd = socket(AF_INET, SOCK_STREAM, 0);
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));

注意:RTMP推H265时,如果CDN不支持,服务端可能直接断开连接或花屏。建议先发一个H264的SPS/PPS做探测,确认支持后再切H265。

4.2 WebRTC:低延迟的王者

WebRTC最初是为浏览器实时通信设计的,延迟能做到500ms以内。H265在WebRTC里原生支持吗?嗯,这里要注意——WebRTC标准只强制支持H264和VP8/VP9,H265是可选的。

WebRTC的优势:

  • 延迟极低:端到端200-500ms,适合互动场景。
  • 抗丢包机制:FEC(前向纠错)+ NACK(丢包重传)+ 带宽估计,动态调整码率。
  • H265支持:通过RTP的Payload Type 96-127动态映射,可以协商H265。

WebRTC推H265的坑:

  • 浏览器兼容性:Chrome不支持H265,Safari支持但需要硬件解码。我做过一个项目,用户用Chrome看H265 WebRTC流,直接黑屏。
  • 带宽估计不准:H265的码率波动比H264大,WebRTC的GCC算法有时反应过慢,导致卡顿。
  • 编码器适配:WebRTC的编码器接口需要实现H265的编码器封装,不是所有硬件编码器都兼容。

避坑指南:我曾经在WebRTC里集成H265硬件编码器,发现SPS/PPS的解析方式跟H264完全不同。WebRTC的H264 SPS解析器会直接崩溃。解决方案是自己实现H265的SPS解析,或者用FFmpeg的bsf模块预处理。

WebRTC优化建议:

  1. 调整带宽估计参数:增大min_bitrate,避免码率被压得太低。
  2. 启用Simulcast:同时推多路不同分辨率的流,接收端根据网络选择。
  3. 使用SVC:H265的SVC(可伸缩编码)在WebRTC里效果不错,但实现复杂。
  4. 关闭NACK重传:如果延迟要求极高,可以只开FEC,牺牲一点画质换流畅度。
// WebRTC SDP中协商H265的示例
// Offer端
m=video 9 UDP/TLS/RTP/SAVPF 96
a=rtpmap:96 H265/90000
a=fmtp:96 profile-id=1; level-id=93; sprop-vps=...; sprop-sps=...; sprop-pps=...

// Answer端
m=video 9 UDP/TLS/RTP/SAVPF 96
a=rtpmap:96 H265/90000
a=fmtp:96 profile-id=1; level-id=93

我的习惯:WebRTC推H265时,我会在服务端做转码兜底。如果客户端不支持H265,自动降级到H264。这样既保证了低延迟,又兼容了老设备。

4.3 SRT:专为公网传输而生

SRT(Secure Reliable Transport)是Haivision开源的协议,基于UDP,但加入了ARQ(自动重传)和加密。我是在做跨国直播时接触到的,效果出乎意料地好。

SRT的核心优势:

  • 抗丢包能力强:ARQ机制只重传丢失的包,不会像TCP那样阻塞后续数据。
  • 延迟可控:可以设置latency参数,比如100ms,超过这个时间的包直接丢弃。
  • H265原生支持:SRT传输的是TS或MPEG-TS流,H265直接封装进去就行。

SRT的适用场景:

  • 公网传输:丢包率5%以内,SRT基本无感。
  • 跨国推流:延迟比RTMP低,比WebRTC高,但稳定性更好。
  • H265高码率:SRT的ARQ机制对H265的大包很友好。

注意:SRT的latency参数设置不当会导致卡顿。设得太小,ARQ来不及重传;设得太大,延迟飙升。我一般建议:公网设200-400ms,内网设100ms。

SRT优化参数:

参数 推荐值 说明
latency 200-400ms 公网传输的延迟容忍度
rcv_latency 150-300ms 接收端缓冲延迟
peer_latency 50-100ms 对端延迟,通常设为latency的一半
maxbw 码率x1.2 最大带宽,防止突发流量丢包
packet_size 1316 TS包大小,7个188字节的TS包
// FFmpeg使用SRT推H265的示例
ffmpeg -i input.mp4 \
  -c:v libx265 -preset medium -b:v 5M \
  -f mpegts \
  'srt://192.168.1.100:9000?mode=caller&latency=300&maxbw=6000000'

// SRT接收端
ffmpeg -i 'srt://0.0.0.0:9000?mode=listener&latency=300' \
  -c:v copy -f flv output.flv

我的经验:SRT的ARQ机制在丢包率超过10%时效果会下降。这时候可以配合FEC使用,但会增加带宽开销。我做过测试,5%丢包率下,SRT+ARQ的流畅度比RTMP高30%以上。

4.4 协议选型决策树

说了这么多,到底怎么选?我总结了一个简单的决策逻辑:

  1. 延迟要求 < 500ms:选WebRTC。互动场景、视频会议、远程控制。
  2. 延迟要求 1-3s,CDN分发:选RTMP。直播推流、点播转码。
  3. 公网传输,丢包率高:选SRT。跨国直播、野外直播、弱网环境。
  4. 需要加密传输:SRT自带AES加密,WebRTC有DTLS-SRTP,RTMP需要额外加TLS。

避坑指南:我曾经在一个项目中同时用了RTMP和SRT做双协议推流。RTMP走CDN,SRT走备用链路。结果发现SRT的延迟比RTMP低,但CDN不支持SRT。最后方案是:主链路用RTMP,备用链路用SRT+转码服务器。

协议选型没有银弹。我的建议是:先明确你的网络环境和延迟要求,再选协议。如果条件允许,做双协议冗余推流,关键时刻能救命。