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

「完整」不是「能推能看」,是六块都得有。 三条容量线里先撞哪条,取决于网卡多大。

📝 本节和前面几节的关系

14.1 讲了架构,这一节讲一个真系统要装哪几块。 14.2.2 换成现成的开源项目来对比。

一、六块拼图

一个能长期跑的直播系统,至少要六块:

模块 做什么 少了会怎样


接入 RTMP 1935、HTTP-FLV 8080 收推流,发播放 切片 HLS 的 m3u8 与 ts 兼容性兜底 录像 按流名建目录 回看与取证 鉴权 推流密钥、播放 token 防蹭流 状态 HTTP 接口报在线数 监控与调度 守护 进程退出自动拉起 夜里不宕

前两块是功能,后四块是「能不能长期跑」。

教材里的示例系统通常只做前两块, 所以拿示例直接上线,第一周就会遇到「被人白蹭流量」和「半夜挂了没人知道」。

这四块里最容易被忽略的是「状态」: 没有在线数接口,就没法做调度,也没法知道「到底几个人在看」。 而它恰恰是最便宜的——一个返回 JSON 的 HTTP 接口,二十行代码。

二、最小系统的三个端口

一个最小直播系统只需要三个端口,各管一件事:

端口 用途 能绑吗


1935 RTMP 推流与拉流 能 8080 HTTP-FLV 与状态接口 能 8000 HLS 静态目录 能

这三个端口在所有教程里都长这样,不是巧合——它们已经成了事实标准。 1935 是 RTMP 的默认端口,8080 是 HTTP 的备用端口,8000 是 Nginx 的常用端口。

「能绑」只说明本机没被占,不代表外面能连—— 云服务器还要放行安全组,这一条在第 11.3.1 节已经栽过一次。

还有一个容易忽略的点:HLS 的目录必须能被 Web 服务器直接读到。 所以切片目录通常直接放在 Nginx 的 html 目录下, 而不是随便找个地方——否则观众请求 m3u8 时就是 404。

三、三条容量线,先撞哪条

一台机器能扛多少路,取决于三条线里最低的那条:

网卡 CPU 限 内存限 带宽限 上限 卡在哪


100 Mbps 800 2048 50 50 带宽 1 Gbps 800 2048 500 500 带宽 10 Gbps 800 2048 5000 800 CPU

注意最后一列:三条线的交叉点会随网卡变。 100 Mbps 与 1 Gbps 时卡在带宽(50 与 500 路),到了 10 Gbps 就变成 CPU 先撞(800 路)。

所以「先撞哪条」不是固定的,取决于你的网卡有多大: 小水管时代花钱买带宽,大水管时代花钱买核。

这也解释了为什么直播比点播贵——点播可以缓存,直播不能。 同样一路流,点播一天只出一次,直播每个观众都要出一份。

四、录像要多少盘

录像容量一算就清楚,但算完通常会吓一跳:

时长 一路流 一百路流


1 小时 0.9 GB 90 GB 1 天 21.6 GB 2160 GB 30 天 648.0 GB 64800 GB

一路 2 Mbps 的流录一天是 21.6 GB,一百路就是 2160 GB。 而且这还是不算索引与冗余的裸容量。

所以「录像保留三十天」这个需求,在提之前最好先算一遍—— 它往往比服务器本身贵十倍。

真想省,有三条路: ① 降码率单独录一份(不要把推流那份原样存下来); ② 只录关键时段(比如只在有人观看时录); ③ 转成 H.265 存,同样画质能省三四成。 三条路的共同点是:先想清楚「录来干什么」,再决定录什么。

五、动手 Demo:完整的流媒体服务器系统——六块拼图与三条容量线

先真绑三个端口、真写一份 HLS 索引,看一个最小系统长什么样。 再把容量与存储按真实参数算一遍。

import os, socket

# 1. 三个端口各管一件事,真绑一遍
for name, port in (("RTMP", 1935), ("HTTP", 8080), ("HLS", 8000)):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        s.bind(("0.0.0.0", port))
        s.listen(4)
        print(name, port, "能绑")
    except OSError as e:
        print(name, port, "绑不上", e)
    finally:
        s.close()

# 2. 真写一份 HLS 索引,三片各 2 秒
os.makedirs("hls/livestream", exist_ok=True)
with open("hls/livestream/index.m3u8", "w") as f:
    f.write("#EXTM3U\n#EXT-X-VERSION:3\n")
    f.write("#EXT-X-TARGETDURATION:2\n")
    for i in range(3):
        f.write("#EXTINF:2.000,\nseg%d.ts\n" % i)
    f.write("#EXT-X-ENDLIST\n")

# 3. 三条容量线,取最小值
for nic, mbps in (("100 Mbps", 100), ("1 Gbps", 1000), ("10 Gbps", 10000)):
    by_cpu = int(8 / 0.01)
    by_mem = int(8192 / 4)
    by_bw = int(mbps / 2.0)
    print(nic, "上限", min(by_cpu, by_mem, by_bw))

实跑输出:

--------------------------------------------------------------------
完整的流媒体服务器系统——六块拼图与三条容量线
--------------------------------------------------------------------
**① 「完整」不是「能推能看」,是六块都得有**

  模块     做什么                       少了会怎样
  --------------------------------------------------------
  接入     RTMP 1935、HTTP-FLV 8080     收推流,发播放
  切片     HLS 的 m3u8 与 ts            兼容性兜底
  录像     按流名建目录                 回看与取证
  鉴权     推流密钥、播放 token         防蹭流
  状态     HTTP 接口报在线数            监控与调度
  守护     进程退出自动拉起             夜里不宕

  **前两块是功能,后四块是「能不能长期跑」。**
  **教材里的示例系统通常只做前两块,**
  **所以拿示例直接上线,第一周就会遇到「被人白蹭流量」和「半夜挂了没人知道」。**

**② 真起一个最小系统**

  **三个端口分别对应接入、状态、切片目录。这里真绑一遍,再真写一份索引。**

  端口     用途                    能绑吗
  -------------------------------------------
  1935     RTMP 推流与拉流         能
  8080     HTTP-FLV 与状态接口     能
  8000     HLS 静态目录            能

  **索引文件写了 143 字节,三片,每片 2 秒,一共 6 秒的内容。**
  **「能绑」只说明本机没被占,不代表外面能连——**
  **云服务器还要放行安全组,这一条在第 11.3.1 节已经栽过一次。**

**③ 三条容量线,先撞哪条**

  网卡         CPU 限     内存限     带宽限     上限     卡在哪
  -----------------------------------------------------------------
  100 Mbps     800        2048       50         50       带宽
  1 Gbps       800        2048       500        500      带宽
  10 Gbps      800        2048       5000       800      CPU

  **注意最后一列:三条线的交叉点会随网卡变。**
  **100 Mbps 与 1 Gbps 时卡在带宽(50 与 500 路),到了 10 Gbps 就变成 CPU 先撞(800 路)。**

  **所以「先撞哪条」不是固定的,取决于你的网卡有多大:**
  **小水管时代花钱买带宽,大水管时代花钱买核。**
  **这也解释了为什么直播比点播贵——点播可以缓存,直播不能。**
  **同样一路流,点播一天只出一次,直播每个观众都要出一份。**

**④ 录像要多少盘,一算就清楚**

  时长       一路流       一百路流
  ------------------------------------
  1 小时     0.9 GB       90 GB
  1 天       21.6 GB      2160 GB
  30 天      648.0 GB     64800 GB

  **一路 2 Mbps 的流录一天是 21.6 GB,一百路就是 2160 GB。**
  **而且这还是不算索引与冗余的裸容量。**
  **所以「录像保留三十天」这个需求,在提之前最好先算一遍——**
  **它往往比服务器本身贵十倍。**

1935、8080、8000 三个端口都能绑上,说明本机没被别的服务占着。 索引写了 143 字节,三片各 2 秒,一共 6 秒的内容。 容量那条线:100 Mbps 与 1 Gbps 都卡在带宽(50 与 500 路), 到了 10 Gbps 才轮到 CPU(800 路)——因为带宽限一下涨到了 5000。 三条线永远是取最小值,而最小值会随网卡变化。 所以扩容前先看「现在卡在哪」,再决定花钱买什么。

30 秒实验

  1. 把 HLS 目录放到别的盘、不做软链,猜播放会怎样——请求 m3u8 返回 404,因为 Web 服务器读不到那个目录。
  2. 把 1935 改成 1936,猜推流端要改什么——推流地址里的端口,漏改就一直连不上。
  3. 一百路流保留三十天又不做降码率,猜要多少盘——64.8 TB 裸容量,做冗余还要翻倍。

常见误区

  1. 「能推能看就是完整系统」——少了鉴权、状态、守护就上不了线。
  2. 「容量就是 CPU 决定的」——带宽往往先撞,看网卡多大。
  3. 「录像占不了多少空间」——一路一天 21.6 GB,一百路就是两 TB。
自测三问(点开对答案) 1. 最小直播系统的三个端口分别干什么? —— **1935 收 RTMP、8080 发 HTTP-FLV 与状态、8000 放 HLS 文件**。 2. 三条容量线怎么取? —— **取最小值**,而且最小值会随网卡大小变化。 3. 一路 2 Mbps 录一天要多少空间? —— **21.6 GB**,一百路就是 2160 GB。

相关笔记