目录
[一、先定边界:M3U8 是什么,不是什么](#一、先定边界:M3U8 是什么,不是什么)
[三、两套 Playlist:Master 与 Media](#三、两套 Playlist:Master 与 Media)
[1️. Master Playlist(多码率入口 / Multivariant)](#1️. Master Playlist(多码率入口 / Multivariant))
[2️. Media Playlist(单码率分片序列)](#2️. Media Playlist(单码率分片序列))
[四、核心 EXT-X 标签速查(面试/排错常问)](#四、核心 EXT-X 标签速查(面试/排错常问))
[五、分片载体:TS 与 fMP4(CMAF)](#五、分片载体:TS 与 fMP4(CMAF))
[传统 TS 分片](#传统 TS 分片)
[现代 fMP4 / CMAF 分片(HLS v6+)](#现代 fMP4 / CMAF 分片(HLS v6+))
[六、ABR 自适应:M3U8 为什么能"边播边换清晰度"](#六、ABR 自适应:M3U8 为什么能“边播边换清晰度”)
[七、加密与 DRM 位置](#七、加密与 DRM 位置)
[八、"m38u" 文件转 MP4 为什么黑屏/音画歪](#八、“m38u” 文件转 MP4 为什么黑屏/音画歪)
[九、手写一个最小合规 M3U8](#九、手写一个最小合规 M3U8)
觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。
由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。
M3U8是一个纯文本的播放列表文件 ,它本身不包含视频内容,而是记录了一系列视频分片的下载地址,播放器根据它去拉取实际的视频片段。
它到底是啥
一、先定边界:M3U8 是什么,不是什么
| 维度 | 事实 |
|---|---|
| 文件本质 | 纯文本播放清单(Playlist),不是容器 |
| 编码 | UTF-8,严禁 BOM,禁控制字符(U+0000~U+001F) |
| 后缀 | .m3u8(UTF-8 版 M3U),.m3u是本地编码老版 |
| 协议归属 | Apple HLS,2017 年成 RFC 8216,草案 pantos-hls-rfc8216bis 持续演进 |
| 自己带媒体吗 | 不带 ,只存 URI + 元数据,真正音视频在 .ts/ .m4s/ .mp4分片里 |
| "m38u" | 非标准拼写,按 m3u8 处理即可 |
一句话:M3U8 是导演,TS/fMP4 是演员,HTTP 是舞台。
二、文件物理结构:行模型
M3U8 按行解析,每行只能是四类之一:
-
空行(忽略)
-
URI 行(分片地址,相对/绝对 URL 都行)
-
注释行 (
#开头但不是#EXT开头) -
标签行 (
<code>#EXT</code>开头,大小写敏感,属性列表用逗号分隔)
最小合法点播媒体列表:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
seg0.ts
#EXTINF:10.0,
seg1.ts
#EXT-X-ENDLIST
-
首行必须是
#EXTM3U,否则老客户端当普通 M3U 忽略扩展标签 -
#EXTINF:后面必须逗号,时长浮点(v3+ 推荐浮点减舍入误差) -
没有
#EXT-X-ENDLIST→ 客户端判定为直播,会周期性重拉
三、两套 Playlist:Master 与 Media
1️. Master Playlist(多码率入口 / Multivariant)
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-STREAM-INF:BANDWIDTH=2000000,AVERAGE-BANDWIDTH=1800000,RESOLUTION=1280x720,FRAME-RATE=30,CODECS="avc1.4d401f,mp4a.40.2"
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p.m3u8
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English",DEFAULT=YES,URI="audio_en.m3u8"
关键属性:
-
BANDWIDTH:峰值码率(ABR 决策主依据,必填) -
AVERAGE-BANDWIDTH:更准,防网络抖动误判 -
CODECS:RFC 6381字符串,avc1.640028=H.264 High@4.0 -
RESOLUTION/FRAME-RATE:展示用,也参与切档 -
#EXT-X-MEDIA:独立音轨 / 字幕 / 多语言,不和视频绑死
2️. Media Playlist(单码率分片序列)
点播(VOD)带 ENDLIST,直播滑动窗口不带:
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:268
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="init.mp4"
#EXTINF:6.006,
seg268.mp4
#EXTINF:6.006,
seg269.mp4
#EXT-X-PROGRAM-DATE-TIME:2026-08-11T09:00:00Z
...
直播时:MEDIA-SEQUENCE随着旧分片剔除而自增,客户端靠它做断点续播与防重复。
四、核心 EXT-X 标签速查(面试/排错常问)
| 标签 | 作用域 | 说明 |
|---|---|---|
#EXTM3U |
文件头 | 声明 Extended M3U,必首行 |
#EXT-X-VERSION |
全局 | 1~7+,特性开关(浮点 EXTINF、fMP4、LL-HLS 等) |
#EXT-X-TARGETDURATION |
Media | 所有分片时长向上取整最大值,客户端缓冲基准 |
#EXT-X-MEDIA-SEQUENCE |
Media | 首片序列号,直播滑动窗口核心 |
#EXTINF |
Media | 紧跟 URI 的时长(秒),≤ TARGETDURATION |
#EXT-X-ENDLIST |
Media | 点播终结符,直播无 |
#EXT-X-STREAM-INF |
Master | 子流(Variant)描述 |
#EXT-X-MEDIA |
Master | 备用音/字幕轨 |
#EXT-X-KEY |
Media | AES-128 / SAMPLE-AES 密钥 URL+IV |
#EXT-X-MAP |
Media | fMP4/CMAF 的 init 段(moov 等价物) |
#EXT-X-DISCONTINUITY |
Media | 时间轴断裂(插广告/切换源) |
#EXT-X-PROGRAM-DATE-TIME |
Media | 绝对时钟对齐,直播同步用 |
#EXT-X-PART |
Media | LL-HLS 局部分片(part-target 级) |
#EXT-X-SERVER-CONTROL |
Media | 低延迟阻塞重载、skip 边界 |
五、分片载体:TS 与 fMP4(CMAF)
传统 TS 分片
-
容器:MPEG-2 Transport Stream(188 字节定长包,同步字节
0x47) -
编码:H.264+ AAC最常见
-
优点:抗丢包、PID 寻址、老 iOS 全支持
-
缺点:固定 188 字节开销大,seek 粗,封装冗余高
现代 fMP4 / CMAF 分片(HLS v6+)
-
容器:ISO-BMFF碎片化(
init.mp4+segN.mp4) -
靠
#EXT-X-MAP指向 init 段,每个 seg 自带 moof/mdat -
同一份 CMAF 切片可同时被 HLS 和 DASH 引用,CDN 缓存翻倍效率
-
低延迟 HLS(LL-HLS)强制走 fMP4 +
#EXT-X-PART亚秒级分片
所以你今天抓到的 m3u8,里面可能是
seg0.ts,也可能是seg0.mp4,协议都合法。
六、ABR 自适应:M3U8 为什么能"边播边换清晰度"
-
客户端先拉 Master → 拿到多路
BANDWIDTH -
按当前吞吐 × 安全系数选一路 → 拉对应 Media
-
播放器维护一个带宽滑动窗口,缓冲低于阈值就降档,空闲就升档
-
切档发生在 分片边界(TS 边界 / CMAF Fragment 边界),不能在中途换
-
多音轨/多字幕通过 Master 里
#EXT-X-MEDIA的GROUP-ID绑定到某路视频
这就是为什么 HLS 能在 2G 到 5G 之间"无感"切 240p↔1080p。
七、加密与 DRM 位置
M3U8 本身不加密,但能指路:
#EXT-X-KEY:METHOD=AES-128,URI="https://key.example.com/k1",IV=0x1234...
-
AES-128:整段 CBC 加密,Key 明文拉取(配合 HTTPS 算"软 DRM") -
SAMPLE-AES:样本级加密,HLS+fMP4 常用cbcs方案,对接 FairPlay -
#EXT-X-SESSION-KEY:Master 里预声明,客户端可提前取密钥省首屏
八、"m38u" 文件转 MP4 为什么黑屏/音画歪
典型原因不在 m3u8 文本,而在分片合并阶段:
-
TS 里 PTS/DTS 来自 PCR,跨分片不连续 → 合并时没做
PTS重基准 -
首片不是 IDR 起始 → MP4 没 I 帧可解,黑屏
-
B 帧顺序没重写 → 解码器报
non-monotonic DTS -
moov 写太早 → 播放器找不到索引
正确姿势(FFmpeg):
ffmpeg -i index.m3u8 -copyts -avoid_negative_ts make_zero -vsync cfr out.mp4
或先 dump ts 再封:
ffmpeg -i index.m3u8 -c copy concat.ts
ffmpeg -i concat.ts -c copy out.mp4
九、手写一个最小合规 M3U8
纯音频 HLS(AAC in TS 或直链 MP3,老文档允许 .m3u思路延用):
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
https://cdn.x/aac_0.ts
#EXTINF:10.0,
https://cdn.x/aac_1.ts
#EXT-X-ENDLIST
只要 HTTP Server 把 .m3u8的 MIME 设成 application/vnd.apple.mpegurl(或 audio/mpegurl),Safari/iOS 直接认。
十、一句话总结
**M3U8 不是媒体格式,是 HLS 的"调度协议":UTF-8 文本、EXT 标签驱动、指向 TS/fMP4 分片、靠 Master/Media 两级实现 ABR、靠 HTTP 穿透一切防火墙。**
遇到
m38u先当m3u8打开;它若带 URI 能下 ts/mp4,就是 HLS;若只是改名壳子,内部一定还是上面这套行模型。