简介
mp4文件格式又被称为MPEG-4 Part 14,出自MPEG-4标准第14部分 。它是一种多媒体格式容器,广泛用于包装视频和音频数据流、海报、字幕和元数据等。(顺便一提,目前流行的视频编码格式AVC/H264定义在MPEG-4 Part 10)。
mp4文件格式基于Apple公司的QuickTime格式,因此,QuickTime File Format Specification 也可以作为我们研究mp4的重要参考。
mp4文件由box组成,每个box分为Header和Data。其中Header部分包含了box的类型和大小,Data包含了子box或者数据,box可以嵌套子box。
下图是一个典型mp4文件的基本结构:
图中看到mp4文件由几个主要组成部分:
ftyp
File Type Box,一般在文件的开始位置,描述的文件的版本、兼容协议等。
moov
Movie Box,包含本文件中所有媒体数据的宏观描述信息以及每路媒体轨道的具体信息。一般位于ftyp之后,也有的视频放在文件末尾。注意,当改变moov位置时,内部一些值需要重新计算。
mdat
Media Data Box,存放具体的媒体数据。
Moov结构解析
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 结构解析
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在文件中的位置。大体需要以下步骤:
- 1.将pts转换到媒体对应的时间坐标系
- 2.根据stts计算某个pts对应的sample序号
- 3.根据stsc计算sample序号存放在哪个chunk中
- 4.根据stco获取对应chunk在文件中的偏移位置
- 5.根据stsz获取sample在chunk内的偏移位置并加上第4步获取的偏移,计算出sample在文件中的偏移
例如,想要获取3.64秒视频sample数据在文件中的位置:
- 1.根据time scale参数,将3.64秒转换为视频时间轴对应的3640000
- 2.遍历累加下表所示stts所有项目,计算得到3640000位于第110个sample
1 | type stts |
- 3.查询下表所示stsc所有项目,计算得到第110个sample位于第109个chunk,并且在该chunk中位于第2个sample
1 | type stsc |
- 4.查询下表所示stco所有项目,得到第109个chunk在文件中偏移位置为1710064
1 | Property name Property value |
- 5.查询下表所示stsz所有项目,得到第109个sample的size为14808。计算得到3.64秒视频sample数据在文件中
offset:1710064+14808 = 1724872
size:17930
1 | type stsz |
- 验证:用编辑器打开mp4文件,定位到文件偏移1724872位置,前4字节值为0x00004606。在avcc中一个sample的前4个字节代表这个包的大小,转换为十进制是17926,正好等于17930减去四个长度字节。