Skip to content

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 为什么特殊


放在一起看

MP4HLSFLV
点播✅ 可用推荐❌ 不适合
直播❌ 不能✅ 可用(延迟高)延迟最低
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"时用。

具体怎么传、传错会怎样,见 怎么选视频源