🎬 第十四章 FFmpeg直播应用综合案例分析
📄 一句话概括

一条直播链路有五个角色,任何一段出问题都表现成「卡」。 延迟是六段加起来的,能调的只有一段。

📝 本节和前面几节的关系

前十三章一直在讲单点:协议、服务器、网页。这一章把它们串成一条线。 14.2 给一个完整系统的样子。

一、五个角色,各自只管一段

一条直播链路上有五个角色,它们的分工非常干净:

角色 干什么 失败时的现象


采集端 摄像头与麦克风出原始帧 没画面,或者一直黑 编码器 把原始帧压成码流 画面卡但音频正常,多半是 CPU 顶不住 推流端 把码流按协议送给服务器 一直重连,服务器看不到这条流 分发服务器 一份流复制给 N 个观众 有人能看有人不能,通常是带宽或连接数 播放端 解码并渲染 转圈、花屏、音画不同步

这五段是串行的,任何一段出问题都会表现成「直播卡」。

所以排障的第一步不是看日志,是先判断「卡在哪一段」: 推流端自己预览流畅、观众卡,问题在推流之后; 所有人都卡,问题在编码或推流;只有部分人卡,问题在分发或播放。

这个判断顺序很重要,因为五段的日志分散在五个地方。 不先缩小范围,就会一头扎进「服务器日志里看不出问题」的死循环。

二、延迟是六段加起来的

端到端延迟不是一个数,是六个环节的累加:

环节 典型耗时 为什么


采集 33 毫秒 30 帧一秒,每帧等 33 毫秒 编码 20 毫秒 硬件编码一段 40 毫秒的片子约 20 毫秒 推流缓冲 40 毫秒 至少凑满一个 GOP 才好切 服务器转发 5 毫秒 内存里拷一份,几乎不耗时 网络 30 毫秒 同城一个往返 播放器缓冲 300 毫秒 抗抖动的代价,可调 合计 428 毫秒 这就是「端到端延迟」

六段加起来大约 0.4 秒。 这里面只有最后一段是能随便调的,其余五段都是硬成本,想省只能换技术路线。

所以「把延迟从 3 秒降到 1 秒」的真实做法是: 先砍播放器缓冲(300 降到 100),再砍推流缓冲(40 降到 10), 剩下的采集与编码几乎动不了——那是物理下限。

注意这 0.4 秒是「理想值」。 教材里那个 3 秒的数字,通常是 HLS 路线——它还得再加一个切片的等待。 同一个系统,换协议就能差一个数量级,这就是前面第五章与第十三章的区别。

三、带宽是观众的乘法

观众数一多,带宽不是线性增长,是乘法:

观众数 单机直出 边缘数 源站出口


100 200 Mbps 1 2 Mbps 1000 2.0 Gbps 10 20 Mbps 10000 20.0 Gbps 100 200 Mbps

一千人看 2 Mbps,单机直出就要 2 Gbps 出口——一台机器做不到。 所以才有「边缘」:源站只出 100 路,每个边缘再复制给一百人。 源站出口从 20.0 Gbps 降到 200 Mbps,省了两个数量级。

但边缘不是白来的: 每个边缘都是一台机器、一份带宽、一份运维。 所以「要不要上边缘」不是技术问题,是「观众在哪儿」的问题。 观众集中在一个城市,一个边缘就够;全国分散,就得按区域铺。

这也是为什么大厂自己做 CDN,小厂买 CDN: 边缘的边际成本很低,但起步成本很高。 观众不到一定量级,自建边缘比买还贵。

四、三种架构,按规模选

规模不同,架构就不同,没有「最好的架构」这回事:

架构 适用规模 关键瓶颈


单机直出 几十人 网卡出口 源站加边缘 几千到几万 源站出口与边缘调度 多级加 P2P 十万以上 调度复杂度

教材这一章给的案例是第二种:源站加边缘。 这也是现实里最常见的一种,因为它刚好卡在「单机扛不住、又不需要自建 CDN」之间。

SRS 的集群配置(第 11.3 节)、Nginx 的多层反代(第十章),讲的都是这一种。 它们解决的是同一个问题:怎么让一份流,变成很多份,而源站只出一份。

第三种(多级加 P2P)在教材里没细讲,但它值得知道: 它的思路是让观众之间互相转发,进一步压低服务器带宽。 代价是调度极其复杂——谁给谁传、传多少、怎么防止作弊,每一项都是硬骨头。

五、动手 Demo:直播系统架构简介——一条链路上有五个角色

先把五个角色与它们失败时的现象列清楚。 再真算延迟预算与带宽阶梯,看瓶颈在哪一环。

STAGES = [("采集", 33), ("编码", 20), ("推流缓冲", 40),
          ("服务器转发", 5), ("网络", 30), ("播放器缓冲", 300)]

total = sum(ms for _n, ms in STAGES)
print("端到端延迟", total, "毫秒")

# 只有播放缓冲能调,看调到 100 能省多少
for cache in (300, 200, 100):
    t = total - 300 + cache
    print("播放缓冲", cache, "毫秒 -> 总计", t, "毫秒")

# 带宽阶梯:2 Mbps 一路,按一百人一个边缘算
kbps = 2000
for n in (100, 1000, 10000):
    direct = n * kbps / 1000.0
    edges = max(1, n // 100)
    origin = edges * kbps / 1000.0
    print(n, "人:直出", direct, "Mbps,边缘",
          edges, "个,源站", origin, "Mbps")

实跑输出:

--------------------------------------------------------------------
直播系统架构简介——一条链路上有五个角色
--------------------------------------------------------------------
**① 五个角色,各自只管一段**

  角色           干什么                     失败时的现象
  ----------------------------------------------------------------------------------
  采集端         摄像头与麦克风出原始帧     没画面,或者一直黑
  编码器         把原始帧压成码流           画面卡但音频正常,多半是 CPU 顶不住
  推流端         把码流按协议送给服务器     一直重连,服务器看不到这条流
  分发服务器     一份流复制给 N 个观众      有人能看有人不能,通常是带宽或连接数
  播放端         解码并渲染                 转圈、花屏、音画不同步

  **这五段是串行的,任何一段出问题都会表现成「直播卡」。**
  **所以排障的第一步不是看日志,是先判断「卡在哪一段」:**
  **推流端自己预览流畅、观众卡,问题在推流之后;**
  **所有人都卡,问题在编码或推流;只有部分人卡,问题在分发或播放。**

**② 延迟是六段加起来的,不是某一处**

  环节           典型耗时     为什么
  --------------------------------------------------------------------
  采集           33 毫秒      30 帧一秒,每帧等 33 毫秒
  编码           20 毫秒      硬件编码一段 40 毫秒的片子约 20 毫秒
  推流缓冲       40 毫秒      至少凑满一个 GOP 才好切
  服务器转发     5 毫秒       内存里拷一份,几乎不耗时
  网络           30 毫秒      同城一个往返
  播放器缓冲     300 毫秒     抗抖动的代价,可调
  合计           428 毫秒     这就是「端到端延迟」

  **六段加起来大约 0.4 秒。**
  **这里面只有最后一段(播放器缓冲)是能随便调的,**
  **其余五段都是硬成本,想省只能换技术路线。**

  **所以「把延迟从 3 秒降到 1 秒」的真实做法是:**
  **先砍播放器缓冲(300 降到 100),再砍推流缓冲(40 降到 10),**
  **剩下的采集与编码几乎动不了——那是物理下限。**
  **想再往下,只能换 WebRTC,把分发与网络这两段一起重做。**

**③ 带宽是观众数的乘法,不是加法**

  观众数     单机直出      边缘数     源站出口
  ------------------------------------------------
  100        200 Mbps      1          2 Mbps
  1000       2.0 Gbps      10         20 Mbps
  10000      20.0 Gbps     100        200 Mbps

  **一千人看 2 Mbps,单机直出就要 2 Gbps 出口——一台机器做不到。**
  **所以才有「边缘」:源站只出 100 路,每个边缘再复制给一百人。**
  **源站出口从 20.0 Gbps 降到 200 Mbps,省了两个数量级。**

  **但边缘不是白来的:**
  **每个边缘都是一台机器、一份带宽、一份运维。**
  **所以「要不要上边缘」不是技术问题,是「观众在哪儿」的问题。**
  **观众集中在一个城市,一个边缘就够;全国分散,就得按区域铺。**

**④ 三种架构,按规模选**

  架构           适用规模       关键瓶颈
  ----------------------------------------------------
  单机直出       几十人         网卡出口
  源站加边缘     几千到几万     源站出口与边缘调度
  多级加 P2P     十万以上       调度复杂度

  **教材这一章给的案例是第二种:源站加边缘。**
  **这也是现实里最常见的一种,因为它刚好卡在「单机扛不住、又不需要自建 CDN」之间。**
  **SRS 的集群配置、Nginx 的多层反代,讲的都是这一种。**

六段加起来 428 毫秒,也就是 0.4 秒。 把播放缓冲从 300 降到 100,总计掉到 228 毫秒——省了一半还多。 而其余五段一共才 128 毫秒,再怎么优化也就省几十毫秒。 这就是「优化延迟」在实操上总是先动播放器参数的原因。 带宽那张表更直接:一万人直出要 20 Gbps,上边缘只出 200 Mbps。 两个数量级的差距,全靠「一份变一百份」这件事发生在边缘而不是源站。

30 秒实验

  1. 把 GOP 从 25 帧改成 50 帧,猜推流缓冲怎么变——变长,因为要凑满更久才切一片;延迟上升,码率下降。
  2. 把播放缓冲降到 0,猜画面会怎样——延迟最低,但网络一抖就卡住,因为没有任何余量。
  3. 一万人不上边缘直接连源站,猜先坏哪——网卡先满,然后所有人都卡,包括原本正常的那些人。

常见误区

  1. 「延迟就是推流那一下」——它是六段累加,每段都占一份。
  2. 「观众多了加机器就行」——带宽是乘法,加机器不解决出口。
  3. 「架构越先进越好」——几十个人用边缘,运维成本反而更高。
自测三问(点开对答案) 1. 端到端延迟里,哪一段最有调整空间? —— **播放器缓冲**,它就是为了抗抖动而故意加的。 2. 为什么一千人看 2 Mbps 就要 2 Gbps 出口? —— **服务器要给每个人各发一份**,不是发一份广播出去。 3. 边缘分发省的是哪一段带宽? —— **源站出口**,一份流只出到边缘,边缘再复制给观众。

相关笔记