在多媒体开发中,MP4(MPEG-4 Part 14) 与 M4A(纯音频 MP4) 是应用广泛的封装容器。它们均派生自 ISO 基础媒体文件格式(ISOBMFF, ISO/IEC 14496-12)。
MP4 以 Box / Atom 组织数据。普通 MP4 文件中,moov 保存轨道信息和样本索引,mdat 保存媒体载荷;播放器需要结合两者找到并解码样本。
下面以普通 MP4 为例说明索引与定位,不展开分片 MP4(fMP4)的结构。
1. MP4 核心概念辨析
[ MP4 文件 ]
├── moov (元数据管理中心: 记录 Sample 索引表与时间轴)
└── mdat (物理载荷数据: 存储编码后的音频/视频裸流)
│
└── Track 0 (音频轨) ──> [ Chunk 0: Sample 0, 1, 2 ] ──> [ Chunk 1: Sample 3, 4 ... ]
└── Track 1 (视频轨) ──> [ Chunk 0: Sample 0 ] ───────> [ Chunk 1: Sample 1 ... ]
- Box (Atom):
- MP4 的基本组织单元。标准 Box 由 8 字节基础头部(4 字节
size+ 4 字节type) 与 Body 数据 构成。当size == 1时启用 8 字节扩展大小(largesize),头部扩展为 16 字节;数据均按大端网络字节序排列;
- MP4 的基本组织单元。标准 Box 由 8 字节基础头部(4 字节
- Sample(样本):
- 媒体数据的最小时间单元。在视频中一个 Sample 通常代表一帧画面;在音频中一个 Sample 代表一个编码帧(如一个包含 1024 个 PCM 样点的 AAC 帧);
- Chunk(数据块):
- 同一个 Track 内连续多个 Sample 在物理文件中的聚合连续存储区间;
- Track(媒体轨道):
- 一个独立的媒体时序流(如音频轨、视频轨、字幕轨)。
2. 顶级 Box 树状层级结构
一个典型的 MP4/M4A 文件顶层通常包含以下结构:
graph TD
MP4[MP4 / M4A 文件根目录] --> FTYP[ftyp: 文件类型与兼容协议标头]
MP4 --> MOOV[moov: 媒体元数据与全量索引 Container]
MP4 --> MDAT[mdat: 音视频真实媒体数据载荷]
MOOV --> MVHD[mvhd: 全局时长/时间刻度 Header]
MOOV --> TRAK[trak: 媒体轨道 音频/视频]
TRAK --> TKHD[tkhd: 轨道属性 Volume/TrackID]
TRAK --> MDIA[mdia: 媒体信息容器]
MDIA --> MDHD[mdhd: 媒体时间基准 Timescale]
MDIA --> HDLR[hdlr: 处理器类型 soun/vide]
MDIA --> MINF[minf: 媒体详细信息]
MINF --> STBL[stbl: 核心 Sample 索引表 Sample Table]
STBL --> STSD[stsd: 编解码器描述 如 mp4a/avc1]
STBL --> STTS[stts: 时间到 Sample 映射解码时长]
STBL --> STSC[stsc: Sample 到 Chunk 映射表]
STBL --> STSZ[stsz: 每个 Sample 的精确字节大小]
STBL --> STCO[stco/co64: Chunk 在文件中的绝对字节偏移]
3. stbl 样本索引表映射机制
播放器要定位第 $K$ 个音视频帧在文件中的物理位置,依靠 stbl(Sample Table Box)下的四个核心子表协同计算:
stts(Time-to-Sample):记录每个 Sample 持续的时长(Delta),用于计算播放时间与 Seek 定位;stsc(Sample-to-Chunk):记录 Sample 如何打包进 Chunk(例如:Chunk 1~5 每个包含 3 个 Sample);stsz(Sample Size):记录每个 Sample 的实际字节大小;stco/co64(Chunk Offset):记录每个 Chunk 在整个文件中的绝对字节偏移。
4. Web 流媒体优化:moov faststart
录制生成普通 MP4 时,复用器常在结束后写入完整索引,因此 moov 可能位于 mdat 之后。
默认未优化 MP4 结构:
+-------------------+------------------------------------------+-------------------+
| ftyp (几十字节) | mdat (媒体数据载荷, 几百 MB ~ 几 GB) | moov (元数据) |
+-------------------+------------------------------------------+-------------------+
(浏览器必须下载完整个文件或发起多次 Range 请求读取末尾 moov 才能起播)
优化后的 faststart MP4 结构:
+-------------------+-------------------+------------------------------------------+
| ftyp (几十字节) | moov (元数据) | mdat (媒体数据载荷, 几百 MB ~ 几 GB) |
+-------------------+-------------------+------------------------------------------+
(播放器可先读取 moov,再按样本索引读取媒体数据)
FFmpeg 优化命令
# 重构 Box 顺序,将 moov 移动至 mdat 之前 (零重编码)
ffmpeg -i input.mp4 -c copy -movflags faststart output_faststart.mp4
5. 总结
排查 MP4 起播或 Seek 问题时,可以先检查 moov 的位置,再检查样本时间与偏移表。faststart 调整的是 Box 顺序,不会重新编码音视频,也不保证消除网络和播放器本身的等待时间。