🎬 第十章 Nginx搭建直播平台
📄 一句话概括

推流地址里那一段,就是 application 的名字。 一个 application 配几行,决定它能推、能播、还是能转发。

📝 本节和前面几节的关系

10.4.1 与 10.4.2 讲的是通用结构,这一节只讲 RTMP 那一段。 这一节的内容配完就能推流,是第十章的落地点。

一、地址是怎么拼出来的

推流地址格式:rtmp://主机:端口/应用名/流名。 中间那个「应用名」就是 application 后面跟的名字。

所以配了 application live,地址里就必须写 /live/。 推 rtmp://host/abc/stream 会失败,因为 /abc/ 不存在。

这个错误的特征很好认: 推流端只报「连接断开」或者一直转圈, 服务端 error.log 里才写着「no such application」。 所以推不上流时,先看服务端日志,别在推流端反复改参数。

二、三个核心指令

① live on;:允许这个应用接收推流。 不开它,只能播不能推。 ② hls on;:同时切 HLS 片。 要配合 hls_path 指定切片目录, hls_fragment 指定片长。 切片目录必须在 http 的 web 根下面,不然播放器取不到。 ③ record 系列:把流录成文件。 record all; 录音视频,record_path 指定目录, record_unique on; 让每次录制单独一个文件。

hls_path 那条最容易配错: 它是文件系统路径,但播放器是通过 HTTP 取它的。 所以这个目录必须落在 http 块里某个 root 能覆盖到的地方。 两者对不上,切片文件生成了却播不了——这是最典型的「配了不生效」。

三、hls 的两个参数要算

hls_fragment 是片长,hls_playlist_length 是列表长度。 两个数一除,就是观众能往回拖几片。

比如片长 5 秒、列表 30 秒,就是 6 片。 观众最远能回到 30 秒前,再早的已经被删了。

这两个数的取舍是延迟与请求数的交换: 片长 5 秒,首屏就不可能低于 5 秒; 压到 2 秒,首屏快了,但请求数翻两倍半,CDN 命中率也跟着掉。 这是第五章算过的老账,在这里只是换了个地方配。

还有一个隐含约束: 切片只能切在关键帧上。 如果编码时 GOP 是 5 秒,hls_fragment 2s 也只能切出 5 秒的片。 所以配切片之前,先确认推流端的 GOP 长度。

四、动手 Demo:rtmp 块配置——一个 application 决定三类地址

真解析一份 rtmp 配置,扫出每个 application, 再按它开了什么,推出能用的地址。

CONF = """\
rtmp {
    server {
        listen 1935;
        application live {
            live on;
            hls on;
            hls_path html/hls;
            hls_fragment 5s;
        }
        application vod {
            play html/vod;
        }
    }
}"""

apps, cur = [], None
for line in CONF.splitlines():
    s = line.strip()
    if s.startswith("application "):
        cur = {"name": s.split()[1], "dirs": {}}
        apps.append(cur)
    elif cur is not None and " " in s and not s.startswith("}"):
        k, v = s.split(" ", 1)
        if k in ("live", "hls", "play", "record", "hls_fragment"):
            cur["dirs"][k] = v.rstrip(";")

for ap in apps:
    d = ap["dirs"]
    if d.get("live") == "on" and d.get("hls") == "on":
        kind = "能推能播,RTMP 与 HLS 都行"
    elif d.get("live") == "on":
        kind = "能推,只能走 RTMP"
    elif "play" in d:
        kind = "点播,只读文件"
    else:
        kind = "没开任何能力"
    print("%-6s rtmp://127.0.0.1:1935/%s/stream   %s"
          % (ap["name"], ap["name"], kind))

frag, pl = 5, 30
print("片长 %ds、列表 %ds -> 能回看 %d 片" % (frag, pl, pl // frag))

实跑输出:

--------------------------------------------------------------------
rtmp 块配置——一个 application 决定三类地址
--------------------------------------------------------------------
**① `application` 是地址里的那一段**

  **推流地址的格式是 `rtmp://主机:端口/应用名/流名`。**
  **中间那个「应用名」就是 `application` 后面跟的名字。**
  **所以配了 `application live`,推流地址里就得写 `/live/`。**

  **这一点最容易被忽略:**
  **配了 `application live`,推 `rtmp://host/abc/stream` 会失败,**
  **因为 `/abc/` 这个应用不存在。**
  **报错不在推流端,在服务端日志里,所以新手常常找不到原因。**

**② 真解析这份配置,推出所有能用的地址**

  **解析器扫一遍 `application` 块,看每个应用开了什么能力。**

  应用     访问地址                              播放方式
  -----------------------------------------------------------------------
  live     rtmp://127.0.0.1:1935/live/stream     RTMP/HLS 都能播
  vod      rtmp://127.0.0.1:1935/vod/stream      点播,地址即播放地址
  push     rtmp://127.0.0.1:1935/push/stream     只能走 RTMP

  **三个应用,三种用途,差别全在块里那几行:**

  **① `live`**:**`live on;` 允许推流,`hls on;` 同时切片。**
  **推一次,RTMP 与 HLS 都能看。**
  **② `vod`**:**`play` 指向一个目录,这是点播不是直播。**
  **推流到这里会失败,因为它只读文件。**
  **③ `push`**:**`push` 那条把流再转给另一台服务器,**
  **这就是最小规模的级联。**

**③ 几个参数的取值要算**

  **这份配置里 `hls_fragment 5s`、`hls_playlist_length 30s`。**
  **算一下:列表里能放 6 个切片,观众最多能往回拖 30 秒。**

  参数                    值       直接后果
  ---------------------------------------------------------------------
  hls_fragment            5s       起播至少要等一个切片
  hls_playlist_length     30s      最多回看六片,再早的删了
  chunk_size              4096     影响 RTMP 分块大小,不改变总字节

  **`hls_fragment` 是延迟的地板:要 5 秒切片,首屏就不可能低于 5 秒。**
  **想压到 2 秒,切片就得设 2 秒,代价是请求数翻倍、CDN 命中率下降。**
  **这是第五章算过的老账,在这里只是换了个地方配。**

地址里那一段就是 application 名,改名字地址就跟着变。 hls on 与 live on 同时开,一路流两种播法都能看。 play 那份是点播,推流到它上面会失败。 片长与列表一除就是回看片数,30 除 5 得 6。

30 秒实验

  1. 把 hls_fragment 改成 2s,看重算的回看片数——15 片,但首屏也从 5 秒降到 2 秒。
  2. 给 vod 也加一条 live on;,想想会怎样——它既能推又能播,但两个用途混在一起容易乱。
  3. 把 hls_path 改到一个 http 根之外,猜播放器能不能取到——不能,切出来了但 HTTP 那边拿不到。

常见误区

  1. 「application 只是个标签」——它就是地址里那一段。
  2. 「切片想切多短就多短」——只能切在关键帧上,受 GOP 限制。
  3. 「hls_path 随便写」——必须在 http 的根目录覆盖范围内。
自测三问(点开对答案) 1. `application live` 对应的推流地址是什么样? —— **`rtmp://主机:1935/live/流名`**,中间那段就是应用名。 2. `hls_path` 配错会有什么现象? —— **切片文件生成了,但 HTTP 取不到,播放器一直转圈。** 3. 片长 5 秒、列表 30 秒,能回看几片? —— **6 片**,也就是往回 30 秒。

相关笔记