你真的懂 MP4 格式吗?
MP4 文件格式又被称为 MPEG-4 Part 14,出自 MPEG-4 标准第 14 部分 。它是一种多媒体格式容器,广泛用于包装视频和音频数据流、海报、字幕和元数据等。(顺便一提,目前流行的视频编码格式 AVC/H264 定义在 MPEG-4 Part 10)。MP4 文件格式基于 Apple 公司的 QuickTime 格式,因此,QuickTime File Format Specification 也可以作为我们研究 MP4 的重要参考。
作者:张武星
审核:泰一
Overview
MP4 文件由 box 组成,每个 box 分为 Header 和 Data。其中 Header 部分包含了 box 的类型和大小,Data 包含了子 box 或者数据,box 可以嵌套子 box。
下图是一个典型 MP4 文件的基本结构:
图中看到 MP4 文件有几个主要组成部分:
fytp
File Type Box,一般在文件的开始位置,描述的文件的版本、兼容协议等。
moov
Movie Box,包含本文件中所有媒体数据的宏观描述信息以及每路媒体轨道的具体信息。一般位于 ftyp 之后,也有的视频放在文件末尾。注意,当改变 moov 位置时,内部一些值需要重新计算。
mdat
Media Data Box,存放具体的媒体数据。
Moov Insider
MP4 的媒体数据信息主要存放在 Moov Box 中,是我们需要分析的重点。moov 的主要组成部分如下:
mvhd
Movie Header Box,记录整个媒体文件的描述信息,如创建时间、修改时间、时间度量标尺、可播放时长等。
下图示例中,可以获取文件信息如时长为 3.637 秒。
udta
User Data Box,自定义数据。
track
Track Box,记录媒体流信息,文件中可以存在一个或多个 track,它们之间是相互独立的。每个 track 包含以下几个组成部分:
tkhd
Track Header Box,包含关于媒体流的头信息。
下图示例中,可以看到流信息如视频流宽度 720,长度 1280。
mdia
Media Box,这是一个包含 track 媒体数据信息的 container box。子 box 包括:
- mdhd:Media Header Box,存放视频流创建时间,长度等信息。
- hdlr:Handler Reference Box,媒体的播放过程信息。
- minf:Media Information Box,解释 track 媒体数据的 handler-specific 信息。minf 同样是个 container box,其内部需要关注的内容是 stbl,这也是 moov 中最复杂的部分。
stbl 包含了媒体流每一个 sample 在文件中的 offset,pts,duration 等信息。想要播放一个 MP4 文件,必须根据 stbl 正确找到每个 sample 并送给解码器。
mdia 展开如下图所示:
Stbl Insider
Sample Table Box,上文提到 mdia 中最主要的部分是存放文件中每个 Sample 信息的 stbl。在解析 stbl 前,我们需要区分 Chunk 和 Sample 这两个概念。
在 MP4 文件中,Sample 是一个媒体流的基本单元,例如视频流的一个 Sample 代表实际的 nal 数据。Chunk 是数据存储的基本单位,它是一系列 Sample 数据的集合,一个 Chunk 中可以包含一个或多的 Sample。
stbl 用来描述每个 Sample 的信息,包含以下几个主要的子 box:
stsd
Sample Description Box,存放解码必须的描述信息。
下图示例中,对于 h264 的视频流,其具体类型为 avc1
,extensions 中存放有 sps,pps 等解码必要信息。
stts
Time-to-Sample Box,定义每个 Sample 时长。Time To Sample 的 table entry 布局如下:
- Sample count:sample 个数
- Sample duration:sample 持续时间
持续时间相同的连续的 Sample 可以放到一个 entry 里面,以达到节省空间的目的。
下图示例中,第 1 个 Sample 时间为 33362 微秒,第 2-11 个 Sample 时间为 33363 微秒:
stss
Sync Sample Box,同步 Sample 表,存放关键帧列表,关键帧是为了支持随机访问。
stss 的 table entry 布局如下:
下图示例中,该视频 track 只有一个关键帧即第 1 帧:
stsc
Sample-To-Chunk Box,Sample-Chunk 映射表。上文提到 MP4 通常把 Sample 封装到 Chunk 中,一个 Chunk 可能会包含一个或者几个 Sample。Sample-To-Chunk Atom 的 table entry 布局如下图所示:
- First chunk:使用该表项的第一个 chunk 序号。
- Samples per chunk:使用该表项的 chunk 中包含有几个 sample。
- Sample description ID:使用该表项的 chunk 参考的 stsd 表项序号。
下图示例中,可以看到该视频 track 一共有两个 stsc 表项,Chunk 序列 1-108,每个 Chunk 包含一个 sample,Chunk 序列 109 开始,每个 Chunk 包含两个 Sample。
stsz
Sample Size Box,指定了每个 Sample 的 size。Sample Size Atom 包含两 Sample 总数和一张包含了每个 Sample Size 的表。
Sample Size 表的 entry 布局如下图:
下图示例中,该视频流一共有 110 个 Sample,第 1 个 Sample 大小为 42072 字节,第 2 个 Sample 大小为 7354 个字节。
stco
Chunk Offset Box,指定了每个 Chunk 在文件中的位置,这个表是确定每个 Sample 在文件中位置的关键。该表包含了 Chunk 个数和一个包含每个 Chunk 在文件中偏移位置的表。每个表项的内存布局如下:
需要注意,这里 stco 只是指定的每个 Chunk 在文件中的偏移位置,并没有给出每个 Sample 在文件中的偏移。想要获得每个 Sample 的偏移位置,需要结合 Sample Size box 和 Sample-To-Chunk 计算后取得。
下图示例中,该视频流第 1 个 Chunk 在文件中的偏移为 4750,第 1 个 Chunk 在文件中的偏移为 47007。
如何计算 Sample 偏移位置
上文提到通过 stco 并不能直接获取某个 Sample 的偏移位置,下面举例说明如何获取某一个 pts 对应的 Sample 在文件中的位置。大体需要以下步骤:
- 将 pts 转换到媒体对应的时间坐标系。
- 根据 stts 计算某个 pts 对应的 Sample 序号。
- 根据 stsc 计算 Sample 序号存放在哪个 Chunk 中。
- 根据 stco 获取对应 Chunk 在文件中的偏移位置。
- 根据 stsz 获取 Sample 在 Chunk 内的偏移位置并加上第 4 步获取的偏移,计算出 Sample 在文件中的偏移。
例如,想要获取 3.64 秒视频 Sample 数据在文件中的位置:
- 根据 time scale 参数,将 3.64 秒转换为视频时间轴对应的 3640000。
- 遍历累加下表所示 stts 所有项目,计算得到 3640000 位于第 110 个 Sample。
type stts
size 224
flags 0
version 0
sample_counts 1,10,1,1,11,1,1,2,1,25,1,1,1,17,1,10,1,1,1,7,1,1,1,1,10,1
sample_deltas 33362,33363,33362,33364,33363,33362,33364,33363,33362,33363,33362,33364,33362,33363,33362,33363,33362,33364,33362,33363,33362,33364,33363,33362,33363,0
- 查询下表所示 stsc 所有项目,计算得到第 110 个 Sample 位于第 109 个 Chunk,并且在该 Chunk 中位于第 2 个 Sample。
type stsc
size 40
flags 0
version 0
first_chunk 1,109
samples_per_chunk 1,2
sample_description_index 1,1
- 查询下表所示 stco 所有项目,得到第 109 个 Chunk 在文件中偏移位置为 1710064。
Property name Property value
type stco
size 452
flags 0
version 0
chunk_offsets 4750,47007,54865,61967,75519,88424,105222,117892,133730,149529,165568,182034,194595,210776,225470,240756,255358,270711,285459,300135,315217,330899,347372,363196,376409,394509,407767,424615,438037,455603,469784,487287,505197,519638,536714,553893,567187,584744,599907,615298,630669,645918,662605,678655,693510,708980,724061,738946,754170,771520,787233,800847,816997,832490,847814,862559,877929,898379,911054,925810,943883,956497,974403,991527,1009478,1025198,1041806,1062609,1078401,1091360,1105142,1118748,1132815,1145281,1156966,1171871,1186742,1202760,1218235,1236688,1249330,1263163,1280880,1297903,1313162,1332885,1345726,1359017,1376283,1391401,1405512,1419550,1433644,1452103,1475241,1492689,1511291,1522606,1535368,1559413,1575331,1588853,1609829,1626623,1642798,1658640,1674160,1693972,1710064
- 查询下表所示 stsz 所有项目,得到第 109 个 Sample 的 size 为 14808。计算得到 3.64 秒视频 Sample 数据在文件中:
offset:1710064 + 14808 = 1724872
size:17930
type stsz
size 460
flags 0
version 0
sample_sizes 42072,7354,6858,13110,12684,16416,12490,15497,15630,15865,16116,12387,15775,14519,14929,14433,15181,14390,14496,14717,15507,16101,15643,12843,17911,13070,16455,13221,17186,14002,17139,17737,14251,16708,16999,12911,17356,14801,15213,15016,15062,16505,15689,14657,15053,14907,14527,15048,17161,15308,13432,15777,15307,14971,14568,14987,20264,12494,14382,17873,12235,17718,16770,17766,15366,16420,20623,15403,12761,13394,13390,13714,12295,11505,14541,14689,15635,15291,18091,12458,13645,17346,16847,14902,19530,12446,13105,16872,14937,13944,13657,13908,18092,22959,17080,18421,11129,12400,23844,15564,13340,20603,16609,15984,15474,15339,19451,15719,14808,17930
sample_size 0
sample_count 110
- 验证:用编辑器打开 MP4 文件,定位到文件偏移 offset = 1724872 的位置,前 4 字节值为 0x00004606。在 avcc 中一个 Sample 的前 4 个字节代表这个包的大小,转换为十进制是 17926,该值正好等于 size = 17930 减去表示长度的四个字节。
参考资料
在线 MP4 解析工具
QuickTime File Format Specification
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。