MP4文件格式解析之结合实例分析

文章目录


一、前言

之前一直接触MP4格式的文件,但一直没有静下心来仔细研究,最近有时间,总结记录一下,顺便也查漏补缺一下。我们首先清楚的是,MP4 也称为 MPEG-4 第 14 部分,是继承 MPEG-4 第 12 部分的 ISO 基础媒体文件格式并略作扩展而来,定义于标准 ISO/IEC 14496-14 中,是一种标准的数字多媒体容器格式,广泛用于包装视频和音频数据流、海报、字幕和元数据等。目前流行的视频编码格式 AVC/H264 定义在 MPEG-4 Part 10。


二、MP4格式概览

1、基本结构

MP4 文件的数据都是封装在一个又一个名为 Box 的单元中。MP4 中最基本的单元就是 Box,它内部是通过一个一个独立的 box 拼接而成的。每个 box 分为 Header 和 Data。其中 Header 部分包含了 box 的类型和大小,Data 包含了子 box 或者数据。一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。


2、整体结构

下图是一个典型 MP4 文件的基本结构:

Box 有不同的类型,有着不同的数据结构,Box 中还可以包含其他 Box。Box 的类型详见下表(其中 * 表示当父 Box 存在时,则必须包含该 Box):


三、结合实例

先贴一些张我们要分析的MP4文件的mediainfo图:

再来一张整体结构图:

 注意此文件的层次结构,方便后续分析理解。


1、File Type Box(ftyp)

File Type Box,一般在文件的开始位置,描述的文件的版本、兼容协议等。该box有且只有1个,并且只能被包含在文件层,而不能被其他box包含。ftyp 相当于mp4 的纲领性说明。即,告诉demuxer它的基本解码版本,兼容格式。简而言之,就是用来告诉客户端,该 MP4 的使用的解码标准。所以,ftyp 都是放在 MP4 的开头。

它的格式为:

aligned(8) class FileTypeBox
   extends Box(‘ftyp’) {
   unsigned int(32)  major_brand;
   unsigned int(32)  minor_version;
   unsigned int(32) compatible_brands[];
}

上面的字段一律都是放在 data 字段中(参考,box 的描述)。

  • major_brand: 因为兼容性一般可以分为推荐兼容性和默认兼容性。这里 major_brand 就相当于是推荐兼容性。一般而言都是使用 isom 这个万金油即可。如果是需要特定的格式,可以自行定义。
  • minor_version: 指最低兼容版本。
  • compatible_brands: 和 major_brand 类似,通常是针对 MP4 中包含的额外格式,比如,AVC,AAC 等相当于的音视频解码格式。

 按照上文所述,每个box的header前 4 个字节 00 00 00 20 表示该 Box 的 size,接着 4 个字节 66 74 79 70 是该 Box 的 type,即 ftyp。接下来 4 个字节 69 73 6F 6D 是主 brand,表示该文件所遵循的标准规格,这里是 isom,即遵循 ISO Base Media File Format。接下来的 4 个字节 00 00 02 00 表示的是这个 Box 格式的版本号。接下来的 16 个字节则是兼容的 compatible brands,即该文件兼容的其他标准规格,这里是 isomiso2avc1mp41


2、Movie Box(moov)

该部分包含了文件媒体的metadata信息,“moov”是一个contanier box,具体内容由其子box诠释。同ftyp box一样该box有且只有一个,且只包含在文件层,一般情况下moov会跟着ftyp出现。moov Box 可以说是 MP4 文件中最重要的 Box,一般播放器的实现都需要读取到 moov 的数据才能开始播放流程。

  一 般情况下,“moov”中会包含1个“mvhd”和若干个“trak”。其中“mvhd”为header box,一般作为“moov”的第一个子box出现(对于其他container box来说,header box都应作为首个子box出现)。“trak”包含了一个track的相关信息,是一个container box。

2.1 Movie Header Box(mvhd)

Movie Header Box,记录整个媒体文件的描述信息,如创建时间、修改时间、时间度量标尺、可播放时长等。

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
creation time 4 创建时间(相对于UTC时间1904-01-01零点的秒数)
modification time 4 修改时间
time scale 4 文件媒体在1秒时间内的刻度值,可以理解为1秒长度的时间单元数
duration 4 该 track的时间长度,用duration和time scale值可以计算track时长,比如audio track的time scale = 8000, duration = 560128,时长为70.016,video track的time scale = 600, duration = 42000,时长为70
rate 4 推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16] 格式,该值为1.0(0x00010000)表示正常前向播放
volume 2 与rate类似,[8.8] 格式,1.0(0x0100)表示最大音量
reserved 10 保留位
matrix 36 表示视频的图形变换矩阵数据。这里的默认值是 { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }
pre-defined 24
next track id 4 下一个track使用的id号

下图就表示了,当前分析的MP4文件的时长为130838ms 


3、Video/AudioTrack Box

虽然track是在moov下,但由于它比较重要,且包含内容较多,所以单独分出来介绍。

“trak” 其实也是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外,hint track 包含用于流媒体协议的打包信息)。一个MP4文件中的媒体可以包含多个track,且至少有一个track,这些track之间彼此独立,有自己的时间和空间信息。 “trak”必须包含一个“tkhd”和一个“mdia”,此外还有很多可选的box(略)。其中“tkhd”为track header box,“mdia”为media box,该box是一个包含一些track媒体数据信息box的container box。


3.1 Track Header Box(tkhd)

tkhd,即 Track Header Box,包含该轨道的创建时间、标识该轨道 的 ID、轨道的播放时长、音量、宽高等信息。“tkhd”结构如下表:

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=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 4 创建时间(相对于UTC时间1904-01-01零点的秒数)
modification time 4 修改时间
track id 4 id号,不能重复且不能为0
reserved 4 保留位
duration 4 track的时间长度
reserved 8 保留位
layer 2 视频层,默认为0,值小的在上层
alternate group 2 track分组信息,默认为0表示该track未与其他track有群组关系
volume 2 [8.8] 格式,如果为音频track,1.0(0x0100)表示最大音量;否则为0
reserved 2 保留位
matrix 36 视频变换矩阵
width 4
height 4 高,均为 [16.16] 格式值,与sample描述中的实际画面大小比值,用于播放时的展示宽高

下面的图中就标识出了当前的track的时长是130806ms,宽640,高360。


3.2 Media Box

 “mdia”也是个container box,其子box的结构和种类还是比较复杂的。先来看一个“mdia”的实例结构树图。

 总 体来说,“mdia”定义了track媒体类型以及sample数据,描述sample信息。一般“mdia”包含一个“mdhd”,一个“hdlr”和 一个“minf”,其中“mdhd”为media header box,“hdlr”为handler reference box,“minf”为media information box。下面依次看一下这几个box的结构。


3.2.1 media header box (mdhd)

mdhd:Media Header Box,存放视频流创建时间,长度等信息。

“mdhd”结构如下表:

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
creation time 4 创建时间(相对于UTC时间1904-01-01零点的秒数)
modification time 4 修改时间
time scale 4 同前表
duration 4 track的时间长度
language 2 媒体语言码。最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义)
pre-defined 2

 实例信息如下:


3.2.2 handler reference box(hdlr)

 hdlr表示了该 track 数据的处理方式,对应的类型包括:Video Track、Audio Track 或者 Hint Track。该box也可以被包含在meta box(meta)中。“hdlr”结构如下表。

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
pre-defined 4
handler type 4 在media box中,该值为4个字符:“vide”— video track“soun”— audio track“hint”— hint track
reserved 12
name 不定 track type name,以‘\0’结尾的字符串

实例信息如下:

其中有两字段需要额外说明一下:

  • handler_type:是代指具体 trak 的处理类型。也就是我们上面列写的 vide,soun,hint 字段。
  • name: 是用来写名字的。其主要不是给机器读的,而是给人读,所以,这里你只要觉得能表述清楚,填啥其实都行。

handler_type 填的值其实就是 string 转换为 hex 之后得到的值。比如:

  • vide 为 0x76, 0x69, 0x64, 0x65
  • soun 为 0x73, 0x6F, 0x75, 0x6E

 3.2.3  Media Information Box(minf)

 “minf” 存储了解释track媒体数据的handler-specific信息,media handler用这些信息将媒体时间映射到媒体数据并进行处理。“minf”中的信息格式和内容与媒体类型以及解释媒体数据的media handler密切相关,其他media handler不知道如何解释这些信息。“minf”是一个container box,其实际内容由子box说明。

  一 般情况下,“minf”包含一个header box,一个“dinf”和一个“stbl”,其中,header box根据track type(即media handler type)分为“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”为data information box,“stbl”为sample table box。下面分别介绍。

3.2.3.1 Video Media Header Box(vmhd) 

v/smhd 是对当前 trak 的描述 box。vmhd 针对的是 video,smhd 针对的是 audio。这两个box在解码中,非不可或缺的(有时候得看播放器),缺了的话,有可能会被认为格式不正确。

vmhd结构定义如下表:

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
graphics mode 4 视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成
opcolor 2×3 {red,green,blue}

实例图: 

smhd结构定义:

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
balance 2 立体声平衡,[8.8] 格式值,一般为0,-1.0表示全部左声道,1.0表示全部右声道
reserved 2

实例图:

3.2.3.2 Data Information Box(dinf)

“dinf”解释如何定位媒体信息,是一个container box。“dinf”一般包含一个“dref”,即data reference box;“dref”下会包含若干个“url”或“urn”,这些box组成一个表,用来定位track数据。简单的说,track可以被分成若干段,每一段都可以根据“url”或“urn”指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track。一般情况下,当数据被完全包含在文件中时,“url”或“urn”中的定位字符串是空的。

“dref”的字节结构如下表:

字段 字节数 意义
box size 4 box大小
box type 4 box类型
version 1 box版本,0或1,一般为0。(以下字节数均按version=0)
flags 3
entry count 4 “url”或“urn”表的元素个数
“url”或“urn”列表 不定

   “url”或“urn”都是box,“url”的内容为字符串(location string),“urn”的内容为一对字符串(name string and location string)。当“url”或“urn”的box flag为1时,字符串均为空。

 下面是一个“dinf”的字节实例图。其中黄色框为“dinf”的box header,由红色框中部分我们知道包含的“url”或“urn”个数为1,红色后面为“url”box的内容。紫色框中为“url”的box header(根据box type我们知道是个“url”),绿色框中为box flag,值为1,说明“url”中的字符串为空,表示track数据已包含在文件中。

3.2.3.1 Sample Table Box(stbl)

Sample Table Box,上文提到 mdia 中最主要的部分是存放文件中每个 Sample 信息的 stbl。在解析 stbl 前,我们需要区分 Chunk 和 Sample 这两个概念。

在 MP4 文件中,Sample 是一个媒体流的基本单元,例如视频流的一个 Sample 代表实际的 nal 数据。Chunk 是数据存储的基本单位,它是一系列 Sample 数据的集合,一个 Chunk 中可以包含一个或多的 Sample。

“stbl”包含了关于track中sample所有时间和位置的信息,以及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)等。
“stsd”必不可少,且至少包含一个条目,该box包含了data reference box进行sample数据检索的信息。没有“stsd”就无法计算media sample的存储位置。“stsd”包含了编码的信息,其存储的信息随媒体类型不同而不同。

      video                      audio

3.2.3.1.1  Sample Description Box(stsd)

stsd,即 Sample Description Box,这里主要包含了采样数据的细节信息,包括编码类型以及解码需要的各种初始化数据信息。box header和version字段后会有一个entry count字段,根据entry的个数,每个entry会有type信息,如“vide”、“sund”等,根据type不同sample description会提供不同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track会有“AudioSampleEntry”类型信息。
视频的编码类型、宽高、长度,音频的声道、采样等信息都会出现在这个box中。

视频 track 对应的 stsd Box:

 我们看到上面还在 avcC 条目里包含了视频的 SPS 和 PPS 的信息,这些都是视频解码需要的信息。SPS 和 PPS 是 H.264 流中的元信息,在 MP4 文件中单独存放在 avcC 中。转换的时候,还需要将 SPS 和 PPS 提取出来,添加上 0x00000001,放在 H.264 视频流的开始位置。对于 H.265,其元信息在 hvcC 类型 Box 中。

3.2.3.1.2 Time To Sample Box(stts)

“stts”时戳-sample序号的映射表,存储了sample的duration,描述了sample时序的映射方法,我们通过它可以找到任何时间的sample。“stts”可以包含一个压缩的表来映射时间和sample序号,用其他的表来提供每个sample的长度和指针。表中每个条目提供了在同一个时间偏移量里面连续的sample序号,以及samples的偏移量。递增这些偏移量,就可以建立一个完整的time to sample表。

3.2.3.1.3 Sync Sample Box(stss)

“stss” 确定media中的关键帧。对于压缩媒体数据,关键帧是一系列压缩序列的开始帧,其解压缩时不依赖以前的帧,而后续帧的解压缩将依赖于这个关键帧。 “stss”可以非常紧凑的标记媒体内的随机存取点,它包含一个sample序号表,表内的每一项严格按照sample的序号排列,说明了媒体中的哪一个 sample是关键帧。如果此表不存在,说明每一个sample都是一个关键帧,是一个随机存取点。

以下是该视频的关键帧分布,大家可以用雷神的VideoEye去看一下关键帧的码流,是可以对的上的。

3.2.3.1.4 Sample To Chunk Box(stsc)

stsc,即 Sample To Chunk Box,包含 Sample 和 Chunk 的映射关系。用chunk组织sample可以方便优化数据获取,一个chunk包含一个或多个sample。“stsc”中用一个表描述了sample与chunk的映射关系,查看这张表就可以找到包含指定sample的chunk,从而找到这个sample。

 可以看到这里的 entry count 数值为5。我们可以从stco 的信息中看到视频 track 有 3876个 chunk。那么这里的 stsc 的 entry table 的数据则表示,第 [1, 590-1] 个 chunk 都包含了 1 个 sample,第 [590, 591-590] 个 chunk 都包含了 1 个 sample,第 [591, 790-591] 个 chunk 都包含了 1 个 sample,第 [790, 791-790] 个 chunk 都包含了 2个 sample,第 [791, last=3876] 个 chunk 都包含了 1 个 sample。所以,总共是 (589 * 1) +(1 * 2)+ (199 * 1)+ (1 * 2)+ (3876 - 791 + 1) * 1 = 3878 个 sample。这个数量刚好和 stsz 示例中显示的 sample 数量对的上。

3.2.3.1.5  Sample Size Boxe(stsz)

stsz,即 Sample Size Boxe,包含每个 Sample 的大小。

3.2.3.1.6  Chunk Offset Box(stco)

stco,即 Chunk Offset Box,每个 Chunk 的偏移。这个偏移是相对文件初始位置的偏移。所以这里需要注意的一点是,当修改 mdat Box 之前其他 Box 的信息时,会影响到 Chunk Offset,这里的记录则需要做对应的更新。如果视频文件较大,Offset 用 32 位表示不下,就用 co64 Box 通过 64 位来表示。

在 MP4 文件中,Chunk 是最小的基本单位,而不是 Sample。一个 Chunk 里可以包含单个或多个 Sample。这里是为了优化数据的 I/O 读取效率。

4、Free Space Box(free)

“free”中的内容是无关紧要的,可以被忽略。该box被删除后,不会对播放产生任何影响。

5、Meida Data Box(mdat)

该box包含于文件层,可以有多个,也可以没有(当媒体数据全部为外部文件引用时),用来存储媒体数据。数据直接跟在box type字段后面,具体数据结构的意义需要参考metadata(主要在sample table中描述)。

猜你喜欢

转载自blog.csdn.net/mdx20072419/article/details/125593974