VOD 流媒体技术全解 · 第 4 章 / 共 12 章
本章你会理解:文件后缀和编码格式的区别、MP4/MKV/TS/WebM 是什么、为什么流媒体都用 fMP4、CMAF 又是干嘛的。
预计阅读时间:18 分钟
先看一个每个新人都会犯的错:
❌ "视频编码是 MP4。"
✅ "视频容器是 MP4,编码是 H.264(或 H.265、AV1 等)。"
MP4 是一个容器(container),是一个盒子。盒子里可以装任何支持的编码流。
┌──────────────────────────────────────┐
│ MP4 容器(一个 .mp4 文件) │
│ ┌────────────────────────────────┐ │
│ │ 视频流:H.264 / H.265 / AV1 … │ │
│ └────────────────────────────────┘ │
│ ┌────────────────────────────────┐ │
│ │ 音频流:AAC / Opus / AC-3 … │ │
│ └────────────────────────────────┘ │
│ ┌────────────────────────────────┐ │
│ │ 字幕流:TTML / WebVTT … │ │
│ └────────────────────────────────┘ │
│ ┌────────────────────────────────┐ │
│ │ 元数据:标题、时长、时间戳索引 │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
类比:MP4 就像"快递盒";H.264 是盒子里装的"商品"。同样一个快递盒可以装手机(H.264)、装衣服(H.265)、装水果(AV1)。
直接存编码后的二进制流不行吗?
不行。编码流里没有这些信息:
• 视频和音频怎么同步播放?
• 字幕怎么对齐?
• 用户拖进度条到 1 分钟,从哪个字节开始读?
• 视频有几条音轨?
• 视频是什么编码?参数?
容器格式负责组织这一切,让播放器能按时间轴读取。
| 容器 | 后缀 | 发明者 | 主要装什么 | 适合 |
|---|---|---|---|---|
| MP4 / fMP4 | .mp4 .m4s .m4a | MPEG | H.264/H.265/AV1 + AAC | 最通用、流媒体默认 |
| MOV | .mov | Apple | 几乎任意 | macOS 制作、素材中转 |
| MKV | .mkv | CoreCodec | 几乎任意 | 高清下载、开源社区 |
| WebM | .webm | VP9/AV1 + Opus | Web 端(非 iOS) | |
| MPEG-TS | .ts | MPEG | H.264/HEVC + AAC/AC3 | 广电、老版 HLS |
| FLV | .flv | Adobe | H.264 + AAC | 已退役,RTMP 残留 |
| AVI | .avi | Microsoft | - | 已过时,不要用 |
| 3GP | .3gp | 3GPP | H.263 + AMR | 2G 时代手机,已过时 |
VOD 项目 99% 的情况只需要两种容器:
• MP4 / fMP4(流媒体发布)
• MOV(素材中转、母版)
MP4 的正式标准叫 ISO Base Media File Format(ISO BMFF),标准号 ISO/IEC 14496-12。
内部由一个个叫 Box(盒子,也叫 atom)的单元组成,Box 可以嵌套:
File start
│
├── [ftyp] File Type Box ← 告诉播放器"这是 MP4"
│
├── [moov] Movie Box ← 元数据"目录表":
│ ├── [mvhd] Movie Header (总时长、时间尺度)
│ ├── [trak] Track Box (每条音频/视频轨一个)
│ │ ├── [tkhd] Track Header
│ │ └── [mdia] Media
│ │ ├── [mdhd] Media Header
│ │ ├── [hdlr] Handler (vide/soun/subt)
│ │ └── [minf]
│ │ └── [stbl] Sample Table ← 每一帧在文件哪个字节
│ └── [trak] ... (更多轨)
│
└── [mdat] Media Data Box ← 真正的音视频二进制数据
└── (大块二进制)
关键概念:
• moov:元数据 / 目录。告诉播放器"第 1 帧在字节 12345,第 2 帧在字节 13800..."。没有它不知道怎么播。
• mdat:真正的数据。纯粹的 H.264 + AAC 二进制。
传统 MP4 生成时 moov 会放在文件末尾(因为编码完成后才知道完整的时间戳信息)。
常规 MP4:
┌─────────┬─────────────────────────────────┬──────┐
│ ftyp │ mdat (99.9%) │ moov │
└─────────┴─────────────────────────────────┴──────┘
8字节 几百 MB / GB 几十 KB
问题来了:Web 播放器要播必须先读 moov,但 moov 在文件末尾 → 要把整个文件下载完才能开始播!
这就是你看到"网页视频要等进度条走到 100% 才能播"的原因。
重新打包,把 moov 挪到 ftyp 之后:
faststart MP4:
┌─────────┬──────┬──────────────────────────────┐
│ ftyp │ moov │ mdat │
└─────────┴──────┴──────────────────────────────┘
↑
读完这里 (< 1 MB) 就能开始播
用 ffmpeg 加 -movflags +faststart 就行:
ffmpeg -i input.mov -c copy -movflags +faststart output.mp4
VOD 发布前必做 faststart。没做的话用户点播后会很久没反应。
传统 MP4 有个大缺点:moov 描述的是整个文件的时间轴。如果内容很长(几小时的电影),moov 会非常大(MB 级),启动慢、修改成本高。
fMP4(Fragmented MP4) 的做法:
把文件拆成很多小片段(fragment),每个片段都自带自己的小 moov(叫 moof)。播放器可以独立解析每个片段。
fMP4 结构:
┌──────┬──────┐ ┌──────┬──────┐ ┌──────┬──────┐ ┌──────┬──────┐
│ moov │ - │ │ moof │ mdat │ │ moof │ mdat │ │ moof │ mdat │
│ init │ │ │ 片 │片 │ │ 片 │片 │ │ 片 │片 │
└──────┴──────┘ └──────┴──────┘ └──────┴──────┘ └──────┴──────┘
独立的
初始化段 每个片段都是独立可解码的
fMP4 有两个核心好处:
1. 可独立切片:每个 moof+mdat 对可以单独存为一个文件(.m4s)。这正是流媒体分发需要的。
2. 启动只需很小的 init segment(几十 KB),而不是整个 moov。
现代 HLS、DASH、CMAF 全部基于 fMP4。TS(老派 HLS)正在被淘汰。
MPEG-TS(.ts 文件)是为广播电视设计的格式:
• 所有数据切成固定 188 字节的小包
• 每个包自带同步字(0x47 sync byte)和时间戳
• 设计目标:卫星信号偶尔丢包也能恢复
TS 结构:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬ ...
│188B │188B │188B │188B │188B │188B │188B │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴
pkt1 pkt2 pkt3 ...
HLS 2009 年诞生时 iOS 强制使用 .ts。但 TS 有几个问题:
• 开销大:每 188 字节就要一个 4 字节头(开销约 2%)
• PAT/PMT 表 每隔一段时间就要重复
• 和 MP4 生态割裂:要多存一套 TS 版本
iOS 10(2016)起支持 fMP4,新项目应统一用 fMP4,可以被 HLS/DASH 共用。
问题:历史上 Apple 主推 HLS(TS),Google/工业界主推 DASH(fMP4),同一个视频要存两份。
CMAF(Common Media Application Format) 2018 年标准化,核心约定:
HLS 和 DASH 共用一份 fMP4 文件。Manifest 不同,segment 完全相同。
一份 CMAF fMP4 源:
┌──────┐ ┌───────┐ ┌───────┐ ┌───────┐
│ init │ │ seg1 │ │ seg2 │ │ seg3 │ ← 物理存储只有一份
└──────┘ └───────┘ └───────┘ └───────┘
↑ ↑
┌────────┴───────┐ ┌──┴──────────┐
│ HLS master.m3u8 │ │ DASH .mpd │
│ 指向同样的片段 │ │ 指向同样的片段│
└────────────────┘ └─────────────┘
收益:
• 存储减半
• CDN 缓存命中率翻倍
• 转码只跑一次
代价:
• 所有平台要兼容 CMAF(现代平台都支持)
• DRM 需要用 CBCS 模式(兼容 FairPlay)
2020 年后新项目请直接用 CMAF。不要再做"HLS TS + DASH fMP4 双发"。
VOD 切片长度是一个工程权衡:
| 切片长度 | 优点 | 缺点 | 适合 |
|---|---|---|---|
| 1-2 秒 | 起播快、ABR 反应快、低延迟直播 | 文件多、HTTP 请求多 | 短剧、短视频、低延迟直播 |
| 2-4 秒 | 平衡 | - | HLS/DASH 默认推荐(4 秒) |
| 6-10 秒 | HTTP 请求少、CDN 友好 | 起播慢、拖动定位粗 | 长电影、传统广电 |
短剧/短视频选 2 秒(因为用户频繁滑动切剧集);长视频 VOD 选 4-6 秒。
切片长度必须是 GOP 的整数倍,见第 1 章关键帧部分。
字幕有三种组织方式:
字幕是独立文件,不在视频容器里:
video.mp4
video.en.vtt ← 英文字幕
video.zh.vtt ← 中文字幕
HLS/DASH manifest 引用这些字幕文件。
优点:容易多语言切换、改字幕不用重做视频。
缺点:多一些 HTTP 请求。
字幕作为一条 track 放在 MP4 里:
video.mp4
├── video track
├── audio track
└── subtitle track (TTML / WebVTT)
把字幕直接绘制到视频画面上。像素级合并,不可关闭。
适合哪种?
• VOD 多语言:用 Sidecar(外挂 WebVTT/IMSC1)
• 短视频:有时烧录(字幕是创意的一部分)
• 广电:内嵌 EIA-608/708
| 格式 | 特点 | 用在哪 |
|---|---|---|
| SRT | 最简单,文本+时间戳 | 通用 |
| WebVTT | SRT 增强(样式、定位) | HTML5 / HLS 标准 |
| TTML / IMSC1 | XML,支持复杂排版 | DASH、广电 |
| ASS / SSA | 强大样式,动画特效 | 番剧爱好者 |
| EIA-608 / 708 | 广电字幕编码 | 传统电视、直播 CC |
一个 WebVTT 例子:
WEBVTT
00:00:01.000 --> 00:00:03.500
Hello, and welcome to our show.
00:00:03.500 --> 00:00:06.000
Today we talk about video streaming.
动手试一试
ffprobe -v error -show_streams input.mp4
会列出所有 video / audio / subtitle 流。
ffmpeg -i input.mov \
-c copy \
-movflags +faststart+frag_keyframe+empty_moov+default_base_moof \
output_fragmented.mp4
解释:
• -c copy:不重新编码,直接复制
• +faststart:moov 前置
• +frag_keyframe:在每个关键帧切片
• +empty_moov +default_base_moof:让 moov 为空(只放 init),数据进 moof/mdat
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 22 -g 60 -keyint_min 60 -sc_threshold 0 \
-c:a aac -b:a 128k \
-f hls \
-hls_time 4 \
-hls_segment_type fmp4 \
-hls_playlist_type vod \
-hls_list_size 0 \
-hls_segment_filename "seg_%04d.m4s" \
output.m3u8
输出会是:
output.m3u8 ← HLS manifest
init.mp4 ← CMAF init segment
seg_0000.m4s
seg_0001.m4s
seg_0002.m4s
...
这就是一个最小可用的 HLS 流媒体包。把它放到一个 Web 服务器(甚至 python3 -m http.server),用 Safari 或 hls.js 就能播。
1. 容器 ≠ 编码。MP4 是容器,H.264 是编码。
2. MP4 内部是由 Box 组成的结构;moov 是目录,mdat 是数据。
3. VOD 发布必做 -movflags +faststart,让 moov 前置支持边下边播。
4. fMP4 把文件切成独立片段,是现代流媒体的基础。
5. CMAF 让 HLS 和 DASH 共用一份 fMP4 文件,存储减半。
6. TS 容器是老派 HLS 用的,新项目请用 fMP4/CMAF。
7. 切片长度:短剧短视频 2 秒、长 VOD 4-6 秒。
8. 字幕优先用 Sidecar WebVTT。
© 2026 Zmead · VOD 流媒体技术全解