HLS 流媒体全链路拆解:m3u8 切片、AES 加密、nginx 分发与播放排错
HLS(HTTP Live Streaming)是今天点播和直播最普遍的传输协议之一。本文从"一段 MP4 怎么变成 m3u8 + ts 分片"讲起,一路讲到 AES-128 加密、nginx 分发和播放端常见排错,帮你把流媒体全链路吃透。
一、HLS 是什么
HLS 把视频切成一堆几秒长的小分片(通常是 .ts 或 .fmp4),再用一个 .m3u8 索引文件按顺序列出来。播放器先拉 m3u8,再按列表逐个下载分片播放。好处是:
- 天然适配 HTTP/CDN,不怕防火墙;
- 支持自适应码率(多份分片,网络差自动降清);
- 边下边播,秒开体验好。
二、m3u8 与分片结构
一个最基础的 VOD m3u8 长这样:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
seg0.ts
#EXTINF:10.0,
seg1.ts
#EXT-X-ENDLIST
#EXT-X-ENDLIST 表示点播结束;直播则没有它,播放器会不断轮询刷新。
2.1 VOD 与 Live 的区别
| 维度 | 点播 VOD | 直播 Live |
|---|---|---|
| 是否有 ENDLIST | 有 | 无 |
| 分片是否变化 | 固定 | 滚动追加 |
| 播放器行为 | 顺序播完 | 持续追新 |
| 时移 | 不支持 | 可保留滑动窗口 |
三、用 FFmpeg 切片生成 HLS
3.1 基础切片
ffmpeg -i in.mp4 -c:v h264 -c:a aac -hls_time 10 ^
-hls_list_size 0 -hls_segment_filename seg%d.ts index.m3u8
-hls_time 10:每片约 10 秒;-hls_list_size 0:保留全部分片(点播);直播设为窗口大小;- 输出
index.m3u8即为索引。
3.2 多码率自适应(Master Playlist)
生成 1080p / 720p / 480p 三档,再用 master playlist 串起来:
ffmpeg -i in.mp4 \
-map 0:v:0 -map 0:a:0 -b:v:0 3000k -s:v:0 1920x1080 -profile:v:0 high \
-map 0:v:0 -map 0:a:0 -b:v:1 1500k -s:v:1 1280x720 -profile:v:1 main \
-map 0:v:0 -map 0:a:0 -b:v:2 800k -s:v:2 854x480 -profile:v:2 baseline \
-f hls -var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" \
-master_pl_name master.m3u8 -hls_segment_filename v%v/seg%d.ts v%v/index.m3u8
播放器读 master.m3u8,根据用户带宽切换 v0/v1/v2。
四、AES-128 加密与密钥分发
HLS 原生支持 #EXT-X-KEY 对分片加 AES-128:
ffmpeg -i in.mp4 -hls_time 10 -hls_key_info_file keyinfo.list index.m3u8
keyinfo.list 内容为:
http://example.com/key/key.bin
/path/local/key.bin
密钥通过 HTTP 下发。要"防盗链",关键是密钥接口做鉴权:
- URL 带临时 token,校验通过才返回 key;
- 回源校验 Referer / IP 白名单;
- 密钥定期轮换,老分片失效。
提醒:AES-128 保护的是"传输层不让轻易下载整段",不是内容版权的最终方案;合规使用、尊重版权始终是前提。
4.1 防盗链:URL 鉴权 + 回源校验
在 nginx 上对 /key/ 与 /v*/ 做 auth_request 子请求校验:
location /key/ {
auth_request /auth_key;
...
}
location = /auth_key {
internal;
proxy_pass http://auth-backend/verify?$args;
}
五、用 nginx 分发 HLS
5.1 MIME 与 CORS
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
location /hls/ {
add_header Access-Control-Allow-Origin *;
add_header Cache-Control "max-age=3600";
}
m3u8 的 MIME 必须是 application/vnd.apple.mpegurl,否则部分播放器拒绝解析。
5.2 断点续传与 Range 支持
分片要支持 Range 请求(nginx 默认 Accept-Ranges: bytes),否则拖动进度条会重新下载整片:
location /hls/ {
mp4;
flv;
}
六、播放端排错
| 现象 | 可能原因 | 排查 |
|---|---|---|
| 跨域报错 | 缺 CORS 头 | nginx 加 Access-Control-Allow-Origin |
| 403 | 密钥/分片鉴权失败 | 检查 token、Referer 白名单 |
| 卡顿 | 分片过大或码率过高 | 减小 -hls_time、降码率 |
| 花屏 | 关键帧间隔大 | 加 -g 30 缩短 GOP |
| 直播延迟高 | 分片时长 + 列表窗口 | 用 LL-HLS 或减小分片 |
缩短 GOP 让切片更密,能改善seek 体验:
ffmpeg -i in.mp4 -g 30 -hls_time 4 index.m3u8
七、下载与本地备份的合规边界
HLS 把内容切成公开 URL,技术上可以逐片下载再 cat 合并。但请注意:
- 仅对自己拥有版权或已授权的内容做备份/离线;
- 不要绕过付费/鉴权批量抓取他人付费内容;
- 尊重平台的
robots与服务条款。
VidDown 的定位是"帮用户更方便地处理自己有权使用的媒体",合规是底线。
八、小结
HLS 全链路 = 切片(FFmpeg)→ 索引(m3u8)→ 加密(AES-128 + 鉴权 key)→ 分发(nginx + CORS/Range)→ 播放(自适应切换)。每一环都有明确的排错抓手,按本文表格逐项核对,绝大多数"播不了/卡顿/403"都能快速定位。