音视频修炼之容器与协议(一):MP4和MOV的box剖析
2026/10/20 12:12:36
网站建设
项目流程
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 的字段、字节数和作用:
![]()
1.1 通用 Box Header
| 字段 | 字节数 | 含义 | 注意事项 |
|---|
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-bitsize。 | 大多数 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,或者不同区间使用不同stsdentry。 |
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-vdebug video.mp42>&1|grep"Box"MP4Box-infovideo.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 一半。