<> 博客简介:Linux、rtos系统,arm、stm32等芯片,嵌入式高级工程师、面试官、架构师,日常技术干货、个人总结、职场经验分享
<> 公众号:嵌入式技术部落
<> 系列专栏:C/C++、Linux、rtos、嵌入式开发、流媒体、数据结构、网络协议、开源库、CMake、Makefile、架构设计模式等
文章目录
- 一、前言
- 二、mp4 文件格式简介
- 三、mp4 文件三种组织方式
- 四、mp4 文件格式概览
- 五、mp4 box解析
- box的组成
- ftyp (File Type Box)
- mdat (Media Data Box)
- free (Free Space Box)
- moov (Movie Box)
-
- mvhd (Movie Header Box)
- udta (User Data Box)
- trak (Track Box)
-
- tkhd (Track Header Box)
- edts(edit Box)
- mdia (Media Box)
- 六、写在最后
一、前言
本系列系统总结MP4文件格式,从格式详解、MP4分析工具、MP4开源库解析等几篇文章来进行系统性总结和梳理。如有错漏,敬请指出,欢迎随时交流。
MP4官方文档: ISO-IEC-14496-14-2003 & ISO-IEC-14496-14-2020
其他参考文档:Introduction to QuickTime File Format Specification
二、mp4 文件格式简介
mp4文件格式又被称为MPEG-4 Part 14,出自MPEG-4标准第14部分 。它是一种多媒体格式容器,广泛用于包装视频和音频数据流、海报、字幕等。(顺便一提,目前流行的视频编码格式AVC/H264定义在MPEG-4 Part 10)。
MP4是最常见的封装格式之一,因为其跨平台的特性而得到广泛应用。MP4文件的后缀为.mp4,基本上主流的播放器、浏览器都支持MP4格式。
三、mp4 文件三种组织方式
(1) 普通MP4,General MP4,box模式为ftyp-mdat-moov。
(2) 快速MP4,Faststart MP4,box模式为ftyp-moov-mdat。
(3) 切片MP4或fMP4,Fragmented MP4,box模式为ftyp-moov-moof-mdat。
1. 普通 MP4(General MP4)
组织方式: ftyp-mdat-moov
特点: box的顺序是 ftyp(文件类型)在开头,mdat(媒体数据)紧接其后,最后是 moov(元数据)。 在播放视频时,由于 moov box位于文件尾部,需要在文件完全下载后才能开始播放。
适用场景: 适用于本地存储和播放场景,例如在存储于硬盘或 USB 设备上的视频文件。 不适合流媒体或点播服务,因为需要在传输完所有数据后才能解码播放。
2. 快速 MP4(Faststart MP4)

组织方式: ftyp-moov-mdat
特点: box的顺序是 ftyp(文件类型)在开头,紧接其后的是 moov(元数据),最后才是 mdat(媒体数据)。 这种配置方式允许视频播放器在下载文件的初期就可以访问到 moov box,从而提前开始播放,提供更快的启动时间。
适用场景: 适用于需要更快启动时间的场景,如网络流媒体和点播服务。 可以在视频文件尚未完全下载的情况下就开始播放,提升用户体验。
3. 切片 MP4 或 fMP4(Fragmented MP4)
组织方式: ftyp-moov-moof-mdat
特点: 多个 moof 和 mdat 片段存在于文件中,每个片段包括它自己的元数据和媒体数据。 支持动态打包和传输,适合流媒体传输。 允许在各种网络条件下实现更灵活的文件下载和播放。
适用场景: 主要用于自适应流媒体传输。 适合需要频繁切换码率和分辨率的网络环境,例如移动网络。 提供更加平滑和连续的播放体验,适应不同带宽和播放条件。
联系
以上三种组织方式都是基于 MP4容器格式,只是 box 的排列顺序不同,目的是为了适应不同的使用场景。它们都有相同的基本元素(ftyp、moov、mdat),只是添加了特定的box(如moof)来增强某些功能。
总结而言,这三种 MP4 组织方式通过不同的box排列方式和附加功能来优化特定的使用场景,普通MP4适用于本地存储和播放,快速MP4适用于快速启动的网络播放 (边下载边播放),而切片MP4则适用于自适应流媒体传输。
四、mp4 文件格式概览
MP4⽂件的基本组成单元是box,也就是说MP4⽂件是由各种各样的box组成的,这些box之间存在⼀定的层次关系,box之间是树状结构。box中可以包含另一个box,这种box称为container box。可以将box理解为一个数据对象块。如下图所示:
一个mp4文件有可能包含非常多的box,在很大程度上增加了解析的复杂性,这个网页上https://mp4ra.org/registered-types/boxes记录了一些当前注册过的box类型。看到这么多box,如果要全部支持,一个个解析,怕是头都要爆了。还好,大部分mp4文件没有那么多的box类型,下图是一个典型mp4文件的基本结构:
一个mp4文件包含的box类型有很多,下面是3个比较重要的顶层box:
ftyp:File Type Box,描述文件遵从的MP4规范与版本,有且只有一个,一般在文件的开始位置。
moov:Movie Box,媒体数据的metadata信息,有且仅有一个,一般位于ftyp之后,也有的视频moov放在文件末尾。注意,当改变moov位置时,内部一些值需要重新计算。
mdat:Media Data Box,存放实际的媒体数据,可以有多个。
metadata(元数据)是关于数据的数据。换句话说,元数据是描述其他数据的结构化信息。它提供了有关某些数据的信息,这些信息可以包括数据的来源、时间、作者、格式、使用权限等。元数据有助于组织、查找、理解和管理数据。
五、mp4 box解析
box的组成
box由header和body组成。
header:包含了整个box的长度size和类型type。
-
size:整个box长度,占4个字节(32位)。
当size==0时,代表这是文件中最后一个Box,通常包含在mdat box中;
当size==1时,意味着box长度需要更多bits来描述,在后面会定义一个64bits的largesize描述box的长度 (一般只有装载媒体数据的mdat box会用到largesize); -
type:box类型,包括 “预定义类型”、“自定义扩展类型”,占4个字节(32位)。
预定义类型:比如ftyp、moov、mdat等预定义好的类型;
自定义扩展类型:当type是uuid时,代表box中的数据是用户自定义扩展类型,size (或largesize)随后的16字节,为自定义类型的值 (extended_type); -
在部分box header中,还存在version、flags字段,这样的box叫做Full Box。相比Box,FullBox 多了 version、flags 字段。version:当前box的版本,为扩展做准备,占1个字节;flags:标志位,占24位,含义由具体的box自己定义。
body:是box的实际数据,可以是纯数据也可以是更多的子box。
- body根据类型有不同的意义和格式。
- 当box body中嵌套其他box时,这样的box叫做container box。
Box的结构图:
ftyp (File Type Box)
File Type Box,一般在文件的开始位置,描述文件的版本、兼容协议等。
“ftyp” body依次包括1个32位的major brand(4个字符),1个32位的minor version(整数)和1个以32位(4个字符)为单位元素的数组compatible brands。这些都是用来指示文件应用级别的信息。
“ftyp” 结构如下表
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
major brand | 4 | 推荐兼容性的版本 |
minor version | 4 | 最低兼容性的版本 |
compatible brands[] | 4 | 所有的兼容性的版本 |
实例:
mdat (Media Data Box)
存放具体的媒体数据。音视频数据的编解码信息都在其它box中保存,解析的时候先读取其它box的信息获取编解码信息以及音视频数据在mdat中的位置,然后再读取mdat获取音视频数据。
mdat的位置比较灵活,可以位于moov之前,也可以位于moov之后,但必须和stbl中的信息保持一致。
实例:
free (Free Space Box)
free box 是可选的,如果存在,则通常出现在moov与mdat之间。
free中的数据通常为全0,其作用相当于占位符,其内容是无关紧要的,可以被忽略。该box被删除后,不会对播放产生任何影响。
在实时拍摄视频,moov数据增多时,将free分配给moov使用。因为设备录制视频时并不能预先知道视频数据大小,如果moov在mdat之前,随着拍摄mdat的数据会增加,moov数据也会增多,如果没有free预留的空间,则要不停的向后移动mdat数据以腾出moov空间。
实例:
moov (Movie Box)
Movie Box包含了mp4文件媒体数据的metadata信息,“moov”是一个container box,具体内容信息由子box诠释。Movie Box,包含本文件中所有媒体数据的宏观描述信息以及每路媒体轨道的具体信息。"moov"⼀般位于放在⽂件末尾,但如果为了⽀持http边下载边播放则需要将moov提前。注意,当改变moov位置时,内部⼀些值需要重新计算。
一般情况下(限于篇幅,本文只讲解常见的MP4文件结构),“moov"中会包含1个"mvhd"和若干个"trak”。其中"mvhd"为Movie Header Box,记录mp4文件的整体信息,比如创建时间、文件时长等,一般作为“moov”的第一个子box出现(对于其他container box来说,header box都应作为首个子box出现)。“trak”为Track Box,是一个container box,一个mp4可以包含一个或多个轨道(比如视频轨道、音频轨道),轨道相关的信息就在trak里。
实例:
mvhd (Movie Header Box)
Movie Header Box,记录整个mp4媒体文件的描述信息,如创建时间、修改时间、时间度量标尺、可播放时长等。
“mvhd”结构如下表。
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
creation_time | 4 | 创建时间(相对于UTC时间1904-01-01零点的秒数) |
modification_time | 4 | 修改时间 |
timescale | 4 | 表示时间粒度,如果是1000,表示1秒被分成1000个粒度,用于下面的duration计算 |
duration | 4 | 媒体文件的总时长,该值需要除以timescale,转换成真正的秒数。例如:timescale = 1000, duration = 5000 ,则媒体文件的总时长为5秒 |
rate | 4 | 推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16] 格式,该值为1.0(0x00010000)表示正常前向播放 |
volume | 2 | 与rate类似,[8,8]格式,前8位表示整数,后8位表示浮点数,0x0100 = 1.0,最大音量播放 |
reserved | 8 | 保留位 |
matrix | 36 | 视频变换矩阵 |
pre-defined | 24 | |
next_track_id | 4 | 下一个track使用的id号,当在mp4中需要新插入一个track时,这个track的id |
实例:
udta (User Data Box)
用户自定义数据。
trak (Track Box)
"trak"是一个container box,其子box包含了该track (轨道) 的媒体数据引用和描述 (hint track除外)。
一个MP4文件可以包含多个track (比如视频轨道、音频轨道),且至少有一个track,这些track之间彼此独立,有自己的时间和空间信息,track (轨道) 的相关的信息就在trak里。
“trak"必须包含一个"tkhd"和一个"mdia”,此外还有很多可选的box(略)。其中"tkhd"为track header box。“mdia"为media box,该box是一个包含一些track媒体数据信息box的container box。 我们只分析必的"tkhd”,“mdia”,至于其他例如"edts"有兴趣的同学可以自行了解。
track有三种类型:video track、audio track、hint track
video track:描述视频流
audio track:描述音频流
hint track:这个特殊的track并不包含媒体数据,而是包含了一些将其它数据track打包成流媒体的指示信息
tkhd (Track Header Box)
Track Header Box,包含关于媒体流的头信息。
“tkhd”结构如下表。
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
version | 1 | box版本,0或1,一般为0 |
flags | 3 | 按位或操作结果值,预定义如下: 0x000001 track_enabled,否则该track不被播放; 0x000002 track_in_movie,表示该track在播放中被引用; 0x000004 track_in_preview,表示该track在预览时被引用。 一般该值为7,如果一个媒体所有track均未设置track_in_movie和track_in_preview,将被理解为所有track均设置了这两项;对于hint track,该值为0 |
creation_time | 8/4 | 创建时间(相对于UTC时间1904-01-01零点的秒数),当version=0时字节数是4 |
modification_time | 8/4 | 修改时间,当version=0时字节数是4 |
track_id | 4 | id号,不能重复且不能为0 |
reserved | 4 | 保留位 |
duration | 8/4 | track的时间长度,单位采用的是mvhd box下的timescale,,当version=0时字节数是4 |
reserved | 8 | 保留位 |
layer | 2 | 视频层,默认为0,值小的在上层 |
alternate_group | 2 | track分组信息,默认为0表示该track不与其他任何track相关 |
volume | 2 | [8.8] 格式,如果为音频track,1.0(0x0100)表示最大音量;视频track为0[0000] |
reserved | 2 | 保留位 |
matrix | 36 | 视频变换矩阵 |
width | 4 | 视频的宽;音频track为0 |
height | 4 | 视频的高【16.16】的格式存储;音频track为0 |
实例:
edts(edit Box)
不是所有的 MP4文件有这个 Box,子Box为elst(Edit List Box),它的作用是使某个track的时间戳产生偏移。
实例:
mdia (Media Box)
“mdia”也是个container box,其子box的结构和种类还是比较复杂的。先来看一个“mdia”的实例结构树图。
在代表一流音频或视频流的track中,除tkhd外,主要的媒体配置信息在mdia中保存。描述了这条音视频轨/流(trak)的媒体数据样本的主要信息,对播放器来说是一个很重要的box。
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并送给解码器。
mdhd (Media Header Box)
Media Header Box,存放视频流创建时间,⻓度等信息。
“mdhd”结构如下表。
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
creation_time | 4 | 创建时间(相对于UTC时间1904-01-01零点的秒数) |
modification_time | 4 | 修改时间 |
timescale | 4 | 同前表 |
duration | 4 | track的时间长度 |
language | 2 | 媒体语言码。最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义) |
pre-defined | 2 |
实例:
hdlr (Handler Reference Box)
Handler Reference Box,解释了媒体的播放过程信息,包含了 track 的类型信息,主要是有字段handler_type区分。
“hdlr”结构如下表。
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
handler_type | 4 | 在media box中,该值为4个字符: “vide”— video track “soun”— audio track “hint”— hint track |
reserved | 12 | |
name | 不定 | track type name,以‘\0’结尾的字符串 |
实例:
minf (Media Information Box)
minf box 顾名思义,是专门用来描述当前track下媒体数据的box,它是一个container box,其内容由其子box来表达。
一般情况下,minf包含了一个header box,一个dinf和一个stbl,其中header box的数据类型有vmhd(视频轨)、smhd(音轨)、hmhd(hint track)、nmhd(null track),dinf为data information box,stbl为sample table box。
(1) vmhd:Video Media Information Header,定义颜色和图形模式信息,只有video track才有此box
(2) smhd: Sound Media Information Header,定义了声音媒体的控制信息,只有audio track才有此box
(3) stbl:Sample Table,包含了音视频数据的索引以及时间信息,也就是音视频数据的映射
需要注意的是video track和audio track下的一些子box有些不同,看下图对比
vmhd (Video Media Header Box)
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
graphicsmode | 4 | 视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成 |
opcolor | 2×3 | {red,green,blue} |
实例:
smhd (Sound Media Header Box)
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
balance | 2 | 立体声平衡,[8.8] 格式值,一般为0,-1.0表示全部左声道,1.0表示全部右声道 |
reserved | 2 |
实例:
hmhd (Hint Media Header Box)
略
nmhd (Null Media Header Box)
非视音频媒体使用该box,略。
dinf (Data Information Box)
"dinf"用于存储关于数据引用的信息,具体来说,它包含了一个或多个描述如何访问该媒体数据的具体信息。dinf对于运行时获取和解析数据位置信息是至关重要的。dinf是一个container box。dinf 一般包含一个dref,即data reference box。dref下会包含一个或多个url或urn,这些box组成一个表,用来定位track数据。
一个track的媒体数据可以被分成若干段,每一段都可以根据url或urn指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track,一般情况下,当数据被完全包含在文件中时,url或urn中的定位字符串是空的。
“dref”的字节结构如下表。
字段 | 字节数 | 意义 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | box版本,0或1,一般为0。(以下字节数均按version=0) |
entry count | 4 | "url"或"urn"表的元素个数 |
"url"或"urn"列表 | 不定 |
实例:
“url”或“urn”都是box,“url”的内容为字符串(location string),“urn”的内容为一对字符串(name string and location string)。当“url”或“urn”的box flag为1时,字符串均为空。
实例:
stbl (Sample Table Box)
“stbl”几乎是普通的MP4文件中最复杂的一个box了,首先需要回忆一下sample的概念。在mp4文件中,sample是一个媒体流的基本单元,例如视频流的一个sample代表实际的nal数据。chunk是一系列sample数据的集合。在MP4文件中,媒体数据是由一个或多个track组成,而一个track是由一个或多个chunk组成,每个chunk可包含一个或多个sample。如下图所示。
“stbl”包含了关于track中sample所有时间和位置的信息,以及sample的编解码等信息。stbl用来描述每个sample的信息,利用这个表,可以解释sample的时序、类型、大小以及在各自存储容器中的位置。
“stbl”是一个container box,其子box包括:sample description box(stsd)、time to sample box(stts)、sample size box(stsz或stz2)、sample to chunk box(stsc)、chunk offset box(stco或co64)、composition time to sample box(ctts)、sync sample box(stss)等。
下面是这几个box概要的介绍:
- stsd:sample description box,样本的描述信息,存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关。给出视频、音频的编码、宽高、音量等信息,以及每个sample中包含多少个frame。
- stco/co64:chunk offset box,chunk在文件中的偏移;
- stsc:sample to chunk box,sample-chunk映射表,每个chunk中包含几个sample;
- stsz/stz2:sample size box,每个sample的size(单位是字节);
- stts:time to sample box,存储了该 track 每个 sample 到 dts 的时间映射关系。
- stss:sync sample box,针对视频,关键帧的序号;
- ctts:composition time to sample box,sample的CTS与DTS的时间差的压缩表;
stsd (Sample Description Box)
Sample Description Box,存储了编码类型和初始化解码器需要的信息,视频的编码类型、宽高、长度,音频的声道、采样等信息都会出现在这个box中。
stsd的内容和track的类型有关,也就是和hdlr的handler_type参数有关。
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | 取 0 或 1,一般取 0 |
entry_count | 4 | entry 个数 |
开始循环 | ||
AudioSampleEntry() | 不定大小 | 子 box,当 handler_type=‘soun’ 时才有 |
VisualSampleEntry() | 不定大小 | 子 box,当 handler_type=‘vide’ 时才有 |
HintSampleEntry() | 不定大小 | 子 box,当 handler_type=‘hint’ 时才有 |
MetadataSampleEntry() | 不定大小 | 子 box,当 handler_type=‘meta’ 时才有 |
结束循环 |
version字段后会有一个entry count字段,根据entry的个数,每个entry会有type信息,如“vide”、“sund”等,根据type不同sample description会提供不同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track会有“AudioSampleEntry”类型信息。
video track的stsd body内容(不同的编码方式,stsd的子box的名称会有不同,但是box中的字段都是相同的):
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型”。预定义类型:比如ftyp、moov、mdat等预定义好的类型;自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
data_reference_index | 2 | 当MP4文件的数据部分,可以被分割成多个片段,每一段对应一个索引,并分别通过URL地址来获取,此时,data_reference_index 指向对应的片段(比较少用到) |
width | 2 | 视频的宽,单位是像素 |
height | 2 | 视频的高,单位是像素 |
horizresolution | 4 | 水平方向的分辨率(像素/英寸),默认是0x00480000(72dpi) |
vertresolution | 4 | 垂直方向的分辨率(像素/英寸),默认是0x00480000(72dpi) |
frame_count | 2 | 一个sample中包含多少个frame,对video track来说,默认是1 |
compressorname | 32 | 仅供参考的名字,通常用于展示,占32个字节,比如 AVC Coding。第一个字节,表示这个名字实际要占用N个字节的长度。第2到第N+1个字节,存储这个名字。第N+2到32个字节为填充字节。compressorname 可以设置为0 |
depth | 2 | 位图的深度信息,比如 0x0018(24),表示不带alpha通道的图片 |
实例:
audio track的stsd body内容(不同的编码方式,stsd的子box的名称会有不同,但是box种的字段都是相同的):
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型”。预定义类型:比如ftyp、moov、mdat等预定义好的类型;自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
data_reference_index | 2 | 当MP4文件的数据部分,可以被分割成多个片段,每一段对应一个索引,并分别通过URL地址来获取,此时,data_reference_index 指向对应的片段(比较少用到) |
channel_count | 声道数,取值为1或2 | |
samplesize | 采样位宽,一般为8bit或16bit | |
samplerate | 采样率 |
实例:
stts (Time To Sample Box)
“stts”记录着该sample到下一个sample的DTS间隔,存储了sample的duration,包含了DTS到sample number的映射表,主要用来推导每个帧的时长,描述了sample时序的映射方法,我们通过它可 以找到任何时间的sample。
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | 取 0 或 1,一般取 0 |
entry_count | 4 | 条目个数 |
开始循环 | ||
sample_count | 4 | 单个entry中,具有相同时长(duration 或 sample_delta)的连续sample的个数 |
sample_delta | 4 | 单个 sample 的播放时长,单位为 timescale,也可以说是相邻两个sample之间dts的差值 |
结束循环 |
实例:
上图中,从第一个sample开始,到第908个sample,他们的DTS间隔都是3000
上图中,第一个sample的持续时间是4497,第二个sample的持续时间是4499,第三第四个sample的持续时间是4498,第五个sample的持续时间是4500。
stss (Sync Sample Box)
"stss"是视频track独有,它包含关键帧所在的 sample 序号。
关键帧是为了支持随机访问。如果没有stss的话,所有的sample中都是关键帧。
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | 取 0 或 1,一般取 0 |
entry_count | 4 | 条目个数 |
开始循环 | ||
sample_number | 4 | sample 计数,从 1 开始 |
结束循环 |
实例:
上图中,第1帧、第91帧、第181帧…,是关键帧,以此类推。
ctts(Composition Time to Sample Box)
存储了每个 sample 从解码(dts)到渲染(pts)之间的差值。
对于只有I帧、P帧的视频来说,解码顺序、渲染顺序是一致的,此时,ctts没必要存在。
对于存在B帧的视频来说,ctts就需要存在了。当PTS、DTS不相等时,就需要ctts了,公式为 CT(n) = DT(n) + CTTS(n) 。
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
version | 1 | 取 0 或 1,一般取 0 |
flags | 3 | |
sample_counts | 4 | 单个entry中,具有相同差值(pts-dts)的连续sample的个数 |
sample_offsets | 4 | 从解码(dts)到渲染(pts)之间的差值 |
实例:
上图中,第1个sample的pts-dts=15000,第2个sample的pts-dts=60000,第3个sample的pts-dts=33000,以此类推。
stsc (Sample To Chunk Box)
stsc中用一个表描述sample与chunk映射表,每个chunk中包含几个sample,查看这张表就可以找到包含指定sample的chunk,从而找到这个sample。
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | |
version | 1 | 取 0 或 1,一般取 0 |
entry_count | 4 | 表项个数 |
first_chunk | 4 | 当前表项中,对应的第一个chunk的序号 |
samples_per_chunk | 4 | 同一个entry中每个chunk包含的sample数 |
sample_description_index | 4 | chunk使用的stsd的序号,即不同chunk可以使用不同编解码信息 |
实例:
上图中,entry_conut是4个,第1个entry的第一个chunk的序号是1,第2个entry的第一个chunk的序号是2(即第1个entry包含索引值为1的chunk),第3个entry的第一个chunk的序号为6,以此类推。
序号1的chunk,包含6个sample。序号2的chunk,包含1个sample。序号3~6的chunk,每个chunk包含2个sample。序号7的chunk,包含1个sample。
以上所有chunk中的sample,对应的sample description的序号都是1,也就是用第一个stsd。
stsz/stz2 (Sample Size Box)
“stsz” 定义了每个sample的大小,包含了媒体中全部sample的数目和一张给出每个sample大小的表。这个box相对来说体积是比较大的。
有两种不同的box类型:stsz、stz2
stsz结构如下:
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | 无实际意义(官方文档也没有介绍) |
version | 1 | 当前box版本,为扩展做准备,取 0 或 1,一般取 0 |
sample_size | 4 | 默认的sample大小(单位是byte),通常为0。如果sample_size不为0,那么,所有的sample都是同样的大小。如果sample_size为0,那么,sample的大小可能不一样 |
sample_count | 4 | 当前track里面的sample数目。如果 sample_size==0,那么,sample_count 等于下面entry_size(sample_sizes)中的数据个数 |
entry_size (sample_sizes) | 4 | 单个sample的大小 (如果sample_size==0的话) |
实例:
上图中,第1个sample的大小是79369,第2个sample的大小是11103,以此类推。
stz2的结构如下:
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | 无实际意义(官方文档也没有介绍) |
version | 1 | 当前box版本,为扩展做准备,取 0 或 1,一般取 0 |
field_size | 1 | entry表中,每个entry_size占据的位数(bit),可选的值为4、8、16。4比较特殊,当field_size等于4时,一个字节上包含两个entry,高4位为entry[i],低4位为entry[i+1] |
sample_count | 4 | 等于下面entry_size中的数据个数 |
entry_size | 4 | 单个sample的大小 |
stco/co64 (Chunk Offset Box)
“stco”定义了每个chunk在媒体流中的偏移量(offsets)。这个位置是在整个文件中的,而不是在任何box中的,这样做就可以直接在文件中找到媒体数据,而不用解析box。需要注意的是一旦前面的box有了任何改变,这张表都要重新建立,因为位置信息已经改变了。
针对小文件、大文件,有两种不同的box类型,分别是stco (32位)、co64 (64位),它们的结构是一样的,只是字段长度不同。
在构建mp4文件的时候,需要特别注意 moov 所处的位置,它对于chunk_offsets 的值是有影响的。有一些MP4文件的 moov 在文件末尾,为了优化首帧速度,需要将 moov 移到文件前面,此时,需要对 chunk_offsets 进行改写。
结构如下:
字段 | 字节数 | 描述 |
---|---|---|
size | 4 | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
type | 4 | box类型,包括 “预定义类型”、“自定义扩展类型” 预定义类型:比如ftyp、moov、mdat等预定义好的类型; 自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
flags | 3 | 无实际意义(官方文档也没有介绍) |
version | 1 | 当前box版本,为扩展做准备,取 0 或 1,一般取 0 |
chunk_offsets | 4 | 在文件本身中的 offset,而不是某个box内部的偏移 |
实例:
上图中,第1个chunk的offsets是35377,第2个chunk的offsets是131537,以此类推。
六、写在最后
关于MP4文件格式详解总结完毕,如有问题,也欢迎随时交流。该系列下一篇文章,将分享几个MP4分析工具,欢迎关注交流。