LV085-MP4常见Box
一、常见 box
好了,下面我们就来看看比较重要的 box 吧。 因为,MP4 的 box 特别多,我们不会一个个都介绍,我们只介绍一下比较重要的 box。
1. ftyp(File Type Box)
1.1 box 说明
首先,ftyp box 放在 MP4 文件的开始,用来表示文件类型,该 box 的 Box Data 包含了 4 字节的主版本(major brand)、4 字节的版本号(minor version)和若干个 4 字节数组组成的兼容版本(compatible_brands)。
该 Box 有且仅有 1 个,并且只能被包含在文件层,而不能被其他 Box 包含。该 Box 放在文件的最开始,指示文件的相关信息。伪代码如下:
aligned(8) class FileTypeBox
extends Box(‘ftyp’) {
unsigned int(32) major_brand; // 主版本
unsigned int(32) minor_version; // 次版本
unsigned int(32) compatible_brands[]; // 指定兼容的版本,注意此字段是一个 list,即可以包含多个 4 bytes 版本号
}1.2 示例
这里是一个 ftyp box 的数据,可以看到大小为 0x18,也就是 24 字节,头占了 8 个字节,数据占了 16 字节。但是这里好像没有显示全,完整的数据应该是下面的部分:

2. moov(Movie Box)
moov 是一个 container box,一个文件只有一个,其包含的所有 box 用于描述媒体信息(metadata)。它可以紧随着 ftyp 出现,也可以出现在文件末尾。
由于是一个 container box,所以除了 box header,其 box body 就是其它的 box。这个 box 中会一层层嵌套很多层 box。总体嵌套逻辑就是 movie 里面是 track,track 里面是 sample,多个 sample 又组成了一个个 chunk。
2.1 mvhd(Movie Header Box)
2.1.1 box 说明
mvhd box(movie header box) 作为媒体信息的 header 出现 (注意此 header 不是 box header,而是 moov 媒体信息的 header),用于描述一些所有媒体共享的基本信息。比如说 MP4 文件的创建时间、时间单位、总时长等信息。
这个 box 的伪代码如下:
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 == 0
unsigned int(32) creation_time;
unsigned int(32) modification_time;
unsigned int(32) timescale;
unsigned int(32) duration;
}
template int(32) rate = 0x00010000; // typically 1.0
template 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 matrix
bit(32)[6] pre_defined = 0;
unsigned int(32) next_track_ID;
}mvhd 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
| 字段 | 类型 | 描述 |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | 标识 |
| creation time | 64/32 bit | 创建时间,当 version = 0 时取 32bit |
| modification time | 64/32 bit | 修改时间,当 version = 0 时取 32bit |
| timescale | 32 bit | 一秒包含的时间单位(整数)。举个例子,如果 timescale 等于 1000,那么,一秒包含 1000 个时间单位(后面 track 等的时间,都要用这个来换算,比如 track 的 duration 为 10,000,那么,track 的实际时长为 10,000/1000 = 10s)。 |
| duration | 64/32 bit | 文件时长,当 version = 0 时取 32bit |
| rate | 32 bit | 播放速率,默认取 0x00010000,即 1.0 |
| volume | 16 bit | 音量,默认取 0x0100,即 1.0 |
| reserved | 16 bit | |
| reserved | 2x32 bit | |
| matrix | 9x32 bit | 可忽略 |
| pre defined | 6x32 bit | |
| next track ID | 32 bit | 32 位整数,非 0,一般可以忽略不计。当要添加一个新的 track 到这个影片时,可以使用的 track id,必须比当前已经使用的 track id 要大。也就是说,添加新的 track 时,需要遍历所有 track,确认可用的 track id |
2.1.2 示例
我们来找一些这个 duration,自己不想计算的话,可以换个工具,比如 MP4 Reader、AtomicBrowser 等工具,都可以直接解析出每部分的含义,这里使用 MP4 Reader 打开,因为 AtomicBrowser 没找到下载链接。

2.2 trak
track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。具体是音频 trak 还是视频 trak,会在 trak box 中的 mdia box 里面的 hdlr box 中表示出来。
它是一个容器 Box,本身不包含任何内容,其含义由内部的 Box 定义。媒体文件中可能包含多条 Track,每一条 Track 是独立的,都有自己的时间和空间信息,比如音频和视频可以单独分为两个 Track 存放,而单个视频流也可以分为多个 Track 存放。
2.3 trak/tkhd
tkhd box(track header box) 作为媒体信息的 header 出现 (注意此 header 不是 box header,而是 track 媒体信息的 header),用于描述一些该 track 的基本信息。比如说视频的宽高信息和音频的音量信息等。一个 Track 只有一个 tkhd。
| 字段 | 类型 | 描述 |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | 标识 |
| creation time | 64/32 bit | 创建时间, 当 version = 0 时取 32bit |
| modification time | 64/32 bit | 修改时间, 当 version = 0 时取 32bit |
| track ID | 32 bit | 当前 track id, 是唯一表示,一个文件中不应该存在两个 Track_ID 相同的 Track; |
| reserved | 32 bit | |
| duration | 64/32 bit | 当前轨道的时长,如果有轨道的编辑列表则值为所有编辑列表持续时间之和;否则为样本持续时间之和。如果无法确认则时间设置为 0xfffffff。另外时间需要根据 mvhd 中的 time_scale 计算。当 version = 0 时取 32bit |
| reserved | 2x32 bit | |
| layer | 16 bit | 视频轨道的叠加顺序, 数字越小越靠近观看者, 比如 1 比 2 靠上,0 比 1 靠上; |
| alternate group | 16 bit | 当前 track 的分组 ID, alternate group 值相同的 track 在同一个分组里面。同个分 组里的 track, 同一时间只能有一个 track 处于播放状态。 当 alternate group 为 0 时, 表示当前 track 没有跟其他 track 处于同个分组。一个分组里面, 也可以只有一个 track; |
| volume | 16 bit | 音量, 如果是 audio track, 则为 0x0100, 即 1.0, 否则取 0 |
| reserved | 16 bit | |
| matrix | 9x32 bit | 视频的转换矩阵,默认是个单位矩阵; |
| width | 32 bit | 宽, [16.16] 格式(高 16 位为整数部分,低 16 位为小数部分), 不必与 sample 的像素尺寸一致, 用于播放时的展示宽高,在对轨道进行操作前图像都会缩放到当前的 size。 |
| height | 32 bit | 高, [16.16] 格式(高 16 位为整数部分,低 16 位为小数部分), 不必与 sample 的像素尺寸一致, 用于播放时的展示宽高,在对轨道进行操作前图像都会缩放到当前的 size。 |
2.4 trak/mdia
trak box 还有一个 mdia box,它是媒体信息 box。它包含了 3 个子 box,一个是 mdhd box,一个是刚才提到的 hdlr box,一个是最重要的 minf box,这个 box 里面包含了 sample 的很多信息,这些信息是找到并正确使用音频和视频数据的关键。
aligned(8) class MediaBox extends Box(‘mdia’) {
}2.5 trak/mdia/mdhd
mdhd(Media Header Box) 作为媒体信息的 header 出现 (注意此 header 不是 box header,而是 media 媒体信息的 header),用于描述一些该 media 的基本信息。mdhd 和 tkhd ,内容大致都是一样的。不过 tkhd 通常是对指定的 track 设定相关属性和内容。而 mdhd 是针对于独立的 media 来设置的。
它的语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
| 字段 | 类型 | 描述 |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | 标识 |
| creation time | 64/32 bit | 创建时间,当 version = 0 时取 32bit |
| modificationtime | 64/32 bit | 修改时间,当 version = 0 时取 32bit |
| timescale | 32 bit | 时间基,同 mvhd 中 timescale |
| duration | 64/32 bit | 本 track 时长,当 version = 0 时取 32bit |
| pad | 1 bit | |
| language | 3x5 bit | 当前 track 的媒体的语言代码。最高位为 0,后面 15 位为 3 个字符(见 ISO 639-2/T 标准中定义) |
| pre defined | 16 bit |
mdhd box 里面最重要的一个值就是时间单位 time scale,这个时间单位是 sample 的时间戳的单位,控制播放速度和音视频同步都需要使用到这个值。
注:timescale 同 mvhd 中的 timescale,但是需要注意虽然意义相同,但是值有可能不同,下边 stts, ctts 等时间戳的计算都是以 mdhd 中的 timescale。
2.6 trak/mdia/hdlr
hdlr box (Handler Reference Box)主要解释了媒体的播放过程信息。声明当前 track 的类型,表明是音频还是视频 track,以及对应的处理器(handler)。
hdlr 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
| 字段 | 类型 | 描述 |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | |
| pre defined | 32 bit | |
| handler type | 32 bit | vide (0x76 69 64 65) video track soun (0x73 6f 75 6e) audio track hint (0x68 69 6e 74) hint track meta (0x6d 65 74 61): Timed Metadata track auxv (0x61 75 78 76) Auxiliary Video track |
| reserved | 3x32 bit | |
| name | 不定长度 | 自定义名称,以 \0 结尾 |
2.7 trak/mdia/minf
minf box(Media Information box) 解释 track 媒体数据的 handler-specific 信息,media handler 用这些信息将媒体时间映射到媒体数据并进行处理。minf 同样是个 container box,其内部需要关注的内容是 stbl box(sample table box),这也是 moov 中最复杂的部分。里面存放着可以计算得到每一个 chunk 的偏移地址、每一个 sample 在文件中的地址信息和大小、每一个 sample 的时间戳和每一个视频 IDR 帧的地址信息。
一般情况下,“minf”包含一个 header box,一个 “dinf” 和一个 “stbl”,其中,header box 根据 track type(即 media handler type)分为“vmhd”、“smhd”、“hmhd” 和“nmhd”,“dinf”为 data information box,“stbl”为 sample table box。
2.7.1 *mhd
*mhd(Media Info Header Box)可分为 “vmhd”、“smhd”、“hmhd” 和“nmhd”,比如视频类型则为 vmhd,音频类型为 smhd。
- vmhd:
graphics mode:视频合成模式,为 0 时拷贝原始图像,否则与 opcolor 进行合成。
opcolor:一组 (red,green,blue),graphics modes 使用。
- smhd
balance:立体声平衡,[8.8] 格式值,一般为 0 表示中间,-1.0 表示全部左声道,1.0 表示全部右声道。
2.7.2 dinf
dinf(Data Information Box)描述了如何定位媒体信息,是一个 container box。“dinf” 一般包含一个 “ dref ”(data reference box)。
“ dref ”下会包含若干个 “url” 或“urn”,这些 box 组成一个表,用来定位 track 数据。简单的说,track 可以被分成若干段,每一段都可以根据 “url” 或“urn”指向的地址来获取数据,sample 描述中会用这些片段的序号将这些片段组成一个完整的 track。一般情况下,当数据被完全包含在文件中时,“url”或 “urn” 中的定位字符串是空的。
2.7.3 stbl
在学习 stbl box 之前,需要先回顾一下 mp4 中定义的 sample 与 chunk:

sample:ISO/IEC 14496-12 中定义 samples 之间不能共享同一个时间戳,因此,在音视频 track 中,一个 sample 代表一个视频或音频帧。
chunk:多个 sample 的集合,实际上音视频 track 中,chunk 与 sample 一一对应。
stbl box 是一个 container box,是整个 track 中最重要的一个 box,其子 box 描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。
其中:
stsd(sample description box):存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关。
stts(time to sample box):存储了该 track 每个 sample 到 dts 的时间映射关系。
stss(sync sample box):针对视频 track,关键帧所属 sample 的序号。
ctts(composition time to sample box):存储了该 track 中,每个 sample 的 cts 与 dts 的时间差。
stsc/stz2(sample to chunk box):存储了该 track 中每个 sample 与 chunk 的映射关系。
stsz(sample size box):存储了该 track 中每个 sample 的字节大小。
stco/co64(chunk offset box):存储了该 track 中每个 chunk 在文件中的偏移。
2.7.4 stbl/stsd
stsd(sample description box)主要存储了编码类型和初始化解码器需要的信息。
aligned(8) abstract class SampleEntry (unsigned int(32) format)
extends Box(format){
const unsigned int(8)[6] reserved = 0;
unsigned int(16) data_reference_index;
}
class BitRateBox extends Box('btrt'){
unsigned int(32) bufferSizeDB;
unsigned int(32) maxBitrate;
unsigned int(32) avgBitrate;
}
aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type)
extends FullBox('stsd', version, 0){
int i ;
unsigned int(32) entry_count;
for (i = 1 ; i <= entry_count ; i++){
SampleEntry(); // an instance of a class derived from SampleEntry
}
}针对不同的 handler_type,SampleDescriptionBox 后续应用不同的 SampleEntry 类型,比如当类型为 video track 时,SampleEntry 为 VisualSampleEntry。
| 字段 | 类型 | 描述 |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| entry count | 32 bit | entry 个数 |
| 开始循环 | ||
| AudioSampleEntry() | 不定大小 | 子 box, 当 handler type ='soun' 时才有 |
| VisualSampleEntry() | 不定大小 | 子 box, 当 handler type ='vide' 时才有 |
| HintSampleEntry() | 不定大小 | 子 box, 当 handler type ='hint' 时才有 |
| MetadataSampleEntry() | 不定大小 | 子 box, 当 handler type ='meta' 时才有 |
| 结束循环 |
这里以视频为例,包含子 box:avc1,表示是 H264 的视频。
这里我们只看一下 avc1 与 avcC,其余 box 可忽略。
- avc1,是 avc/h264/mpeg-4 part 10 视频编解码格式的代称,是一个 container box,但是 box body 也携带自身的信息。
| 字段 | 类型 | 描述 |
| reserved | 6x8 bit | 0 |
| data_reference_index | 16 bit | 可以检索与当前 sampledescription 关联的数据。数据引用存储在 data reference box |
| pre_defined | 16 bit | 0 |
| reserved | 16 bit | 0 |
| pre_defined | 3x32 bit | 0 |
| width | 16 bit | 像素宽度 |
| height | 16 bit | 像素高度 |
| horizresolution | 32 bit | 每英寸的像素值(dpi),[16.16] 格式的数据,固定为 0x00480000,72 dpi |
| vertresolution | 32 bit | 每英寸的像素值(dpi),[16.16] 格式的数据,固定为 0x00480000,72 dpi |
| reserved | 32 bit | 0 |
| frame_count | 16 bit | 每个 sample 中的视频帧数,固定为 1 |
| compressorname | 32 bit | 0 |
| depth | 16 bit | 0x0018, rgb24 位深 |
| pre_defined | 16 bit | -1 |
- avcC(AVC Video Stream Definition Box),存储 sps && pps,即在 ISO/IEC 14496-15 中定义的 AVCDecoderConfigurationRecord 结构。
| 字段 | 类型 | 描述 |
| configurationVersion | 8 bit | 固定 1 |
| AVCProfileIndication | 8 bit | 编码时设置的 profile,例如 base、main、high 等,具体 参见 ISO_IEC_14496-10-AVC-2003.pdf, page 45 |
| profile_compatibility | 8 bit | |
| AVCLevelIndication | 8 bit | 编码时设置的 level |
| reserved | 6 bit | '111111'b |
| lengthSizeMinusOne | 2 bit | |
| reserved | 3 bit | '111′b |
| numOfSequenceParameterSets | 8 bit | sps 个数,一般为 1 |
| sps 循环开始 | ||
| sequenceParameterSetLength | 16 bit | |
| sps | 8*sequenceParameterSetLength | sps |
| sps 循环结束 | ||
| numOfPictureParameterSets | 8 bit | pps 个数,一般为 1 |
| pps 循环开始 | ||
| pictureParameterSetLength | 16 bit | |
| pps | 8*pictureParameterSetLength | pps |
| pps 循环结束 |
2.7.5 stbl/stts
stts(time to sample box)存储了该 track 每个 sample 到 dts 的时间映射关系。包含了一个压缩版本的表,通过这个表可以从解码时间映射到 sample 序号。表中的每一项是连续相同的编码时间增量 (Decode Delta) 的个数和编码时间增量。通过把时间增量累加就可以建立一个完整的 time to sample 表。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | stts |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | |
| entry_count | 32 bit | 条目个数 |
| 开始循环 | ||
| sample_count | 32 bit | 播放时长为 sample_delta 的连续 sample 个数 |
| sample_delta | 32 bit | 单个 sample 的播放时长,单位为 timescale |
| 结束循环 |
2.7.6 stbl/ctts
ctts(composition time to sample box)存储了该 track 中,每个 sample 的 pts 与 dts 时间差 (cts = pts - dts)。如果一个视频只有 I 帧和 P 帧,则 ctts 这个表就不需要了,因为解码顺序和显示顺序是一致的,但是如果视频中存在 B 帧,则需要 ctts。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | ctts |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| entry_count | 32 bit | 条目个数 |
| 开始循环 | ||
| sample_count | 32 bit | cts 为 sample_offset 的连续 sample 个数 |
| sample_offset | signed/unsigned 32 bit | 单个 sample 的 cts, 单位为 timescale。支持负数, 当 version = 1 时为 signed |
| 结束循环 |
2.7.7 stbl/stss
stss(sync sample box)它包含 media 中的关键帧的 sample 表。关键帧是为了支持随机访问。如果此表不存在,说明每一个 sample 都是一个关键帧。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | stss |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| entry_count | 32 bit | 条目个数 |
| 开始循环 | ||
| sample_number | 32 bit | 媒体流中关键帧的 sample 序号 |
| 结束循环 |
2.7.8 stbl/stsc
stsc(sample to chunk box)存储了该 track 中每个 sample 与 chunk 的映射关系。,也就是哪些 sample 属于哪个 chunk。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | stsc |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| entry_count | 32 bit | 条目个数 |
| 开始循环 | ||
| first_chunk | 32 bit | 一组 chunk 的第一个 chunk 的序号,chunk 的编号从 1 开始 |
| samples_per_chunk | 32 bit | 每个 chunk 有多少个 sample |
| sample_description_index | 32 bit | stsd box 中 sample desc 信息的索引 |
| 结束循环 |
2.7.9 stbl/stco、co64
stco(chunk offset box)中存储了每个 chunk 在文件中的位置,这样就可以直接在文件中找到媒体数据,而不用解析 box。需要注意的是一旦前面的 box 有了任何改变,这张表都要重新建立。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | stco/co64 |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| entry_count | 32 bit | Chunk offset 的数目。 |
| 开始循环 | ||
| chunk_offset | 32/64 bit | 这个 chunk 相对于文件开头的偏移。co64 box 为 64bit |
| 结束循环 |
stco 有两种形式,如果视频过大的话,就有可能造成 chunkoffset 超过 32bit 的限制。所以,这里针对大 Video 额外创建了一个 co64 的 Box。它的功效等价于 stco,也是用来表示 sample 在 mdat box 中的位置。只是,里面 chunk_offset 是 64bit 的。
需要注意,这里 stco 只是指定的每个 Chunk 在文件中的偏移位置,并没有给出每个 Sample 在文件中的偏移。想要获得每个 Sample 的偏移位置,需要结合 Sample Size box 和 Sample-To-Chunk 计算后取得。
2.7.10 stbl/stsz
stsz(sample size box)包含 sample 的数量和每个 sample 的字节大小,这个 box 相对来说体积比较大的。表明视频帧或者音频帧大小,FFmpeg 里面的 AVPacket 的 size 数据大小,就是从这个 box 中来的。
| 字段 | 类型 | 描述 |
| box size | 4*8 bit | box 的字节数 |
| box type | 4*8 bit | stsz |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | |
| sample_size | 32 bit | 指定默认的 sample 字节大小,如果所有 sample 的大小不一样,这个字段为 0, 如果所有的 sample 大小相同,那么大小就是这个值 |
| sample_count | 32 bit | track 中 sample 的数量 |
| 开始循环 | ||
| sample_size | 32 bit | 每个 sample 的字节大小 |
| 结束循环 |
3. mdat(media data box)
mdat box 是 MP4 的音视频数据存放的地方。mdat box 基本由头部和数据两部分组成,box type 是 “mdat” 的 ASCII 码值。对于 H264 来说,是一个个 NALU,码流格式使用的是《码流结构》里面的 MP4 格式。这里的 NLAU 不再包含 SPS 和 PPS,这些数据已经放到 moov box 里面了,此处 NALU 类型是图像数据或者 SEI 数据。
mdat 的位置比较灵活,可以位于 moov 之前,也可以位于 moov 之后,但必须和 stbl 中的信息保持一致。它可以引用外部的数据,参见 moov → udta → meta,这里不讨论,只讨论数据存储在本文件中的形式。
mdat 也是一个 box,拥有 box header 和 box body。对于 box body 部分,采用一个一个 samples 的形式进行存储,即一个一个音频帧或视频帧的形式进行存储。没有同步字,没有分隔符,只能根据索引进行访问,也就是说媒体数据 mdat 即便没有 Box Header 也能正常解析。。即:
Box header + Box Data
==>
Box size + Box type + NALU + ... + NALU
NALU = NALU length + NALU Header + NALU Data码流组织方式采用 avcc 格式,即 AUD + slice size + slice 的形式。
AUD(Access Unit Delimiter)访问单元分隔符 用于标识一个访问单元(AU)的起始位置,帮助解码器快速同步视频流。H264 中它的 NALU 类型值为 9,加上起始码的话就是 00 00 00 01 09
参考资料:
封装格式之 MP4 格式简介 — MyNote release 文档
MP4 封装格式—音视频基础知识 · FFmpeg 原理 · FFmpeg 书籍