🎬 第五章 HLS流媒体协议
📄 一句话概括

m3u8 的标签按用途可分五类:结构、直播、切片、加密、低延迟。 真正常用的不到十个,其余都是特定场景才需要。

📝 本节和前面几节的关系

5.2.1 只讲了四个必备标签,这一节把标签系统看全。 5.4 转去讲 TS 与 PS 的分工,那是「切片里装什么」的问题。

一、标签分五类

① 结构类:#EXTM3U、#EXT-X-VERSION。 这类决定文件本身怎么被读,必看。 ② 直播类:#EXT-X-TARGETDURATION、#EXT-X-MEDIA-SEQUENCE。 决定刷新节奏与序号起算,必看。 ③ 切片类:#EXTINF、#EXT-X-BYTERANGE、#EXT-X-DISCONTINUITY。 按需看,做广告插入或单文件切片时必须懂。 ④ 加密类:#EXT-X-KEY、#EXT-X-MAP。 用 AES-128 加密时才需要。 ⑤ 低延迟类:#EXT-X-PART、#EXT-X-PRELOAD-HINT。 只有 LL-HLS 才出现。

所以「学 m3u8」并不需要背全部标签。 记住结构类与直播类四个,再认得 #EXT-X-KEY 长什么样, 日常排查就够用了。

二、逐行分类一份真列表

拿一份带多看点的列表来逐行看,一共 12 行: 全局标签四行:EXTM3U、VERSION:7、TARGETDURATION:6、MEDIA-SEQUENCE:2048。 不连续点两行:#EXT-X-DISCONTINUITY-SEQUENCE:2 与 #EXT-X-DISCONTINUITY。 加密一行:#EXT-X-KEY:METHOD=AES-128,URI="key.bin"。 切片相关四行:两个 #EXTINF 加两个 .ts 文件名。 结束一行:#EXT-X-ENDLIST。

这份列表有个细节值得注意: MEDIA-SEQUENCE 已经到 2048,说明这场直播已经播了两千多片。 如果每片 6 秒,那就是三个多小时。 序号是「历史总片数」,不是「当前保留几个」——这一点最容易搞混。

三、两个最容易被忽视的标签

① #EXT-X-DISCONTINUITY:表示「时间轴在这里不连续」。 广告插入、码率切换、服务器切换都会插它。 播放器看到它会把解码器重置,否则画面会花。 ② #EXT-X-DISCONTINUITY-SEQUENCE:给不连续点编号。 直播滚动时要靠它让新观众知道「已经跳了几次」。 漏了它,从中间进来的观众时间轴会乱。

举个具体场景:直播中途插了一段广告。 广告前后各有一个 DISCONTINUITY。 如果播放器不重置解码器,广告结束的第一帧会引用广告之前的参考帧, 画面就会花屏几帧——这是很多人在做广告插入时踩的坑。

顺带一条工程建议: 自己生成 m3u8 时,只要「切片的 PTS 出现跳变」就补一个 DISCONTINUITY。 宁可多插,不要漏插——多插只是多一次解码器重建,代价很小。

四、动手 Demo:m3u8 格式——哪些标签必须懂,哪些可以不管

先按五类列出常用标签,再拿一份 12 行的真列表逐行分类、量长度。 最后盯住 DISCONTINUITY 这一对——它是广告插入能不能不花屏的关键。

import io, os, tempfile

txt = ("#EXTM3U\n#EXT-X-VERSION:7\n#EXT-X-TARGETDURATION:6\n"
       "#EXT-X-MEDIA-SEQUENCE:2048\n"
       "#EXT-X-DISCONTINUITY-SEQUENCE:2\n"
       "#EXT-X-KEY:METHOD=AES-128,URI=\"key.bin\"\n"
       "#EXTINF:6.006,\nseg2048.ts\n"
       "#EXT-X-DISCONTINUITY\n"
       "#EXTINF:6.006,\nseg2049.ts\n"
       "#EXT-X-ENDLIST\n")

kinds = {
    "#EXTM3U": "全局", "#EXT-X-VERSION": "全局",
    "#EXT-X-TARGETDURATION": "全局", "#EXT-X-MEDIA-SEQUENCE": "全局",
    "#EXT-X-DISCONTINUITY-SEQUENCE": "不连续",
    "#EXT-X-DISCONTINUITY": "不连续",
    "#EXT-X-KEY": "加密", "#EXTINF": "时长",
}
for ln in txt.splitlines():
    tag = ln.split(":", 1)[0]
    kind = kinds.get(tag, "切片 URI" if not ln.startswith("#") else "其他")
    print("%-42s %-8s %d" % (ln, kind, len(ln)))

print()
# 不连续点前后的切片,PTS 应该能对上两个不同的时间基准
print("DISCONTINUITY 出现 %d 次,SEQUENCE 记为 %d"
      % (txt.count("#EXT-X-DISCONTINUITY\n"), 2))

实跑输出:

--------------------------------------------------------------------
m3u8 格式——哪些标签必须懂,哪些可以不管
--------------------------------------------------------------------
**① 常用标签分三类**

  类别         标签                                              是否必看
  ------------------------------------------------------------------------------
  结构类       #EXTM3U #EXT-X-VERSION                            必看
  直播类       #EXT-X-TARGETDURATION #EXT-X-MEDIA-SEQUENCE       必看
  切片类       #EXTINF #EXT-X-BYTERANGE #EXT-X-DISCONTINUITY     按需
  加密类       #EXT-X-KEY #EXT-X-MAP                             AES 时要看
  低延迟类     #EXT-X-PART #EXT-X-PRELOAD-HINT                   LL-HLS 才看

**② 真拿一份带多看点的列表来逐行分类**

  行                                          类型         长度
  -----------------------------------------------------------------
  #EXTM3U                                     全局标签     7
  #EXT-X-VERSION:7                            全局标签     16
  #EXT-X-TARGETDURATION:6                     全局标签     23
  #EXT-X-MEDIA-SEQUENCE:2048                  全局标签     26
  #EXT-X-DISCONTINUITY-SEQUENCE:2             不连续点     31
  #EXT-X-KEY:METHOD=AES-128,URI="key.bin"     加密         39
  #EXTINF:6.006,                              切片时长     14
  seg2048.ts                                  切片 URI     10
  #EXT-X-DISCONTINUITY                        不连续点     20
  #EXTINF:6.006,                              切片时长     14
  seg2049.ts                                  切片 URI     10
  #EXT-X-ENDLIST                              全局标签     14

**③ 两个最容易被忽视的标签**

  **① `#EXT-X-DISCONTINUITY`**:**表示「时间轴在这里不连续」。**
  **广告插入、码率切换、服务器切换都会插它。**
  **播放器看到它会把解码器重置,否则画面会花。**
  **② `#EXT-X-DISCONTINUITY-SEQUENCE`**:**给不连续点编号。**
  **直播滚动时要靠它让新观众知道「已经跳了几次」。**
  **漏了它,从中间进来的观众时间轴会乱。**

12 行里最长的一行是 #EXT-X-KEY:...,39 字节——密钥地址写在 URI 里。 切片 URI 那两行不带 #,所以判类型要分两步:先看有没有 #,再查表。 DISCONTINUITY 在 #EXTINF 之后、下一个 #EXTINF 之前,它的位置就是「新的时间轴从下一条开始」的意思。

30 秒实验

  1. 把 VERSION 从 7 改成 3,看哪些标签会变非法——#EXT-X-MAP 之类要 6 以上才允许。
  2. 在第二个 #EXTINF 前再加一个 DISCONTINUITY,数出现几次——变 2 次,但顺序会乱,播放器可能直接放弃。
  3. 把 #EXT-X-KEY 的 URI 改成绝对地址,看长度——长很多,这也是加密列表不能太频繁刷新的原因之一。

常见误区

  1. 「标签越多越专业」——常用就那几个,多的是特定场景才用。
  2. 「DISCONTINUITY 只影响时间轴显示」——不重置解码器会花屏。
  3. 「MEDIA-SEQUENCE 是当前列表第一片的序号」——对,但它是历史累计值。
自测三问(点开对答案) 1. m3u8 标签分哪五类? —— **结构、直播、切片、加密、低延迟**;常用的只有前两类。 2. DISCONTINUITY 是干什么的? —— **标记时间轴不连续**,让播放器重置解码器,否则会花屏。 3. MEDIA-SEQUENCE 2048 意味着什么? —— **历史上一共生成过 2048 片以上**,不等于当前列表有几片。

相关笔记