MP4 / MOV box 结构剖析
MP4 是音视频容器之王。理解 box 结构,就能理解为什么 MP4 既能流式播放,又能精确 seek、跳转章节、承载多语言字幕和元数据。
本文速览
| 章节 |
阅读重点 |
一句话结论 |
| 0. MP4 vs MOV |
区分 QuickTime MOV 和 ISO MP4 的关系。 |
MP4 基本可以理解为 MOV 的标准化演进版本。 |
| 1. box 基础结构 |
看每个 box 如何用 size/type/payload 组织数据。 |
MP4 本质是一棵 box 嵌套树。 |
| 2. MP4 顶级结构 |
看 ftyp、moov、mdat 的分工。 |
moov 是目录,mdat 是内容。 |
| 3. ftyp 文件类型 |
看播放器如何判断兼容品牌。 |
ftyp 决定"这是不是我能处理的 MP4"。 |
| 4. moov 元数据中心 |
看影片级、轨道级元数据如何组织。 |
没有 moov,只有裸媒体数据,播放器很难开播。 |
| 5. stbl sample 表 |
理解时间、关键帧、chunk 偏移、sample 大小如何查表。 |
MP4 的 seek 能力主要靠 stbl。 |
| 6. mdat 实际数据 |
看真实 H.264 NAL / AAC frame 放在哪里。 |
mdat 存数据,但解释数据要靠 moov/stbl。 |
| 7. moov 在前还是在后 |
理解 faststart。 |
HTTP 流式播放必须让 moov 前置。 |
| 8. fMP4 |
理解 DASH / HLS 为什么用 moof + mdat 分片。 |
fMP4 把一次性索引拆成每段独立索引。 |
| 9. udta 元数据 |
看作者、标题、章节等信息放在哪里。 |
udta 负责非解码必需的描述信息。 |
| 10. 解析工具 |
学会用工具看 box 树。 |
mp4dump / ffprobe 是排查 MP4 的入口。 |
| 11. 常见坑 |
识别 moov 太大、时基混用、stsd 多 entry 等坑。 |
大多数 MP4 播放问题都能在 box 表里定位。 |
| 12. 为什么 MP4 是容器之王 |
从流式、seek、多轨、兼容性解释 MP4 成功原因。 |
MP4 复杂,但功能完整、生态最强。 |
| 13. 总结 |
汇总实战要点。 |
搞懂 stsd/stts/stsc/stsz/stco/stss,就懂 MP4 一半。 |
0. MP4 vs MOV
| 格式 |
来源 / 标准 |
关系 |
常见差异 |
| MOV |
Apple QuickTime 格式。 |
更早出现,是 QuickTime 生态的容器。 |
可能包含一些 QuickTime 私有 box 或历史字段。 |
| MP4 |
ISO/IEC 14496-14,基于 ISO Base Media File Format。 |
可以理解为 MOV 思路的标准化版本。 |
更强调跨平台、流媒体和标准兼容。 |
| 结论 |
说明 |
| box 结构基本一致 |
二者都使用 size + type + payload 的 box 嵌套结构。 |
| 兼容性很高 |
大量播放器和工具对 MOV / MP4 的解析逻辑可以复用。 |
| 区别多在细节 |
MOV 可能多一些 Apple / QuickTime 私有 box,MP4 更标准化。 |
1. box 基础结构
常见 box 字段布局先看图。图里列了常见 box 的字段、字节数和作用:
| 字段 |
字节数 |
含义 |
注意事项 |
size |
4 |
当前 box 总大小,包含 size 和 type 本身。 |
普通 box 用 32-bit size。 |
type |
4 |
4 字符 ASCII,例如 ftyp、moov、mdat。 |
是解析分支的核心依据。 |
large_size |
8 |
当 size == 1 时出现,用 64-bit 表示 box 大小。 |
大文件或超大 mdat 常见。 |
payload |
N |
box 负载。 |
如果是容器型 box,payload 又是一串子 box。 |
1.2 size 特殊值
size 值 |
后续结构 |
含义 |
常见场景 |
| 普通大于 1 |
size + type + payload |
box 大小就是 32-bit size。 |
大多数 box。 |
1 |
后面追加 8 字节 large_size。 |
box 使用 64-bit size。 |
超大文件、超大媒体数据。 |
0 |
没有显式结束位置。 |
box 延伸到文件末尾。 |
mdat 常见。 |
1.3 box 嵌套
| box 类型 |
payload 内容 |
例子 |
| 叶子 box |
具体字段和数据。 |
ftyp、mvhd、stts、stsz。 |
| 容器 box |
一系列子 box。 |
moov、trak、mdia、minf、stbl。 |
| 媒体数据 box |
连续媒体 payload。 |
mdat。 |
2. MP4 顶级结构
| 顶级 box |
作用 |
关键点 |
ftyp |
文件类型和兼容品牌。 |
播放器用它判断是否支持这个文件。 |
moov |
元数据中心。 |
保存时长、轨道、codec 配置、sample 索引表。 |
mdat |
实际媒体数据。 |
H.264 NAL、AAC frame 等通常在这里。 |
| 其他 box |
扩展信息。 |
例如 free、skip、uuid 等。 |
核心 :moov 是"目录",mdat 是"内容"。没有 moov,播放器就不知道 mdat 里哪段数据是什么时间、什么大小、是不是关键帧。
3. ftyp ------ 文件类型
ftyp 用于声明文件品牌和兼容能力。
| 字段 |
字节数 |
示例 |
作用 |
size |
4 |
00 00 00 18 |
box 总大小,示例为 24 字节。 |
type |
4 |
66 74 79 70 = ftyp |
标识这是文件类型 box。 |
major_brand |
4 |
6D 70 34 32 = mp42 |
主品牌。 |
minor_version |
4 |
00 00 00 00 |
次版本号。 |
compatible_brands |
4 × N |
mp42、isom 等 |
兼容品牌列表。 |
常见 brand 可以这样理解:
| brand |
含义 |
常见用途 |
mp42 |
MP4 v2。 |
常见 MP4 文件。 |
isom |
ISO Base Media。 |
ISO BMFF 兼容标记。 |
M4A |
Apple 音频。 |
.m4a 音频文件。 |
qt |
QuickTime。 |
MOV / QuickTime 生态。 |
3gp4 |
3GPP。 |
移动通信视频格式。 |
dash |
DASH。 |
DASH / fMP4 场景。 |
4. moov ------ 元数据中心
moov 是 MP4 的导航目录,里面包含影片级信息和每条轨道的信息。
| 子 box |
层级 |
作用 |
mvhd |
moov/mvhd |
影片级头信息,例如全局时基、总时长。 |
trak |
moov/trak |
一条媒体轨道,例如视频、音频、字幕。 |
udta |
moov/udta |
用户数据和元数据,例如标题、作者、章节。 |
4.1 mvhd
| 字段 |
字节数 |
作用 |
version + flags |
4 |
FullBox 公共头。 |
creation_time |
4 / 8 |
创建时间,v0 为 32-bit,v1 为 64-bit。 |
modification_time |
4 / 8 |
修改时间,v0 为 32-bit,v1 为 64-bit。 |
timescale |
4 |
整个 MP4 的全局时基,常见 1000 表示毫秒。 |
duration |
4 / 8 |
按 timescale 计数的总时长。 |
rate |
4 |
默认播放速率,0x00010000 表示 1.0。 |
volume |
2 |
默认音量。 |
matrix 等 |
variable |
显示矩阵、预定义字段、next track id 等。 |
| 计算 |
公式 |
示例 |
| 总时长秒数 |
duration / timescale |
duration=60000、timescale=1000 表示 60 秒。 |
4.2 trak ------ 一条流
| 子 box |
作用 |
说明 |
tkhd |
track 头。 |
轨道 ID、宽高、持续时间等。 |
mdia |
media 信息。 |
包含媒体时基、handler、媒体信息。 |
mdhd |
media header。 |
轨道自己的 timescale 和 duration。 |
hdlr |
handler。 |
标识视频 / 音频 / 字幕轨。 |
minf |
media information。 |
媒体类型相关信息。 |
stbl |
sample table。 |
MP4 seek 和解码定位的核心表。 |
5. stbl ------ sample 表(核心中的核心)
stbl 里的一堆表共同回答 5 个问题:
| 问题 |
由谁回答 |
作用 |
| 这个 sample 用什么 codec 解? |
stsd |
找到 avc1/hev1/mp4a 等 codec entry 和配置。 |
| 这个 sample 什么时候显示? |
stts / ctts |
计算 PTS / DTS 和显示顺序。 |
| 哪些 sample 是关键帧? |
stss |
seek 时找最近同步点。 |
| sample 属于哪个 chunk? |
stsc |
建立 sample 到 chunk 的映射。 |
| chunk 在文件哪个字节偏移? |
stco / co64 |
找到 mdat 里的实际位置。 |
| sample 自己多大? |
stsz |
在 chunk 内继续定位到具体 sample。 |
5.1 stsd(codec 信息)
| 字段 / entry |
字节数 |
作用 |
常见内容 |
version + flags |
4 |
FullBox 公共头。 |
通常为 0。 |
entry_count |
4 |
codec entry 数量。 |
多数文件为 1。 |
avc1 / avc3 |
variable |
H.264 视频 sample entry。 |
宽高、像素信息、avcC。 |
hev1 / hvc1 |
variable |
H.265 视频 sample entry。 |
hvcC。 |
mp4a |
variable |
AAC 音频 sample entry。 |
sample rate、channel、esds。 |
tx3g |
variable |
mov_text 字幕。 |
字幕样式和文本相关配置。 |
avcC / hvcC / esds |
variable |
codec config。 |
SPS/PPS、VPS/SPS/PPS、AAC config。 |
关键点 :H.264 解码器需要的 SPS/PPS 通常在 avcC 里;没有这些配置,decoder 不知道 profile、level、分辨率、NAL 长度前缀等信息。
5.2 stts(时长表)
| 字段 |
字节数 |
含义 |
示例 |
version + flags |
4 |
FullBox 公共头。 |
通常为 0。 |
entry_count |
4 |
后面有多少组 (sample_count, sample_delta)。 |
1。 |
sample_count |
4 × N |
连续多少个 sample 使用同一个时长。 |
1800。 |
sample_delta |
4 × N |
每个 sample 的时长,单位是 track timescale。 |
33。 |
| 例子 |
含义 |
[1800, 33],timescale=1000 |
1800 个 sample,每个 33ms,约等于 30fps。 |
5.3 stss(关键帧)
| 字段 |
字节数 |
含义 |
注意事项 |
version + flags |
4 |
FullBox 公共头。 |
通常为 0。 |
entry_count |
4 |
关键帧数量。 |
例如 60。 |
sample_number |
4 × N |
关键帧 sample index,1-based。 |
例如 1, 31, 61, 91...。 |
| 情况 |
解释 |
存在 stss |
seek 时找不超过目标时间的最近关键帧。 |
不存在 stss |
通常表示所有 sample 都是同步 sample,类似 MJPEG 这类帧内编码场景。 |
5.4 stsc(sample → chunk 映射)
| 字段 |
字节数 |
含义 |
示例 |
version + flags |
4 |
FullBox 公共头。 |
通常为 0。 |
entry_count |
4 |
映射规则条数。 |
1。 |
first_chunk |
4 × N |
从哪个 chunk 开始使用这条规则。 |
1。 |
samples_per_chunk |
4 × N |
每个 chunk 有多少 sample。 |
1。 |
sample_description_idx |
4 × N |
使用 stsd 的第几个 entry。 |
1。 |
| 场景 |
映射特点 |
| 简单场景 |
一个 chunk 一个 sample,1:1 映射。 |
| 复杂场景 |
一个 chunk 多个 sample,或者不同区间使用不同 stsd entry。 |
5.5 stco / co64(chunk 偏移)
| box |
偏移字段字节数 |
作用 |
适用场景 |
stco |
4 |
记录每个 chunk 在文件中的 32-bit 偏移。 |
小于 4GB 偏移范围的文件。 |
co64 |
8 |
记录每个 chunk 在文件中的 64-bit 偏移。 |
大文件,尤其超过 4GB 时。 |
| 字段 |
字节数 |
含义 |
version + flags |
4 |
FullBox 公共头。 |
entry_count |
4 |
chunk 偏移数量。 |
chunk_offset[] |
4 × N 或 8 × N |
每个 chunk 的文件偏移。 |
这就是 MP4 能精确 seek 的关键 :先查 stss 找关键帧,再查 stsc/stco/stsz 得到字节位置,最后跳到 mdat 对应偏移读取。
5.6 stsz(sample 大小)
| 字段 |
字节数 |
含义 |
说明 |
version + flags |
4 |
FullBox 公共头。 |
通常为 0。 |
sample_size |
4 |
如果非 0,表示所有 sample 等大。 |
PCM 音频可能使用。 |
sample_count |
4 |
sample 总数。 |
例如 1800。 |
entry_size[] |
4 × N |
当 sample_size == 0 时,逐个记录 sample 大小。 |
视频最常见,因为 I/P/B 帧大小不同。 |
| 情况 |
解释 |
sample_size == 0 |
每个 sample 大小不同,必须读取 entry_size[]。 |
sample_size != 0 |
所有 sample 一样大,可以省掉逐 sample 大小表。 |
6. mdat ------ 实际数据
| 项 |
说明 |
| payload 内容 |
一堆 H.264 NAL、H.265 NAL、AAC frame、字幕 sample 等媒体数据。 |
| 排列方式 |
通常按 sample / chunk 顺序连续拼接。 |
| 如何定位 |
通过 stco/co64 找 chunk 起点,通过 stsz 和 stsc 定位 sample。 |
| 自身限制 |
mdat 通常只存数据,不告诉你时间、关键帧、codec 配置。 |
关键点 :没有 stbl,播放器就不知道 mdat 里哪段是哪个 sample,也不知道该从哪里 seek。
7. moov 在前还是在后
| 方式 |
文件顺序 |
播放体验 |
适用场景 |
| moov 在前 |
ftyp → moov → mdat |
拉到 moov 后就能开始播放。 |
HTTP 点播、渐进式下载、faststart。 |
| moov 在后 |
ftyp → mdat → moov |
必须先拿到文件尾部 moov 才能知道索引。 |
本地录制完成后再播放,或播放器支持 range 回源。 |
| faststart |
把 moov 移到 mdat 前。 |
改善起播速度。 |
Web 视频分发必备。 |
HTTP 流式 MP4 必须 faststart ,否则用户可能要等到文件尾部的 moov 下载完成后才能开始播。
8. fragmented MP4(fMP4)
DASH / HLS 常使用 fMP4。它把一个大 MP4 拆成初始化段和媒体分片。
| 段 / box |
作用 |
内容 |
ftyp |
文件类型。 |
声明 fMP4 / DASH 等兼容品牌。 |
moov |
init segment。 |
主要包含 codec 信息和轨道信息,不包含完整 sample 表。 |
moof |
movie fragment。 |
当前分片的 sample 表和索引。 |
mdat |
当前分片媒体数据。 |
当前分片的 H.264 / AAC 等 sample。 |
后续 moof + mdat |
后续分片。 |
直播或点播按段持续追加。 |
| 优势 |
说明 |
| 可分片传输 |
每段都有自己的 fragment 索引,适合 HTTP 分段。 |
| 可实时生成 |
直播时不用等完整 moov,可以持续产出 moof + mdat。 |
| 适配 DASH / HLS |
现代 HLS / DASH 大量使用 fMP4 segment。 |
9. udta ------ 元数据(作者 / 标题)
| 子项 |
作用 |
常见内容 |
| 标题 |
展示媒体标题。 |
title / name。 |
| 作者 |
展示作者或制作方。 |
artist / author。 |
| 章节 |
支持章节跳转。 |
chapter track 或相关 metadata。 |
| 封面 / 扩展信息 |
展示专辑封面、应用私有数据等。 |
cover art、uuid、自定义字段。 |
10. 解析工具
| 工具 |
命令 |
适合看什么 |
| mp4info |
mp4info video.mp4 |
快速查看 MP4 概要和 box 树。 |
| mp4dump |
mp4dump video.mp4 |
详细查看 box 内容。 |
| ffprobe |
ffprobe -v debug video.mp4 2>&1 | grep "Box" |
看 FFmpeg 内部解析过程。 |
| Bento4 |
mp4dump video.mp4 |
MP4 结构分析常用工具。 |
| GPAC / MP4Box |
MP4Box -info video.mp4 |
查看 MP4 信息、封装和修复。 |
常用命令保留如下,方便复制:
mp4info video.mp4
mp4dump video.mp4
ffprobe -v debug video.mp4 2>&1 | grep "Box"
MP4Box -info video.mp4
11. 常见坑
11.1 moov 太大 → 起播慢
| 原因 |
表现 |
处理方式 |
| 长视频 / 多轨 / 大量 sample 表 |
4K 长视频 moov 可能达到 MB 级。 |
即使 faststart,也要先下载完整 moov 才能开播。 |
| 元数据和索引过多 |
起播前等待时间变长。 |
可考虑分片方案,如 HLS / DASH / fMP4。 |
11.2 sample 大小变化
| 情况 |
stsz 表现 |
说明 |
| 视频 sample 大小不同 |
sample_size == 0,必须有 entry_size[]。 |
I 帧大、P/B 帧小,1800 个 sample 就要记录 1800 个大小。 |
| 所有 sample 等大 |
sample_size != 0,可省略逐个大小。 |
PCM 音频这类固定大小 sample 可能使用。 |
11.3 时基不一致
| 时基 |
所在位置 |
含义 |
常见坑 |
| 全局 timescale |
moov/mvhd |
整个文件的影片级时基。 |
常见 1000,表示毫秒。 |
| 轨道 timescale |
trak/mdia/mdhd |
单条视频 / 音频 track 自己的时基。 |
视频可能是 30000、90000 等。 |
| 计算规则 |
各用各的 timescale。 |
不要把 mvhd 和 mdhd 的 timescale 混用。 |
混用会导致时长、PTS、seek 计算错误。 |
11.4 stsd 多 entry
| 场景 |
表现 |
影响 |
| 中途 codec 配置变化 |
stsd 有多个 entry。 |
sample 通过 stsc.sample_description_idx 标记使用哪条 entry。 |
| 不同质量 / 不同配置切换 |
某些特殊文件可能出现。 |
解码器初始化和切换要读对 entry。 |
12. 为什么 MP4 是音视频容器之王
| 优势 |
依赖能力 |
说明 |
| 流式播放友好 |
faststart / range request。 |
moov 前置后,HTTP 渐进式播放体验好。 |
| 精确 seek |
stco/co64、stsz、stts、stss。 |
可以从时间点快速换算到文件字节偏移。 |
| 多轨道支持 |
多个 trak。 |
视频、音频、字幕、多语言音轨都能放。 |
| 章节 / 字幕 / 元数据 |
chap、udta、字幕 track。 |
支持复杂媒体产品需求。 |
| 兼容性极好 |
浏览器、系统播放器、移动设备长期支持。 |
Web、移动端、桌面端生态都成熟。 |
| 适配 HLS / DASH |
fMP4。 |
现代流媒体分片协议大量使用 MP4 分片。 |
| 大厂推动 |
Apple / Microsoft / Google 等。 |
标准、工具链和平台支持稳定。 |
| 不足 |
说明 |
缓解方式 |
| 头部和索引较复杂 |
相比 TS / FLV,MP4 元数据表更多。 |
工具链成熟,问题可定位。 |
| 传统 MP4 不适合边生成边播 |
完整 moov 常在写完文件后生成。 |
用 faststart 或 fMP4。 |
| 实时直播历史上不如 TS / FLV 简单 |
单文件 MP4 需要完整索引。 |
fMP4 已解决很多直播分片问题。 |
13. 总结
MP4 = box 嵌套结构:
实战要点如下:
| 要点 |
作用 |
记忆方式 |
| faststart |
moov 前置,HTTP 流式播放必备。 |
起播先找目录。 |
大文件用 co64 |
64-bit chunk offset,避免超过 4GB 偏移限制。 |
大文件别只看 stco。 |
关键帧靠 stss |
seek 要先跳到同步点。 |
没 stss 通常表示全关键帧。 |
| fMP4 用于 DASH / HLS |
moof + mdat 分片,适合流媒体。 |
每段有自己的小索引。 |
stbl 是核心 |
时间、大小、偏移、关键帧、codec 配置都在这里。 |
弄懂 stsd/stts/stsc/stsz/stco/stss,就懂 MP4 一半。 |
金句 :MP4 看着复杂,但所有关键能力都藏在 stbl 那一堆表里。弄懂 stsd/stts/stsc/stsz/stco/stss 这 6 个表,就懂 MP4 一半。