LV085-MP4常见Box
说明:本节都是基于 oceans.mp4 这个视频来分析的。
一、常见 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。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | 标识,一般为 0 |
| creation time | 64/32 bit | 创建时间(从 UTC 时间的 1904 年 1 月 1 日 0 点至今的秒数)。当 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 没找到下载链接。

从这里可以看到 duration 是 27968,Time scale 是 600,所以总时长(秒)= duration / timescale = 27968/600 = 46.13s, 我们可以找到这个视频看一下:oceans.mp4

可以看到大概就是 46s。
2.2 trak
track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。具体是音频 trak 还是视频 trak,会在 trak box 中的 mdia box 里面的 hdlr box 中表示出来。
它是一个容器 Box(container box),本身不包含任何内容,其含义由内部的 Box 定义。媒体文件中可能包含多条 Track,每一条 Track 是独立的,都有自己的时间和空间信息,比如音频和视频可以单独分为两个 Track 存放,而单个视频流也可以分为多个 Track 存放。
Tips:
(1)一个 MP4 文件中的媒体可以包含多个 track,且至少有一个 track,这些 track 之间彼此独立,有自己的时间和空间信息。
(2) ISO Base Media File Format (ISOBMFF) 规范中,一个
trakbox 必须包含 一个tkhd(Track Header Box)和一个mdia(Media Box)。此外还有很多可选的 box
2.3 trak/tkhd
2.3.1 box 说明
tkhd box(track header box) 作为媒体信息的 header 出现 (注意此 header 不是 box header,而是 track 媒体信息的 header),用于描述一些该 track 的基本信息。比如说视频的宽高信息和音频的音量信息等。一个 Track 只有一个 tkhd。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| version | 8 bit | 取 0 或 1, 一般取 0 |
| flags | 24 bit | 标识,一般是 7 |
| creation time | 64/32 bit | 创建时间(相对于 UTC 时间 1904-01-01 零点的秒数), 当 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。 |
- 视频 tkhd,从这里可以看到这是一个 960x400 的视频

- 音频 tkhd

2.4 trak/mdia
trak box 还有一个 mdia box,它是媒体信息 box,定义了 track 的媒体类型以及 sample 数据,是一个 container box。它包含了 3 个子 box,一个是 mdhd box,一个是刚才提到的 hdlr box,一个是最重要的 minf box,这个 box 里面包含了 sample 的很多信息,这些信息是找到并正确使用音频和视频数据的关键。
aligned(8) class MediaBox extends Box(‘mdia’) {
}2.5 trak/mdia/mdhd
2.5.1 box 说明
mdhd(Media Header Box) 作为媒体信息的 header 出现 (注意此 header 不是 box header,而是 media 媒体信息的 header),用于描述一些该 media 的基本信息。mdhd 和 tkhd ,内容大致都是一样的。不过 tkhd 通常是对指定的 track 设定相关属性和内容。而 mdhd 是针对于独立的 media 来设置的。
它的语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box类型 |
| 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。即文件媒体在1秒时间内的刻度值,可以理解为1s长度的时间单元数 |
| duration | 64/32 bit | 本 track 时间长度,当 version = 0 时取 32bit,通过duration和timescale可以计算track时长 |
| pad | 1 bit | |
| language | 3x5 bit | 当前 track 的媒体的语言代码。最高位为 0,后面 15 位为 3 个字符(见 ISO 639-2/T 标准中定义) |
| pre defined | 16 bit | 预定义 |
mdhd box 里面最重要的一个值就是时间单位 time scale,这个时间单位是 sample 的时间戳的单位,控制播放速度和音视频同步都需要使用到这个值。不同 trak 下的 mdhd 的 Time scale 可以不同,即视频 / 音频时间刻度值可以不同。视频和音频的总长度可以不同。mdhd 的 timescale 通常取该轨道的 自然采样率:
- 视频轨道:常见 12288、90000 等
- 音频轨道:常见 44100、48000(即采样率)
注:timescale 同 mvhd 中的 timescale,但是需要注意虽然意义相同,但是值有可能不同,下边 stts, ctts 等时间戳的计算都是以 mdhd 中的 timescale。理论上应该满足:
textmvhd.duration / mvhd.timescale ≈ mdhd.duration / mdhd.timescale即 换算成秒后,两者的时长应该相等(允许微小的舍入误差)。
特性 mvhd(Movie Header Box)mdhd(Media Header Box)作用范围 整个 MP4 文件(Movie) 单个轨道(Track)内的媒体数据 所属层级 moov的子 boxmdia的子 boxtimescale 含义 整个电影的全局时间刻度 该轨道媒体的时间刻度 duration 含义 整个电影的时长 该轨道媒体的时长
2.5.2 示例

可以看到这个里面 Duration 是 1117116,Time scale 是 24000,这里也可以得到总时长 = 1117116/24000≈46.54.

可以看到这个里面 Duration 是 2237440,Time scale 是 48000,这里也可以得到总时长 = 2237440/48000≈46.61.
2.6 trak/mdia/hdlr
2.6.1 box 说明
hdlr box (Handler Reference Box)主要解释了媒体的播放过程信息。声明当前 track 的类型,表明是音频还是视频 track,以及对应的处理器(handler)。
hdlr 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| 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:
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型,vmhd |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | box 标志,一般是 0 |
| graphics mode | 32 bit | 视频合成模式,为 0 时拷贝原始图像,否则与 opcolor 进行合成。 |
| opcolor | 3*16 bit | 一组 (red,green,blue),graphics modes 使用。 |
- smhd
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型,vmhd |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | box 标志,一般是 0 |
| balance | 32 bit | 立体声平衡,[8.8] 格式值,一般为 0 表示中间,-1.0 表示全部左声道,1.0 表示全部右声道。 |
| reserved | 16 bit | 保留位 |
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” 中的定位字符串是空的。简单了解下 dref(Data Reference Box):
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型,vmhd |
| version | 8 bit | 取 0 或 1,一般取 0 |
| flags | 24 bit | box 标志,一般是 0 |
| Entry count | 32 bit | 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 的序号,用于确认哪个sample是media中的关键帧。
- ctts(composition time to sample box):存储了该 track 中,每个 sample 的 cts 与 dts 的时间差。
- stsc/stz2(sample to chunk box):存储了该 track 中每个 sample 与 chunk 的映射关系,每个chunk可能包含多个sample。
- stsz(sample size box):存储了该 track 中每个 sample 的字节大小。
- stco/co64(chunk offset box):存储了该 track 中每个 chunk 在文件中的偏移,这个偏移是相对于文件0字节的绝对偏移。
Tips:stbl box 包含了关于 track 中 sample 所有时间和位置的信息,以及 sample 的编解码等信息。利用这个表,可以解释 sample 的时序、类型、大小以及在各自存储容器中的位置。
2.7.4 stbl/stsd
stsd(sample description box)主要存储了编码类型和初始化解码器需要的信息。stsd box 本身的信息主要是 entry_count(条目数),目前常见为 1。stsd box 包含的 box 记录媒体的编码信息,视频的编码类型、宽高、长度,音频的声道、采样等信息都会出现在这个 box 中,且各自名称格式不同,如 H.264 编码的话将包含 avc1 box, H.265 编码包含则包含 hvc1 box。这些 box 可能还会包含 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。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| 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 也携带自身的信息。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| 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 | 压缩名称 |
| depth | 16 bit | 0x0018, rgb24 位深 |
| pre_defined | 16 bit | -1 |

- avcC(AVC Video Stream Definition Box),存储 sps && pps,即在 ISO/IEC 14496-15 中定义的 AVCDecoderConfigurationRecord 结构。
| 字段 | 类型 | 描述 |
| Box Size | 32 bit | box 长度 |
| Box Type | 32 bit | box 类型 |
| 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 长度 |
| sps | 8*sequenceParameterSetLength | sps 内容 |
| sps 循环结束 | ||
| numOfPictureParameterSets | 8 bit | pps 个数,一般为 1 |
| pps 循环开始 | ||
| pictureParameterSetLength | 16 bit | pps 长度 |
| pps 内容 | 8*pictureParameterSetLength | pps |
| pps 循环结束 | ||

2.7.5 stbl/stts
stts(time to sample box)存储了该 track 每个 sample 到 dts 的时间映射关系,我们通过它可以找到任何时间的 sample。它可以包含一个压缩版本的表,通过这个表可以从解码时间映射到 sample 序号,用其他的表来提供每个 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 |
| 结束循环 |
这里的 entry_count 为 1,这表示 视频中所有帧的持续时间都相同,即这是一个 恒定帧率(CFR) 的视频。这里就有 1116 个帧的播放时长相同,从前面知道 trak/mdia/mdhd 的 Time scale 是 24000,那么总时长 = sample_count x sample_delta / mdhd_TimeScale = 1116x1101/24000 ≈46.54s
Tips:为什么 sample_delta 是 1001?这是 NTSC 制式遗留下来的标准。29.97fps 实际上是 30000/1001 ≈ 29.97003 fps,而不是精确的 30fps。
- 精确 30fps:
delta = 1000(timescale = 30000 时)- 29.97fps:
delta = 1001(timescale = 30000 时)这就是所谓的 "drop frame" 时间基准,源自模拟电视时代为了兼容彩色信号而将帧率从 30fps 微调为 29.97fps 的历史原因。
帧率 timescale delta 说明 23.976fps 24000 1001 电影/NTSC 29.97fps 30000 1001 电视/NTSC 59.94fps 60000 1001 高帧率/NTSC
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 表。关键帧是为了支持随机访问。对于压缩媒体数据,关键帧是一系列压缩序列的开始帧,其解压缩时不依赖以前的帧,而后续帧的解压缩将依赖于这个关键帧。“stss”可以非常紧凑的标记媒体内的随机存取点,它包含一个 sample 序号表,表内的每一项严格按照 sample 的序号排列,说明了媒体中的哪一个 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 序号 |
| 结束循环 |
对于,正常严格 25fps 的视频,都是应该 I 帧间隔是 25,例如:

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 在文件中的偏移位置(相对于整个 MP4 文件的起始位置,即**文件开头(字节 0)**开始计算的),并没有给出每个 Sample 在文件中的偏移。想要获得每个 Sample 的偏移位置,需要结合 Sample Size box 和 Sample-To-Chunk 计算后取得。

注意:
stco的偏移量是文件绝对偏移,与moov和mdat的先后顺序无关。
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 的字节大小 |
| 结束循环 |
这里会有 1116 个 sample:
因为每个 sample 代表一个视频帧或者音频帧,他们的大小肯定是不同的,所以这里默认的 Sample size 为 0,后面每一个 sample 都会有自己的 sample size。
3. mdat(media data box)
[Box Size][Box Type (mdat)][Media Data]
4Bytes 4Bytes NBytesmdat box 是 MP4 的音视频数据存放的地方。mdat box 基本由头部和数据两部分组成,box type 是 “mdat” 的 ASCII 码值。对于 H264 来说,是一个个 NALU,码流格式使用的是《码流结构》里面的 MP4 格式。这里的 NLAU 不再包含 SPS 和 PPS,这些数据已经放到 moov box 里面了,此处 NALU 类型是图像数据或者 SEI 数据。
mdat 的位置比较灵活,可以位于 moov 之前,也可以位于 moov 之后(有些播放器可能不支持 mdat 在 moov 后面,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 的形式。
也就是说,mdat 数据中,对于 H264/H265 的编码格式,会先把一帧数据的前 4 个字节(0x00000001, 也就是起始码)替换成此帧的帧长,在读取的时候需要进行还原处理。
AUD(Access Unit Delimiter)访问单元分隔符 用于标识一个访问单元(AU)的起始位置,帮助解码器快速同步视频流。H264 中它的 NALU 类型值为 9,加上起始码的话就是 00 00 00 01 09
二、3 个问题
问题 1:录制一段 mp4,未知要保存的时间,没有任何限制,如何写一个 mp4 文件开销少(少拷贝,少占用内存)?
问题 2:基于问题 1,考虑到部分播放器兼容性,必须符合 moov 在前 mdat 在后,如何写一个 mp4 文件开销小?
问题 3:基于问题 2,如果大概知道要保存的时间最大值,如何写一个 mp4 文件开销小?
参考资料:
封装格式之 MP4 格式简介 — MyNote release 文档
MP4 封装格式—音视频基础知识 · FFmpeg 原理 · FFmpeg 书籍
