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

点播、直播、事件三种模式,判据只有一个:有没有 #EXT-X-ENDLIST。 列表只增还是滚动,决定了观众能不能回看。

📝 本节和前面几节的关系

5.2.1 认清了 m3u8 的语法,这一节看它在时间上怎么变。 5.2.3 转向清单里指的那些 .ts 文件本身。

一、三种模式的区别

点播 VOD:有 #EXT-X-ENDLIST,列表是全集,永不删片。 进度条能随便拖。 直播 LIVE:没有 #EXT-X-ENDLIST,只留最近几个切片。 只能看尾部,回看有限。 事件 EVENT:开始不加 ENDLIST,播完才加。 列表只增不删,已播部分可以拖回去。

判据就一条:有没有 #EXT-X-ENDLIST。 有就是「播完了」,播放器会显示总时长与可拖动进度条。 没有就是「还在生成」,播放器会定期重新拉列表。

事件模式是个折中: 它解决「直播想让人回看前面」的需求,代价是列表越来越长。 播十几个小时后,m3u8 本身就能有几百 KB,刷新一次很浪费。

二、滚动窗口是怎么滚的

直播时服务器只保留最近 N 个切片。 每生成一个新切片,最老的一个就被淘汰,列表整体前移一格。

实测窗口 3 个、喂 7 个切片的滚动过程: 第 1 轮 MEDIA-SEQUENCE 是 0,列表里是 seg0、seg1、seg2。 第 5 轮序号变 4,列表滑到 seg4、seg5、seg6。

注意第 1 轮只有 3 个,到第 5 轮窗口滑到了 seg4 到 seg6。 新观众进来只能从 seg4 开始,所以直播的「回看」是有限的。 这正是 HLS 直播延迟的一部分来源。

「回看」(DVR)能留多长,完全由 hls_list_size 决定。 设 3 就是留 3 个切片,设 0 就是不删——那就变成事件模式了。

三、为什么点播能随便拖

因为点播的 m3u8 里写了全部切片的 #EXTINF, 播放器算得出总时长,也知道每个时间点对应哪个切片。 拖动就是「按时间查切片序号,然后发一个 HTTP GET」。 不需要服务端做任何特殊支持——这是 HLS 能架在纯静态存储上的根本原因。

直播拖不动,不是「技术做不到」,而是「数据不在」: 被淘汰的切片已经删了,就算播放器想拖也取不到。 所以「能不能拖」本质上是「服务端留了多少」。

还有一个细节:拖到某个位置后,播放器必须从那个切片的关键帧开始解。 因为每个切片都以关键帧开头,所以 HLS 的拖动天然是「按切片对齐」的, 精度就是一个切片时长。

四、动手 Demo:播放模式——ENDLIST 有无决定点播还是直播

先摆三种模式的对照,再模拟一次「窗口 3 个、喂 7 片」的滚动。 滚动时要盯住 MEDIA-SEQUENCE——它才是观众「能回到哪儿」的边界。

models = [("点播 VOD", "有", "全量,不删", "能拖"),
          ("直播 LIVE", "无", "只留最近几个", "只能看尾部"),
          ("事件 EVENT", "最后才加", "只增不删", "已播部分可拖")]
for m, e, ln, seek in models:
    print("%-10s ENDLIST=%-6s %-12s %s" % (m, e, ln, seek))

print()
win, total = 3, 7
for t in range(1, total - win + 2):
    seq = t - 1
    segs = ["seg%d.ts" % i for i in range(seq, seq + win)]
    print("第 %d 轮:MEDIA-SEQUENCE=%d,%s" % (t, seq, "、".join(segs)))

print()
# 点播的列表里每片都有 EXTINF,累加就是总时长
durs = [4.0] * total
print("点播总时长 %.1f 秒,能拖到任意切片边界" % sum(durs))

实跑输出:

--------------------------------------------------------------------
播放模式——ENDLIST 有无决定点播还是直播
--------------------------------------------------------------------
**① 三种模式**

  模式           ENDLIST      列表长度         能否拖
  -------------------------------------------------------------
  点播 VOD       有           全量,不删       能拖
  直播 LIVE      无           只留最近几个     只能看尾部
  事件 EVENT     最后才加     只增不删         已播部分可拖

  **判据只有一个:有没有 `#EXT-X-ENDLIST`。**
  **有就是「播完了」,播放器会显示总时长与可拖动进度条。**
  **没有就是「还在生成」,播放器会定期重新拉列表。**

**② 模拟一个滚动窗口**

  **直播时服务器只保留最近 N 个切片,列表每生成一个新切片就滚动一次。**
  第 1 轮:MEDIA-SEQUENCE=0,切片 seg0.ts、seg1.ts、seg2.ts
  第 2 轮:MEDIA-SEQUENCE=1,切片 seg1.ts、seg2.ts、seg3.ts
  第 3 轮:MEDIA-SEQUENCE=2,切片 seg2.ts、seg3.ts、seg4.ts
  第 4 轮:MEDIA-SEQUENCE=3,切片 seg3.ts、seg4.ts、seg5.ts
  第 5 轮:MEDIA-SEQUENCE=4,切片 seg4.ts、seg5.ts、seg6.ts

  **注意第 1 轮只有 3 个,到第 5 轮窗口滑到了 seg4 到 seg6。**
  **新观众进来只能从 seg4 开始,所以直播的「回看」是有限的。**
  **这正是 HLS 直播延迟的一部分来源。**

**③ 为什么点播能随便拖**

  **因为点播的 m3u8 里写了全部切片的 EXTINF,播放器算得出总时长,也知道每个时间点对应哪个切片。**
  **拖动就是「按时间查切片序号,然后发一个 HTTP GET」。**
  **不需要服务端做任何特殊支持**——**这是 HLS 能架在纯静态存储上的根本原因。**

第 1 轮到第 5 轮,序号从 0 加到 4——窗口固定 3 个,序号只增不减。 新观众永远只能看到「最后 3 个」,他能回到的最早位置是 seg4。 总时长 28 秒是按 7 片 × 4 秒算的,真实直播里这个数会一直变。

30 秒实验

  1. 把 win 改成 6,看第 1 轮有几个切片——只有 6 个,因为总共才喂 7 片,达不到窗口上限。
  2. 把 total 改成 20,看轮数——18 轮,说明窗口固定时轮数与总片数线性相关。
  3. 把每片时长改成 2.0,看点播总时长——14 秒,总时长只跟片数与片长有关。

常见误区

  1. 「没有 ENDLIST 就是直播、有就是点播」——大体对,但事件模式中途也没有。
  2. 「直播不能拖动是播放器的限制」——不是,是旧切片被删了。
  3. 「MEDIA-SEQUENCE 会跟着窗口回退」——不会,它只增,这是关键。
自测三问(点开对答案) 1. 三种模式的唯一判据是什么? —— **有没有 `#EXT-X-ENDLIST`**;事件模式播完才加。 2. 直播的回看长度由什么决定? —— **`hls_list_size`**,设几个就留几个切片。 3. 为什么拖动精度是一个切片时长? —— **每个切片都以关键帧开头**,只能按切片边界对齐。

相关笔记