3. RTMP协议详解
HLS 这个协议,说白了就是苹果搞的一套“切碎再拼”的玩法。我最早接触它是在 2012 年,那时候还在用 RTMP 做直播,结果苹果设备死活不支持 Flash。嗯,从那以后我就开始认真研究 HLS 了。
今天咱们把 HLS 拆开来看。核心就三样东西:m3u8 索引文件、媒体分段(TS 或 CMAF)、自适应码率逻辑。搞懂了这三块,你就能理解为什么 HLS 能成为目前互联网上最主流的直播协议之一。
3.1 m3u8 索引文件:播放列表的“地图”
m3u8 本质上就是一个文本文件,它告诉播放器“去哪里拿数据”。我习惯把它比作一张地图——地图本身不包含风景,但告诉你风景在哪。
3.1.1 单码率 vs 多码率
先看最简单的单码率 m3u8:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000,
segment_000.ts
#EXTINF:10.000,
segment_001.ts
#EXTINF:10.000,
segment_002.ts
#EXT-X-ENDLIST
这个文件告诉播放器:有 3 个分片,每个 10 秒,按顺序播。注意 #EXT-X-ENDLIST 这个标签——有它表示点播(VOD),没有它表示直播(Live)。
多码率就复杂一些了。我当年第一次配置多码率时踩过一个坑:子播放列表的路径写成了相对路径,结果 CDN 回源时找不到文件。来看标准的多码率结构:
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=640x360
low.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=854x480
mid.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5120000,RESOLUTION=1280x720
high.m3u8
播放器拿到这个“主播放列表”后,会根据当前网络带宽自动选择最合适的子列表。这里 BANDWIDTH 的单位是 bps,注意别写错了。
BANDWIDTH 值建议比实际码率高出 10%-20%。因为网络波动时,播放器需要一点“余量”来做切换决策。
3.1.2 关键标签详解
| 标签 | 作用 | 避坑点 |
|---|---|---|
#EXT-X-TARGETDURATION |
单个分片的最大时长(秒) | 所有分片时长不能超过这个值,否则播放器会报错 |
#EXT-X-MEDIA-SEQUENCE |
直播场景下,当前第一个分片的序号 | 直播时这个值会递增,播放器用它来同步 |
#EXT-X-DISCONTINUITY |
标记音视频编码参数发生变化 | 切广告或切换分辨率时一定要加,否则花屏 |
#EXT-X-KEY |
加密信息(AES-128 等) | 密钥文件路径必须是绝对 URL 或相对路径,不能混用 |
我曾经遇到过一个线上问题:直播流每隔几分钟就卡一下。查了半天,发现是 #EXT-X-DISCONTINUITY 标签没加——编码器在 GOP 边界调整了参数,但播放器不知道,结果解码器崩了。
3.2 TS 分段与 CMAF 分段
分片格式是 HLS 的“血肉”。早期清一色用 TS(MPEG-2 Transport Stream),后来苹果在 HLS 第 7 版引入了 CMAF(Common Media Application Format)。
3.2.1 TS 格式:老当益壮
TS 分片的特点:每个分片都是独立的 MPEG-2 TS 流,包含完整的 PAT/PMT 表。这意味着播放器拿到任何一个分片都能开始解码。
TS 分片的典型结构:
TS Packet (188 bytes)
├── 4 bytes: Sync byte + PID + flags
├── Adaptation Field (可选)
└── Payload (PES 数据)
├── Video (H.264/H.265)
└── Audio (AAC/MP3)
我早期做 HLS 推流时,最头疼的就是 TS 分片的对齐问题。举个例子:如果分片边界没有对齐到关键帧(IDR),播放器切换码率时就会出马赛克。
3.2.2 CMAF 格式:现代选择
CMAF 是苹果和微软联手推的“统一格式”。它基于 ISO BMFF(MP4),核心思想是:同一个分片文件,HLS 和 DASH 都能用。
CMAF 分片的结构:
CMAF Fragment
├── ftyp (文件类型)
├── moov (元数据)
├── moof (分片元数据)
│ ├── mfhd (分片头部)
│ └── traf (轨道片段)
└── mdat (媒体数据)
├── 视频样本 (关键帧 + 非关键帧)
└── 音频样本
CMAF 相比 TS 的优势:
- 文件体积更小:没有 TS 的 188 字节固定包头,节省约 5%-8% 带宽
- 支持加密更灵活:原生支持 Common Encryption (CENC)
- 兼容性更好:同一份文件同时服务 HLS 和 DASH
但 CMAF 也有坑。我记得有一次升级 CMAF 后,某些老旧机顶盒播放不了——因为它们只支持 TS 格式。所以我的建议是:如果终端设备可控,优先用 CMAF;如果面向海量未知设备,保留 TS 回退方案。
3.3 自适应码率(ABR)逻辑
自适应码率是 HLS 最吸引人的特性。播放器会根据网络状况,在多个码率版本之间无缝切换。
3.3.1 切换策略
常见的 ABR 算法有几种:
| 算法 | 原理 | 优缺点 |
|---|---|---|
| 基于吞吐量 | 统计最近 N 个分片的下载速度 | 简单直接,但对突发网络抖动反应慢 |
| 基于缓冲区 | 根据播放器缓冲区水位决策 | 缓冲区快空了就降码率,满了就升码率 |
| 混合策略 | 结合吞吐量和缓冲区 | 最稳定,但实现复杂 |
我个人比较推荐混合策略。举个例子:当缓冲区低于 5 秒时,立即降一档码率;当缓冲区高于 15 秒且吞吐量足够时,升一档码率。这样既避免了频繁切换,又能快速响应网络恶化。
3.3.2 码率阶梯设计
码率阶梯不是随便设几个值就行的。我见过很多团队把码率设成 500k、1000k、2000k、4000k——结果播放器在 1000k 和 2000k 之间反复横跳。
合理的码率阶梯应该满足:相邻两档的码率差不超过 1.5 倍。比如:
档位 1: 500 kbps (360p)
档位 2: 800 kbps (480p)
档位 3: 1300 kbps (720p)
档位 4: 2000 kbps (1080p)
这样播放器在切换时,视觉差异不会太明显。另外,最低码率要保证画面可看——我见过有人设 200kbps,结果画面全是马赛克,用户直接关掉了。
3.4 HLS 协议架构总览
下面这张图把 HLS 的整体流程串起来了。从编码器到 CDN,再到播放器,每一步都有讲究。
从这张图能看出来,HLS 的每个环节都环环相扣。编码器输出要稳定,分片器要保证对齐,m3u8 要实时更新,CDN 要低延迟分发,播放器要智能决策——任何一个环节出问题,用户体验都会打折扣。
3.5 实战中的几个关键点
最后分享几个我在项目中积累的经验:
- 分片时长不要设太短:2 秒分片虽然延迟低,但 CDN 请求量暴增,容易把源站打垮。我一般用 4-6 秒。
- 直播场景下,m3u8 要保留至少 3 个分片:这样播放器在启动时能快速填满缓冲区,减少卡顿。
- 加密时注意密钥轮换:
#EXT-X-KEY的KEYFORMAT要写对,否则某些播放器会解密失败。 - 测试 ABR 切换时,用 Chrome 的 DevTools 模拟弱网:我习惯把网络限速到 200kbps,然后看播放器能不能平滑降码率。
嗯,HLS 协议的内容就讲到这里。记住:m3u8 是地图,分片是砖块,ABR 是智能导航。把这三样东西搞明白,HLS 对你来说就没有秘密了。