🎬 第四章 RTMP流媒体协议
📄 一句话概括

拉流的首屏时间主要由「等一个关键帧」决定,其次是播放器自己的缓冲。 GOP 越长首屏越慢,但 GOP 越短码率越高——这是个绕不过去的取舍。

📝 本节和前面几节的关系

4.3.1 讲的是发送端,这一节看接收端要等什么。 4.4 开始拆「这些帧到底怎么装进块发出去」。

一、拉流的三个等待

从「点了播放」到「看到画面」,中间有三段等待: ① TCP 连接:三次握手加服务器调度,10~50 毫秒。 这一段很快,通常不是瓶颈。 ② 等关键帧:必须从 IDR 开始才能解码,最坏要等一整个 GOP。 ③ 播放器缓冲:自己攒几帧再播,200~500 毫秒。 攒得少容易卡,攒得多延迟大。

三段里第②段最大,而且它随 GOP 线性变化: GOP 1 秒、最坏等 1 秒;GOP 4 秒、最坏等 4 秒。 所以「首屏慢」这件事,八成的答案在 GOP 上。

二、算一下最坏与平均

关键帧是周期性出现的,所以「什么时候进来」决定了要等多久。 ① 最好情况:刚好在关键帧前一瞬间进来,等几十毫秒。 ② 最坏情况:刚好错过关键帧,要等一整个 GOP。 ③ 平均情况:均匀到达的话,期望是半个 GOP。

实测四组参数:25 fps 配 GOP 25 帧最坏等 1.0 秒;GOP 50 帧最坏 2.0 秒;30 fps 配 GOP 60 帧最坏 2.0 秒;GOP 250 帧最坏 10.0 秒。 注意后两组「fps 与 GOP 同时变」,结果是「两个不同的帧率可以给出同样的秒数」——因为真正决定首屏的是「GOP 帧数除以帧率」,也就是关键帧间隔的秒数。

三、GOP 怎么选

按场景分三档: ① 电商直播、连麦:GOP 1 到 2 秒。 宁可码率高一截,也要低延迟。 ② 秀场、游戏:GOP 2 秒左右。折中。 ③ 监控、录播转直播:GOP 4 秒甚至更长。 省带宽优先,反正没人盯着首屏。

GOP 变短的代价是码率上升:多一个关键帧就多一帧「整幅画面」,而关键帧通常是 P 帧的 5 到 10 倍大。 所以 GOP 从 4 秒压到 1 秒,码率可能涨 30% 以上。

还有一条:GOP 越短,丢包后恢复越快。 丢了一个关键帧,最多等一个 GOP 就能恢复——这就是为什么低延迟场景愿意为短 GOP 付码率的账。

四、RTMP 拉流的先天不足

RTMP 没有「请求关键帧」的机制。 HLS 与 WebRTC 都能让服务端立刻补一个 IDR,RTMP 不能——它只能等发送端按自己的 GOP 出。

这带来两个现实后果: ① 首屏慢没法「优化」:只能靠缩短 GOP 硬顶,没有巧办法。 ② 中途花屏恢复慢:丢了关键帧就只能等下一轮。

有些服务器实现了「服务端缓存关键帧」来缓解: 它在内存里留着最近一个 IDR,新观众进来立刻发给他。 这叫 GOP Cache,是 SRS、ZLMediaKit 这类服务器的常见配置项。 代价是服务端要额外占内存,而且缓存越长、首屏越快但延迟越大。

五、动手 Demo:首屏为什么要等一个 GOP

把三个等待列清楚,再按四组参数算最坏与平均等待。 最后看 GOP 与码率的取舍关系。

waits = [("TCP 连接", "三次握手 + 调度", "10~50 毫秒"),
         ("等关键帧", "必须从 IDR 开始", "半个到两个 GOP"),
         ("客户端缓冲", "播放器自己攒", "200~500 毫秒")]
for a, b, c in waits:
    print("%-10s %-20s %s" % (a, b, c))

print("")
for fps, gop in ((25, 25), (25, 50), (30, 60), (25, 250)):
    sec = float(gop) / fps
    print("%d fps、GOP %d 帧 → 最坏 %.1f 秒,平均 %.1f 秒"
          % (fps, gop, sec, sec / 2))

print("")
# GOP 与关键帧占比:关键帧按 P 帧的 5 倍估
for gop_sec in (1, 2, 4):
    kf = 25.0 / gop_sec                  # 每秒关键帧数
    share = kf * 5 / (kf * 5 + (25 - kf))  # 关键帧字节占比估算
    print("GOP %d 秒 → 每秒 %.0f 个关键帧,字节占比约 %.0f%%"
          % (gop_sec, kf, share * 100))

实跑输出:

--------------------------------------------------------------------
直播拉流——首屏为什么要等一个 GOP
--------------------------------------------------------------------
**① 拉流的三个等待**

  等待           来源                      典型值
  -----------------------------------------------------------
  TCP 连接       三次握手 + 服务器调度     10~50 毫秒
  等关键帧       必须从 IDR 开始才能解     半个到两个 GOP
  客户端缓冲     播放器自己攒几帧再播      200~500 毫秒

**② 算一下「等一个 GOP」有多久**

  25 fps、GOP 25 帧 → 最坏要等 1.0 秒(平均 0.5 秒)
  25 fps、GOP 50 帧 → 最坏要等 2.0 秒(平均 1.0 秒)
  30 fps、GOP 60 帧 → 最坏要等 2.0 秒(平均 1.0 秒)
  25 fps、GOP 250 帧 → 最坏要等 10.0 秒(平均 5.0 秒)

  **最坏情况是「刚好错过关键帧」,平均情况是「等半个 GOP」。**
  **所以 GOP 是首屏延迟的第一决定因素,比网络快慢重要得多。**

**③ 关键帧间隔怎么选**

  **① 电商直播、连麦**:GOP 设 1 到 2 秒,**宁可码率高一截也要低延迟。**
  **② 秀场、游戏**:GOP 2 秒左右,**折中。**
  **③ 监控、录播转直播**:GOP 4 秒甚至更长,**省带宽优先。**

  **但 GOP 越长,丢包后恢复越慢。**
  **丢了一个关键帧,要等到下一个关键帧才能恢复——GOP 4 秒意味着最坏卡 8 秒。**
  **这就是「低延迟」与「抗丢包」的正面冲突,没有两全的设法。**

  **还有一个常被忽略的:RTMP 没有「请求关键帧」的机制。**
  **HLS 与 WebRTC 都能让服务端立刻补一个 IDR,RTMP 不能**——**它只能等发送端自己按 GOP 出。**
  **所以 RTMP 链路上「首屏慢」基本无解,只能靠缩短 GOP 硬顶。**

三个等待里,只有「等关键帧」是协议与参数决定的,另外两个是工程选择。 GOP 250 帧那组最坏要等 10 秒——这就是很多「监控转直播」场景首屏特别慢的原因。 GOP 从 4 秒压到 1 秒,关键帧字节占比从约 17% 涨到约 44%(按关键帧是 P 帧 5 倍估),码率压力很实在。

30 秒实验

  1. 把 GOP 从 25 改成 50,算首屏最坏等待——正好翻倍。
  2. 把帧率从 25 改成 15 而 GOP 保持 25,算首屏最坏等待——变成 1.67 秒,因为关键帧间隔的秒数变长了。
  3. 把「关键帧是 P 帧 5 倍」改成 10 倍,看字节占比怎么变——短 GOP 的代价会更明显。

常见误区

  1. 「拉流快慢主要看网速」——首屏大头是等关键帧,跟带宽关系没那么大。
  2. 「GOP 越短越好」——码率会涨,而且丢包时错误传播更频繁。
  3. 「RTMP 可以请求关键帧」——不能,只能等发送端按 GOP 出。
自测三问(点开对答案) 1. 拉流的三个等待是什么? —— **TCP 连接、等关键帧、客户端缓冲。**最大的一块是等关键帧。 2. 为什么 25 fps 配 GOP 50 与 30 fps 配 GOP 60 首屏一样? —— **因为关键帧间隔都是 2 秒。**决定首屏的是秒数,不是帧数。 3. GOP Cache 是什么? —— **服务端在内存里留着最近一个关键帧**,新观众进来立刻发,减少首屏等待。

相关笔记