LV080-MP4封装简介
一、MP4 简介
了解了 FLV 封装之后,我们再来看一下 MP4 封装。MP4 封装相比 FLV 更常见,但是也更复杂一些。其实它们的基本的思想还是一样的,就是用一个规定的格式组织存放音视频数据和一些基础信息。跟 FLV 由一个个 Tag 组成有点类似,MP4 由一个个 box 组成,每一个 box 存放了不同的数据,而且 box 里面还可以嵌套着 box。
MP4 或称 MPEG-4 第 14 部分(MPEG-4 Part 14),是一种标准的数字多媒体容器格式。扩展名为.mp4。虽然被官方标准定义的唯一扩展名是.mp4,但第三方通常会使用各种扩展名来指示文件的内容:同时拥有音频视频的 MPEG-4 文件通常使用标准扩展名.mp4;仅有音频的 MPEG-4 文件会使用.m4a 扩展名。还有一些其他的,例如 .m4p, .m4b, .m4r, .m4v 也是 MP4 格式的扩展名,只是不同场景使用不同的扩展名。
大部分数据可以通过专用数据流嵌入到 MP4 文件中,因此 MP4 文件中包含了一个单独的用于存储流信息的轨道。目前得到广泛支持的编解码器或数据流格式有:
- 视频格式:H.264/AVC、H.265/HEVC、VP8/9 等
- 音频格式:AAC、MP3、Opus、G711 等
MP4 的封装格式是 基于 ISO/IEC 14496-12:2022 标准实现的。14496 其实是 MPEG-4 标准的一组协议族,大概有 30 多个文档,定义了一些编解码标准,容器格式。而 MP4 所用的文档,就是第 12 个文档,标题是 “ISO base media format” (基础媒体格式)。
这里有一份可以在线看的文档可以参考:ISO IEC 14496-12 2022 - 道客巴巴,还有一个 2015 版本的在这里:ebooks/ISO_IEC_14496-12_2015.pdf at master · qhyuan1992/ebooks · GitHub
MPEG-4 是一个庞大的“总标准”,而 ISO/IEC 14496-12 是这个总标准下的一个具体的“基础性分标准”。 MP4 文件格式正是基于这个基础分标准定义的。
二、几个术语
为了后面能比较规范的了解这种文件格式,这里需要了解下面几个概念和术语,这些概念和术语是理解好 MP4 媒体封装格式和其操作算法的关键。
1. Box
这个概念起源于 QuickTime 中的 atom,也就是说 MP4 文件就是由一个个 Box 组成的,可以将其理解为一个数据块,它由 Header+Data 组成,Data 可以存储媒体元数据和实际的音视频码流数据。Box 里面可以直接存储数据块但是也可以包含其它类型的 Box,我们把这种 Box 又称为 container box。
2. Sample
简单理解为采样,对于视频可以理解为一帧数据,音频一帧数据就是一段固定时间的音频数据,可以由多个 Sample 数据组成,简而言之:存储媒体数据的单位是 sample。
3. Track
表示一些 sample 的集合,对于媒体数据而言就是一个视频序列或者音频序列,我们常说的音频轨和视频轨可以对照到这个概念上。当然除了 Video Track 和 Audio Track 还可以有非媒体数据,比如 Hint Track,这种类型的 Track 就不包含媒体数据,可以包含一些将其他数据打包成媒体数据的指示信息或者字幕信息。简单来说:Track 就是电影中可以独立操作的媒体单位。
4. Chunk
一个 track 的连续几个 sample 组成的单元被称为 chunk,每个 chunk 在文件中有一个偏移量,整个偏移量从文件头算起,在这个 chunk 内,sample 是连续存储的。
这样就可以理解为 MP4 文件里面有多个 Track, 一个 Track 又是由多个 Chunk 组成,每个 Chunk 里面包含着一组连续的 Sample,正是因为定义了上述几个概念,MP4 这种封装格式才容易实现灵活、高效、开放的特性,所以要仔细理解。
二、封装格式简介
1. 一个简单的 ISO file 结构
下图是一个简单的交换文件的结构:

ISO 媒体文件通过 Box 索引和存储数据,并且数据和索引是分开存放的,从上面的图中能够看到 trak 存储了索引信息,而具体的数据存储在 mdat 中。
有哪些 box 类型?具体可以查看 ISO IEC 14496-12 2022 的 Table 1-Box types, structure and cross-reference
2. MP4 的 Box 简介
MP4 最外层的 box 主要有三个,分别是 File Type box(ftyp box)、Movie box(moov box)和 Media Data box(mdat box)。其中最重要、最复杂的就是 moov box 了,它里面存放了音视频的基本信息和每一个音视频数据的具体位置。

还有一点需要说明的就是:在 MP4 文件中,视频的一帧和音频的一段编码数据称为一个 sample。连续的几个 sample 称之为 chunk,而视频的所有 sample 称为一个视频 track,同样音频的所有 sample 也称为一个音频 track。
因此一般 MP4 文件是由音频 track 和视频 track 组成,而 track 由 sample 组成,其中若干个 sample 组成一个 chunk。如下图,测试视频来自于一个在线视频文件 vjs.zencdn.net/v/oceans.mp4:

3. 完整的 Box 结构
MP4 是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,以及各种编码的音视频。MP4 文件由许多个 Box 和 FullBox 组成。每个 Box 包含不同的信息, 这些 Box 以树形结构的方式组织。
每个 Box 由 Header 和 Data 两部分组成。Data 是 Box 的实际数据,可以是纯数据,也可以是更多的子 Box(这个 Box 称为 Container Box)。

FullBox 是 Box 的拓展,其在 Box 结构的基础上,在 Header 中添加 1 字节的 version 标志和 3 字节的 flags 标志。
根据 Box Header 中的 type 我们将 box 分为不同类型的 box,每一种不同的 box 对应的 Box Data 都是不一样。Box Data 里面又可以嵌套 box。MP4 的总体 box 分布图如下图所示:

MP4 标准中没有对 moov 和 mdat 的存放位置没有强制先后要求,所以它们的顺序不定。在互联网视频的点播中,如果希望 MP4 文件被快速打开,则需要将 moov 存放在 mdat 的前面,这个叫索引前置;如果 moov 放在 mdat 的后面,则需要将 MP4 文件全部下载完才可以播放,这叫索引后置。
常见 Box 的简要说明:
| box 类型 | 十六进制 | 说明 | ||||
| ftyp | 0x66747970 | file type,表明文件类型 | ||||
| moov | 0x6D6F6F76 | metadata container, 存放媒体信息的地方 | ||||
| mvhd | 0x6D766864 | movie header,文件的总体信息,如时长,创建时间等 | ||||
| trak | 0x7472616B | track or stream container, 存放视频/音频流的容器 | ||||
| tkhd | 0x746B6864 | track header,track 的总体信息,如时长,宽高等 | ||||
| mdia | 0x6D646961 | trak media information container, 不解释 | ||||
| mdhd | 0x6D646864 | media header,定义 TimeScale,trak 需要通过 TimeScale 换算成真实时间 | ||||
| hdlr | 0x68646C72 | handler,表明本 trak 类型,指明是 video/audio/还是 hint | ||||
| minf | 0x6D696E66 | media information container, 数据在子 box 中 | ||||
| stbl | 0x7374626C | sample table box,存放时间/偏移的映射关系表,数据在子 box 中 | ||||
| stsd | 0x73747364 | sample descriptions | ||||
| stts | 0x73747473 | (decoding)time-to-sample, "时戳-sample 序号" 的映射表 | ||||
| stsc | 0x73747363 | sample-to-chunk,sample 和 chunk 的映射表,这里的算法比较巧妙 | ||||
| stsz | 0x7374737A | sample size, 每个 sample 的大小 | ||||
| stz2 | sample size,另一种 sample size 的存储算法,更节省空间 | |||||
| stss | 0x73747373 | sync sample table,可随机访问的 sample 列表(关键帧列表) | ||||
| stco | 0x7374636F | chunk offset,每个 chunk 的偏移,sample 的偏移可根据其他 box 推算出来 | ||||
| co64 | 0x636F3634 | 64-bit chunk offset | ||||
| ctts | 0x63747473 | (composition) time to sample | ||||
| mdat | 0x6D646174 | media data container, 具体的媒体数据 | ||||
4. Box Header
box 分为普通 box 和 fullbox。在 ISO IEC 14496-12 2022 的 4.2.2 Object definitions 一节中有描述:
aligned(8) class Box (unsigned int(32) boxtype, optional unsigned int(8)[16] extended_type) {
unsigned int(32) size;
unsigned int(32) type = boxtype;
if (size==1) {
unsigned int(64) largesize;
}
else if (size==0) {
// box extends to end of file
}
if (boxtype==‘uuid’) {
unsigned int(8)[16] usertype = extended_type;
}
}
aligned(8) class FullBox(unsigned int(32) boxtype, unsigned int(8) v, bit(24) f)
extends Box(boxtype) {
unsigned int(8) version = v;
bit(24) flags = f;
}Box Header 至少占 8 字节。第一个字段是 size,占 4 字节, 表示整个 Box 的大小。第二个字段是 type,占 4 字节,表示 Box 的类型。当 size = 0 时,代表这是 MP4 文件的最后一个 Box,当 size = 1 时,说明 Box 长度需要更多位才能描述,在 type 后会定义一个 8 字节的 largesize 用来描述 Box 的长度。当 type = uuid 时,说明这个 Box 中的数据时用户自定义拓展类型。
- (1)普通 box header 结构如下:
| 字段 | 类型 | 描述 |
|---|---|---|
| size | 4 Bytes | 包含 box header 的整个 box 的大小 |
| type | 4 Bytes | 4 个 ascii 值,如果是 "uuid",则表示此 box 为用户自定义,可忽略 |
| large size | 8 Bytes | size = 1 时才有的字段,用于扩展,例如 mdat box 会需要此字段 |
- (2)fullbox 在上面的基础上新增了 2 个字段:
| 字段 | 类型 | 描述 |
|---|---|---|
| version | 1 Bytes | 版本号 |
| flags | 3 Bytes | 标识 |
参考资料:
封装格式之 MP4 格式简介 — MyNote release 文档
MP4 封装格式—音视频基础知识 · FFmpeg 原理 · FFmpeg 书籍