Appearance
HLS / FLV / MP4:三种协议的底层差异
这三个名字经常被并列提起,但它们不是同一层的东西。搞清楚这点, 很多"为什么 iOS 播不了"的问题就自解了。
先分清:容器 vs 传输方式
| 它是什么 | 一次请求拿到什么 | |
|---|---|---|
| MP4 | 一个文件 | 整个视频(或用 Range 请求拿一段) |
| HLS | 一个播放列表 + 一堆小分片 | 先拿列表,再逐个拿分片 |
| FLV | 一条持续不断的流 | 一个永不结束的 HTTP 响应 |
这个差异决定了它们各自适合什么。
MP4:最简单,也最不适合流媒体
浏览器原生支持,一个 <video src="a.mp4"> 就能播。
问题在于它的结构。MP4 有个叫 moov 的元数据块,记录每一帧的位置和时间戳。 浏览器必须先拿到 moov 才能开始播。
而 moov 默认被写在文件末尾:
[视频数据 ................................ 500MB][moov]
↑
浏览器要它才能起播结果是:一个 500MB 的视频,用户得等整个文件下完才看到第一帧。
解法是编码时加 -movflags +faststart,把 moov 挪到开头。这是后端的事, SDK 修不了(坑:大 MP4 moov 在末尾)。
MP4 还有个致命短板:没有 ABR
MP4 是单一码率。用户网络变差时,播放器没有低码率版本可切——只能卡。
HLS 天生支持多码率自适应(ABR),这才是点播推荐 HLS 而不是 MP4 的决定性理由。 详见 怎么选视频源 § 4.2。
HLS:苹果发明的,分片 + 播放列表
播一个 HLS,浏览器实际发出的是一串请求:
master.m3u8 ← 主列表:有哪些清晰度
└─ 720p.m3u8 ← 媒体列表:这个清晰度有哪些分片
├─ seg-001.ts ← 分片,通常 2-10 秒一个
├─ seg-002.ts
└─ ...优点:
- ABR:网络变差自动切低码率,不卡
- iOS / Safari 原生支持——这是它无可替代的地方
- 点播直播通用
缺点:延迟高。
延迟的来源是分片本身:服务器必须攒够一个完整分片才能发出去。 分片 6 秒,播放器又通常缓冲 3 个分片:
6 秒 × 3 = 18 秒起步延迟普通 HLS 直播延迟 10-30 秒是常态。LL-HLS 就是来解决这个的。
FLV:老技术,但直播延迟最低
HTTP-FLV 的做法是:一个永不结束的 HTTP 响应,数据来一点推一点。
GET /live/room.flv
→ HTTP 200, 连接保持打开
→ [数据][数据][数据][数据]... 持续推送,不分片没有"攒够一个分片"这一步,所以延迟能做到 1-3 秒。
代价是它需要 MSE(Media Source Extensions)——浏览器不认 FLV 容器, 必须用 JS(flv.js)把 FLV 解开、重新封装成浏览器认识的格式,再喂给 <video>。
这就是 iOS 播不了 FLV 的根本原因
iOS 全系没有标准 MSE(所有浏览器都是 WebKit 内核),flv.js 起不来; 而 Safari 的 <video> 又不认 FLV 容器,没有原生兜底。
两条路都堵死 = 物理上播不了。详见 iOS 为什么特殊。
放在一起看
| MP4 | HLS | FLV | |
|---|---|---|---|
| 点播 | ✅ 可用 | ✅ 推荐 | ❌ 不适合 |
| 直播 | ❌ 不能 | ✅ 可用(延迟高) | ✅ 延迟最低 |
| iOS | ✅ | ✅ | ❌ 物理不可能 |
| 需要 MSE | ❌ | 需要(含 Safari —— ADR-056 后按能力选内核;iOS < 17.1 才回落原生) | ✅ 必须 |
| 多码率 ABR | ❌ | ✅ | ❌ |
| 直播延迟 | — | 10-30s(LL-HLS 2-5s) | 1-3s |
所以实践上的结论
直播传两种:[FLV, HLS]。 非 iOS 走 FLV 拿低延迟,iOS 自动落到 HLS。SDK 的 SourceRouter 替你选, 你不用写 UA 判断。
点播传一种:HLS。 它有两条播放路径(Safari 原生 / 其他走 hls.js),全平台覆盖,还带 ABR。
MP4 支持但不推荐,只在"就是一个短视频文件、不在乎 ABR"时用。
具体怎么传、传错会怎样,见 怎么选视频源。