发布时间:2025-12-09 16:03:18 浏览次数:3
MP4文件中的所有数据都装在box(QuickTime中为atom)中,也就是说MP4文件由若干个box组成,每个box有类型和长度,可以将box理解为一个数据对象块。box中可以包含另一个box,这种box称为container box。一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。
下面是一些概念:
结构如下图:
MP4文件由多个box组成,每个box存储不同的信息,且box之间是树状结构,如下图所示。
box类型有很多,下面是3个比较重要的顶层box:
isom(ISO Base Media file)是在 MPEG-4 Part 12 中定义的一种基础文件格式,MP4、3gp、QT 等常见的封装格式,都是基于这种基础文件格式衍生的。
MP4 文件可能遵循的规范有mp41、mp42,而mp41、mp42又是基于isom衍生出来的。
ftyp定义
ftyp 定义如下:
下面是是 brand 的描述,其实就是具体封装格式对应的代码,用4个字节的编码来表示,比如 mp41。
A brand is a four-letter code representing a format or subformat. Each
file has a major brand (or primary brand), and also a compatibility
list of brands.
ftyp 的几个字段的含义:
在实际使用中,不能把 isom 做为 major_brand,而是需要使用具体的brand(比如mp41),因此,对于isom,没有定义具体的文件扩展名、mime type。
下面是常见的几种brand,以及对应的文件扩展名、mime type,更多brand可以参考 这里 。
在讨论 MP4 规范时,提到AVC,有的时候指的是“AVC文件格式”,有的时候指的是"AVC压缩标准(H.264)",这里简单做下区分。
Movie Box,存储 mp4 的 metadata,一般位于mp4文件的开头。
aligned(8) class MovieBox extends Box(‘moov’){ }moov中,最重要的两个box是 mvhd 和 trak:
mvhd针对整个影片,tkhd针对单个track,mdhd针对媒体,vmhd针对视频,smhd针对音频,可以认为是从 宽泛 >具体,前者一般是从后者推导出来的。
mvhd(Movie Header Box)
MP4文件的整体信息,跟具体的视频流、音频流无关,比如创建时间、文件时长等。
定义如下:
aligned(8) class MovieHeaderBox extends FullBox(‘mvhd’, version, 0) { if (version==1) {unsigned int(64) creation_time;unsigned int(64) modification_time;unsigned int(32) timescale;unsigned int(64) duration;} else { // version==0unsigned int(32) creation_time;unsigned int(32) modification_time;unsigned int(32) timescale;unsigned int(32) duration;}template int(32) rate = 0x00010000; // typically 1.0template int(16) volume = 0x0100; // typically, full volume const bit(16) reserved = 0;const unsigned int(32)[2] reserved = 0;template int(32)[9] matrix ={ 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 };// Unity matrixbit(32)[6] pre_defined = 0;unsigned int(32) next_track_ID;}字段含义如下:
tkhd(Track Box)
单个 track 的 metadata,包含如下字段:
version:tkhd box的版本;
flags:按位或操作获得,默认值是7(0x000001 | 0x000002 |0x000004),表示这个track是启用的、用于播放的 且 用于预览的。
Track_enabled:值为0x000001,表示这个track是启用的,当值为0x000000,表示这个track没有启用;
Track_in_movie:值为0x000002,表示当前track在播放时会用到;
Track_in_preview:值为0x000004,表示当前track用于预览模式;
creation_time:当前track的创建时间;
modification_time:当前track的最近修改时间;
track_ID:当前track的唯一标识,不能为0,不能重复;
duration:当前track的完整时长(需要除以timescale得到具体秒数);
layer:视频轨道的叠加顺序,数字越小越靠近观看者,比如1比2靠上,0比1靠上;
alternate_group:当前track的分组ID,alternate_group值相同的track在同一个分组里面。同个分组里的track,同一时间只能有一个track处于播放状态。当alternate_group为0时,表示当前track没有跟其他track处于同个分组。一个分组里面,也可以只有一个track;
volume:audio track的音量,介于0.0~1.0之间;
matrix:视频的变换矩阵;
width、height:视频的宽高;
定义如下:
aligned(8) class TrackHeaderBox extends FullBox(‘tkhd’, version, flags){ if (version==1) {unsigned int(64) creation_time;unsigned int(64) modification_time;unsigned int(32) track_ID;const unsigned int(32) reserved = 0;unsigned int(64) duration;} else { // version==0unsigned int(32) creation_time;unsigned int(32) modification_time;unsigned int(32) track_ID;const unsigned int(32) reserved = 0;unsigned int(32) duration;}const unsigned int(32)[2] reserved = 0;template int(16) layer = 0;template int(16) alternate_group = 0;template int(16) volume = {if track_is_audio 0x0100 else 0}; const unsigned int(16) reserved = 0;template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrixunsigned int(32) width;unsigned int(32) height;}例子如下:
mehd是可选的,用来声明影片的完整时长(fragment_duration)。如果不存在,则需要遍历所有的fragment,来获得完整的时长。对于fmp4的场景,fragment_duration一般没办法提前预知。
aligned(8) class MovieExtendsHeaderBox extends FullBox(‘mehd’, version, 0) {if (version==1) {unsigned int(64) fragment_duration;} else { // version==0unsigned int(32) fragment_duration;}}trex(Track Extends Box)
用来给 fMP4 的 sample 设置各种默认值,比如时长、大小等。
aligned(8) class TrackExtendsBox extends FullBox(‘trex’, 0, 0){ unsigned int(32) track_ID;unsigned int(32) default_sample_description_index; unsigned int(32) default_sample_duration;unsigned int(32) default_sample_size;unsigned int(32) default_sample_flags}字段含义如下:
default_sample_flags 占4个字节,比较复杂,结构如下:
老版本规范里,前6位都是保留位,新版规范里,只有前4位是保留位。is_leading 含义不是很直观,下一小节会专门讲解下。
例子如下:
关于 is_leading
is_leading 不是特别好解释,这里贴上原文,方便大家理解。
A leading sample (usually a picture in video) is defined relative to a reference sample, which is the immediately prior sample that is marked as “sample_depends_on” having no dependency (an I picture). A leading sample has both a composition time before the reference sample, and possibly also a decoding dependency on a sample before the reference sample. Therefore if, for example, playback and decoding were to start at the reference sample, those samples marked as leading would not be needed and might not be decodable. A leading sample itself must therefore not be marked as having no dependency.
为方便讲解,下面的 leading frame 对应 leading sample,referenced frame 对应 referenced samle。
以 H264编码 为例,H264 中存在 I帧、P帧、B帧。由于 B帧 的存在,视频帧的 解码顺序、渲染顺序 可能不一致。
mp4文件的特点之一,就是支持随机位置播放。比如,在视频网站上,可以拖动进度条快进。
很多时候,进度条定位的那个时刻,对应的不一定是 I帧。为了能够顺利播放,需要往前查找最近的一个 I帧,如果可能的话,从最近的 I帧 开始解码播放(也就是说,不一定能从前面最近的I帧播放)。
将上面描述的此刻定位到的帧,称作 leading frame。leading frame 前面最近的一个 I 帧,叫做 referenced frame。
回顾下 is_leading 为 1 或 3 的情况,同样都是 leading frame,什么时候可以解码(decodable),什么时候不能解码(not decodable)?
1: this sample is a leading sample that has a dependency before the referenced I‐picture (and is therefore not decodable);
3: this sample is a leading sample that has no dependency before the referenced I‐picture (and is therefore decodable);
1、is_leading 为 1 的例子: 如下所示,帧2(leading frame) 解码依赖 帧1、帧3(referenced frame)。在视频流里,从 帧2 往前查找,最近的 I帧 是 帧3。哪怕已经解码了 帧3,帧2 也解不出来。
2、is_leading 为 3 的例子: 如下所示,此时,帧2(leading frame)可以解码出来。
moof是个container box,相关 metadata 在内嵌box里,比如 mfhd、 tfhd、trun 等。
伪代码如下:
aligned(8) class MovieFragmentBox extends Box(‘moof’){ }结构比较简单,sequence_number 为 movie fragment 的序列号。根据 movie fragment 产生的顺序,从1开始递增。
aligned(8) class MovieFragmentHeaderBox extends FullBox(‘mfhd’, 0, 0){unsigned int(32) sequence_number;}traf(Track Fragment Box)
aligned(8) class TrackFragmentBox extends Box(‘traf’){ }对 fmp4 来说,数据被氛围多个 movie fragment。一个 movie fragment 可包含多个track fragment(每个 track 包含0或多个 track fragment)。每个 track fragment 中,可以包含多个该 track 的 sample。
每个 track fragment 中,包含多个 track run,每个 track run 代表一组连续的sample。
tfhd 用来设置 track fragment 中 的 sample 的 metadata 的默认值。
伪代码如下,除了 track_ID,其他都是 可选字段。
aligned(8) class TrackFragmentHeaderBox extends FullBox(‘tfhd’, 0, tf_flags){unsigned int(32) track_ID;// all the following are optional fields unsigned int(64) base_data_offset; unsigned int(32) sample_description_index; unsigned int(32) default_sample_duration; unsigned int(32) default_sample_size; unsigned int(32) default_sample_flags}sample_description_index、default_sample_duration、default_sample_size 没什么好讲的,这里只讲解下 tf_flags、base_data_offset。
首先是 tf_flags,不同 flag 的值如下(同样是求按位求或) :
sample 位置计算公式为 base_data_offset + data_offset,其中,data_offset 每个 sample 单独定义。如果未显式提供 base_data_offset,则 sample 的位置的通常是基于 moof 的相对位置。
举个例子,比如 tf_flags 等于 57,表示 存在 base_data_offset、default_sample_duration、default_sample_flags。
base_data_offset 为 1263 (ftyp、moov 的size 之和为 1263)。
trun 伪代码如下:
aligned(8) class TrackRunBox extends FullBox(‘trun’, version, tr_flags) {unsigned int(32) sample_count;// the following are optional fieldssigned int(32) data_offset;unsigned int(32) first_sample_flags;// all fields in the following array are optional{unsigned int(32) sample_duration;unsigned int(32) sample_size;unsigned int(32) sample_flagsif (version == 0){ unsigned int(32) sample_composition_time_offset; }else{ signed int(32) sample_composition_time_offset; }}[ sample_count ]}前面听过,track run 表示一组连续的 sample,其中:
tr_flags 如下,大同小异:
举例如下,tr_flags 为 2565。此时,存在 data_offset 、first_sample_flags、sample_size、sample_composition_time_offset。