Skip to content

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 放在文件的最开始,指示文件的相关信息。伪代码如下:

c
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 示例 ​

image-20251216151128679

这里是一个 ftyp box 的数据,可以看到大小为 0x18,也就是 24 字节,头占了 8 个字节,数据占了 16 字节。但是这里好像没有显示全,完整的数据应该是下面的部分:

image-20251216151927108

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 的伪代码如下:

c
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
mvhd box 中,最主要的功能是通过 Time scale 和 Duration 可计算总时长,并且建议 Time scale 的值可以参考采样率,便于计算不容易出现小数。**`mvhd` 的 timescale** 通常取一个 **能整除所有轨道 timescale** 的值,常见值有 **1000**(毫秒级精度)、600、90000 等,这样方便用整数表示电影总时长。
2.1.2 示例 ​

我们来找一些这个 duration,自己不想计算的话,可以换个工具,比如 MP4 Reader、AtomicBrowser 等工具,都可以直接解析出每部分的含义,这里使用 MP4 Reader 打开,因为 AtomicBrowser 没找到下载链接。

image-20251216161402603

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

image-20260522140136189

可以看到大概就是 46s。

2.2 trak ​

image-20251216154441744

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) 规范中,一个 trak box 必须包含 一个 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。
##### 2.3.2 示例
  • 视频 tkhd,从这里可以看到这是一个 960x400 的视频

image-20260522141446938

  • 音频 tkhd

image-20260522141453858

2.4 trak/mdia ​

trak box 还有一个 mdia box,它是媒体信息 box,定义了 track 的媒体类型以及 sample 数据,是一个 container box。它包含了 3 个子 box,一个是 mdhd box,一个是刚才提到的 hdlr box,一个是最重要的 minf box,这个 box 里面包含了 sample 的很多信息,这些信息是找到并正确使用音频和视频数据的关键。

c
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。理论上应该满足:

text
mvhd.duration / mvhd.timescale ≈ mdhd.duration / mdhd.timescale

即 换算成秒后,两者的时长应该相等(允许微小的舍入误差)。

特性mvhd(Movie Header Box)mdhd(Media Header Box)
作用范围整个 MP4 文件(Movie)单个轨道(Track)内的媒体数据
所属层级moov 的子 boxmdia 的子 box
timescale 含义整个电影的全局时间刻度该轨道媒体的时间刻度
duration 含义整个电影的时长该轨道媒体的时长
2.5.2 示例 ​

image-20260522140424448

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

image-20260522141105581

可以看到这个里面 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.6.2 示例
  • 视频

image-20260522141723762

  • 音频

image-20260522141732248

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 帧的地址信息。

image-20251216194049614

一般情况下,“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)。

image-20251216194236099

“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 表的元素个数
“url”或“urn”都是 box,“url”的内容为字符串(location string),“urn”的内容为一对字符串(name string and location string)。这里就不展开“url”或“urn”的 box 格式,只需要记住“url”或“urn”的 box flag 为 1 时,字符串均为空。

image-20260522143036773

2.7.3 stbl ​

在学习 stbl box 之前,需要先回顾一下 mp4 中定义的 sample 与 chunk:

image-20251216200717378

sample:ISO/IEC 14496-12 中定义 samples 之间不能共享同一个时间戳,因此,在音视频 track 中,一个 sample 代表一个视频或音频帧。

chunk:多个 sample 的集合,实际上音视频 track 中,chunk 与 sample 一一对应。

stbl box 是一个 container box,是整个 track 中最重要的一个 box,其子 box 描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。

image-20251216200824216

其中:

  • 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。

c
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 的视频。

image-20251216201248106

这里我们只看一下 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

image-20260522150641965

  • 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 循环结束

image-20260522151240136

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
结束循环
![image-20260522152111435](LV085-MP4常见Box/img/image-20260522152111435.png)

这里的 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 的历史原因。

帧率timescaledelta说明
23.976fps240001001电影/NTSC
29.97fps300001001电视/NTSC
59.94fps600001001高帧率/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 序号
结束循环
![image-20260522154228934](LV085-MP4常见Box/img/image-20260522154228934.png)

对于,正常严格 25fps 的视频,都是应该 I 帧间隔是 25,例如:

image-20260522154425220

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 信息的索引
结束循环
用 chunk 组织 sample 可以方便优化数据获取,一个 chunk 包含一个或多个 sample。“stsc”中用一个表描述了 sample 与 chunk 的映射关系,查看这张表就可以找到包含指定 sample 的 chunk,从而找到这个 sample。最简单的方式就是一个 sample 一个 chunk。

image-20260522151605085

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 计算后取得。

image-20260522154819714

注意: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 的字节大小
结束循环
![image-20260522153128114](LV085-MP4常见Box/img/image-20260522153128114.png)

这里会有 1116 个 sample:

image-20260522153200837

因为每个 sample 代表一个视频帧或者音频帧,他们的大小肯定是不同的,所以这里默认的 Sample size 为 0,后面每一个 sample 都会有自己的 sample size。

3. mdat(media data box) ​

text
[Box Size][Box Type (mdat)][Media Data]
  4Bytes       4Bytes         NBytes

mdat 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 也能正常解析。即:

md
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 封装格式 - 沉默的思想 - 博客园

(5 封私信) 视频封装格式:MP4 格式详解 - 知乎

封装格式之 MP4 格式简介 — MyNote release 文档

MP4 封装格式—音视频基础知识 · FFmpeg 原理 · FFmpeg 书籍

MP4 封装格式详解_mp4 封装-CSDN 博客

MP4 文件结构解析_在线解析 mp4 box-CSDN 博客

视频封装格式:MP4_格式详解

MP4 文件格式详解——元数据 moov(一)mvhd box_mp4 moov-CSDN 博客