MP4 封装格式详解
- [MP4 封装格式详解](#MP4 封装格式详解)
-
- 简介
- 概念与术语
- [MP4 整体结构](#MP4 整体结构)
- [Box 结构](#Box 结构)
-
- [Box Header](#Box Header)
- [Box Data](#Box Data)
- [MP4 典型 Box](#MP4 典型 Box)
-
- [ftyp(File Type Box)](#ftyp(File Type Box))
- [moov(Movie Box)](#moov(Movie Box))
-
- [mvhd(moov header)](#mvhd(moov header))
- trak
-
- [tkhd(track header box)](#tkhd(track header box))
- [edts(edit Box)](#edts(edit Box))
- [mdia(media box)](#mdia(media box))
- [udta(user data)](#udta(user data))
- [mdat(media data box)](#mdat(media data box))
- [free(free space box)](#free(free space box))
- [stbl(Sample Table Box)](#stbl(Sample Table Box))
-
- [MP4 中的 chunk 和 sample](#MP4 中的 chunk 和 sample)
- [stsd、stts、stss、ctts、stsc、stsz、stco 概述](#stsd、stts、stss、ctts、stsc、stsz、stco 概述)
- [stsd(Sample Description Box)](#stsd(Sample Description Box))
- [stts(Decoding Time to Sample Box)](#stts(Decoding Time to Sample Box))
- [stss(Sync Sample Box)](#stss(Sync Sample Box))
- [ctts(Composition Time to Sample Box)](#ctts(Composition Time to Sample Box))
- [stsc(Sample To Chunk Box)](#stsc(Sample To Chunk Box))
- [stsz(Sample Size Boxes)](#stsz(Sample Size Boxes))
- [stco/co64(Chunk Offset Box)](#stco/co64(Chunk Offset Box))
- [MP4 音视频数据解析过程](#MP4 音视频数据解析过程)
- [MP4 的使用场景及优缺点](#MP4 的使用场景及优缺点)
- Q&A
-
- metadata中的major_brand、minor_version、compatible_brands的作用都是什么?
- 什么是isom?
- [hint track是什么?](#hint track是什么?)
- 为什么mp4要采用这种chunk-sample这种方式进行逻辑上的存储呢?
- 如何区分一个track描述的是视频流还是音频流?
- 参考
MP4 封装格式详解
简介
MP4(MPEG-4 Part 14)是一种标准的数字多媒体容器格式,其扩展名为.mp4,以存储数字音频及数字视频为主,也可存储字幕和静止图像。
虽然被官方标准定义的唯一扩展名是.mp4,但第三方通常会使用各种扩展名来指示文件的内容:
- 同时拥有音频视频的MPEG-4文件通常使用标准扩展名.mp4;
- 仅有音频的MPEG-4文件会使用.m4a扩展名。
大部分数据可以通过专用数据流嵌入到MP4文件中,因此MP4文件中包含了一个单独的用于存储流信息的轨道。目前得到广泛支持的编解码器或数据流格式有:
视频格式:H.264/AVC、H.265/HEVC、VP8/9等。
音频格式:AAC、MP3、Opus等。
因其可容纳支持比特流的视频流(如高级视频编码),MP4可以在网络传输时使用流式传输。
在线解析 MP4 工具:
MP4box.js:https://gpac.github.io/mp4box.js/test/filereader.html
mp4parser:https://www.onlinemp4parser.com/
概念与术语
概念与术语是理解好MP4媒体封装格式和其操作算法的关键,为了方便了解MP4文件格式,需先了解以下几个概念与术语:
- Box:MP4文件是由一个个Box组成的,可以将其理解为一个数据块,它由Header+Data组成,Data 可以存储媒体元数据和实际的音视频码流数据。Box可直接存储数据块,也可包含其它Box,我们把包含其它Box的Box称为container box。
- Sample:可理解为采样,对于视频可理解为一帧数据,音频一帧数据就是一段固定时间的音频数据,可以由多个Sample数据组成,存储媒体数据的单位是sample。
- Chunk:连续几个sample组成的单元被称为chunk,每个chunk在文件中有一个偏移量,整个偏移量从文件头算起,在这个chunk内,sample是连续存储的。
- Track:表示一些chunk的集合,对于媒体数据而言就是一个视频序列或者音频序列,常说的音频/视频轨可对照该概念上。除了Video Track和Audio Track外,还可以有非媒体数据,比如Hint Track,这种类型的Track就不包含媒体数据,可以包含一些将其他数据打包成媒体数据的指示信息或者字幕信息。简单来说,Track是音视频中可以独立操作的媒体单位。
可理解为MP4文件中有多个Track,一个Track由多个Chunk组成,每个Chunk包含一组连续的Sample。例如视频流的一个Sample代表实际的nal数据,Chunk是数据存储的基本单位,它是一系列Sample数据的集合。
MP4 整体结构
MP4是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,以及各种编码的音视频。
MP4 文件由许多个 Box 和 FullBox 组成。每个Box 包含不同的信息, 这些 Box 以树形结构的方式组织。
每个 Box 由 Header 和 Data 两部分组成。Data 是 Box 的实际数据,可以是纯数据,也可以是更多的子 Box(这个 Box 称为 Container Box)。
FullBox 是 Box 的拓展,其在 Box 结构的基础上,在 Header 中添加 1 字节的 version 标志和 3 字节的 flags 标志。
Box Header 至少占 8 字节。第一个字段是 size,占 4 字节, 表示整个 Box 的大小。第二个字段是 type,占 4字节,表示 Box 的类型。当 size = 0 时,代表这是 MP4 文件的最后一个 Box,当 size = 1 时,说明 Box 长度需要更多位才能描述,在 type 后会定义一个 8 字节的 largesize 用来描述 Box 的长度。当 type = uuid 时,说明这个 Box 中的数据时用户自定义拓展类型。
下图是常见的 Box 的结构图,可用来大致了解MP4文件的构造:
一个 MP4 文件的典型结构如下:
cpp
+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ftyp | moov | mdat |
+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中:
- ftyp(file type box):在文件的开始位置,记录一些兼容性信息,比如文件的版本、兼容协议等;
- moov(movie box):音视频数据的metadata信息。这个box中不包含具体媒体数据,但包含本文件中所有媒体数据的宏观描述信息,moov box下有mvhd box和trak box。
- mvhd 中记录了创建时间、修改时间、时间度量标尺、可播放时长等信息。
- trak 中的一系列子box描述了每个媒体轨道的具体信息。
- mdat(media data box):媒体负载,media 数据容器。我们最终解码播放的实际媒体数据都在这里面。
因为 MP4 标准中没有对 moov 和 mdat 的存放位置没有强制先后要求,所以它们的顺序不定。在互联网视频的点播中,如果希望 MP4 文件被快速打开,则需要将 moov 存放在 mdat 的前面;如果moov 放在 mdat 的后面,则需要将 MP4 文件全部下载完才可以播放。
常见 Box 的简要说明:
Box 结构
通过上面的介绍,我们了解了MP4格式就是由一个个的box组合成的box树,所有的数据都包含在box里,下面来了解一下box的基本结构。
一个box是由Header+Data组成。
cpp
+-+-+-+-+-+-+-+-+-+-+
| Header | Data |
+-+-+-+-+-+-+-+-+-+-+
其它所有 box 都在语法上继承自此基本 box 结构。
Box Header
Box 分为普通 Box 和 FullBox。
普通 Box Header 结构如下:
字段 | 类型 | 描述 |
---|---|---|
size | 4 Bytes | 表示整个 box 的大小。size=0说明是最后一个box;size=1时,后面的largesize表示真正的box长度 |
type | 4 Bytes | 4 个 ASCII 值,如果是 "uuid",则表示此 box 为用户自定义拓展类型 |
largesize | 8 Bytes | size=1 时才有的字段,用于扩展,例如 mdat box 会需要此字段 |
FullBox 在上面的基础上新增了 2 个字段:
字段 | 类型 | 描述 |
---|---|---|
version | 1 Byte | 版本号 |
flags | 3 Bytes | 标识 |
Box Data
一个 Box 可能会包含其它多个 Box,此种 Box 称为 Container Box。
因此,Box Data 可能是数据,也可能是其他 Box。
MP4 典型 Box
虽然Box的类型非常多,大概有70多种,但是并不是都是必须的,一般的MP4文件都是含有必须的Box和个别非必须Box。下面介绍几种典型的 Box。
ftyp(File Type Box)
ftyp是MP4文件的第一个Box,通过判断该Box来确定文件的类型。该Box有且仅有1个,并且只能被包含在文件层,而不能被其他Box包含。该Box放在文件的最开始,指示文件的相关信息。
ftyp Box Header首先是 size(4字节),表示整个 Box 的大小;然后是 type(4字节),表示 Box 类型,其内容当然是 "ftyp"。
ftyp Box Data 有三个字段:
字段 | 类型 | 描述 |
---|---|---|
major_brand | 4 bytes | 主版本号 |
minor_version | 4 bytes | 次版本号 |
compatible_brands[] | 4×n bytes | 指定兼容的版本,注意此字段是一个 list,可以包含多个版本号,每个版本号占 4 字节 |
实例:
moov(Movie Box)
moov 是MP4文件中必须有的一个 Box,但只能存在一个,一般出现在 ftyp 之后或 MP4 文件末尾。
实例:
moov 是一个 Container Box,其包含的所有 Box 用于描述媒体信息(metadata),其子 Box 一般有三种:
- mvhd(moov header):用于简单描述一些所有媒体共享的信息。
- trak:即track,轨道。用于描述音频流或视频流信息,可以有多个轨道,上面的样例中trak出现了 2 次,分别表示一路音频和一路视频流。
- udta(user data):用户自定义,可忽略。
实例:
mvhd(moov header)
该box是全文件唯一的一个 Box,其对整个媒体文件所包含的媒体数据进行全面的描述。其中包含了媒体的创建和修改时间,默认音量、色域、时长等信息。
mvhd 语法继承自 FullBox,注意下述示例出现的 version 和 flags 字段属于 FullBox header。
字段 | 类型 | 描述 |
---|---|---|
version | 1 Byte | 版本,取 0 或 1,一般取 0 |
flags | 3 Bytes | 标识 |
creation_time | 8/16 Bytes | 创建时间,当 version=0 时取 4 字节 |
modification_time | 8/16 Bytes | 修改时间,当 version=0 时取 4 字节 |
timescale | 8 Bytes | 时间基 |
duration | 8/16 Bytes | 文件时长,当 version=0 时取 4 字节 |
rate | 8 Bytes | 播放速率,默认取 0x00010000,即 1.0 |
volume | 4 Bytes | 音量,默认取 0x0100,即 1.0 |
reserved | 4 Bytes | 0 |
reserved | 2 x 8 Bytes | 0 |
matrix | 9 x 8 Bytes | 视频播放矩阵,可忽略 |
pre_defined | 6 x 8 Bytes | 0 |
next_track_id | 8 Bytes | 下一个紧邻的 track box id |
实例:
trak
trak box 是一个 container box,其子 box 包含了该 track 的媒体信息。
一个 mp4 文件可以包含多个 track,track之间是独立的,trak box 用于描述每一路媒体流。一般情况下,一个MP4文件有两个trak,分别对应音频流和视频流。
每个Trak Box都需要有一个tkhd box和mdia box,其它的box都是可选择的。
- tkhd(track header box):用于简单描述该路媒体流的信息,如时长,宽度等。
- mdia(media box):用于详细描述该路媒体流的信息
- edts(edit Box):子Box为elst(Edit List Box),它的作用是使某个track的时间戳产生偏移。
tkhd(track header box)
tkhd(track header box)是一个 FullBox,对应字段的含义参考下图:
edts(edit Box)
不是所有的 MP4文件有这个 Box
子Box为elst(Edit List Box),它的作用是使某个track的时间戳产生偏移。
实例:
mdia(media box)
mdia(media box)也是Container Box,里面包含子Box,一般必须有mdhd box、hdlr box、minf box。基本就是当前Track媒体头信息和媒体句柄以及媒体信息。它自身非常简单,就是一个标识而已,但最复杂的还是里面包含的子box。
其中:
- mdhd(Media Header Box):用于简单描述该路媒体流的信息。其中我们最关心的两个字段是timescale和duration,分别表示了该Track的时间戳和时长信息,这个时间戳信息也是PTS和DTS的单位。
- hdlr(Handler Reference Box):该box解释了媒体的播放过程信息,用来设置不同Track的处理方式,标识了该Track的类型,音频Track的handler为soun,视频Track的handler为vide。
- minf(Media Information box):该box建立了时间到真实音视频sample的映射关系,是音视频数据操作的关键。该box是container box,含有三大必须的子Box:
- Media Info Header Box:根据track type(即media handler type)分为"vmhd"(视频)、"smhd"(音频)、"hmhd"和"nmhd"
- dinf(data information box):描述了如何定位媒体信息,是一个container box。"dinf"一般包含一个"dref"(data reference box)。"dref"下会包含若干个"url"或"urn",这些box组成一个表,用来定位track数据。简单的说,track可以被分成若干段,每一段都可以根据"url"或"urn"指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track。一般情况下,当数据被完全包含在文件中时,"url"或"urn"中的定位字符串是空的。
- stbl(Sample Table Box):stbl box是一个container box,是整个track中最重要的一个box,其子box描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。
实例:
udta(user data)
udta中保存了用户定义数据,例如iTune使用的meta数据就保存在udta中。
mdat(media data box)
mdat 用于存储音视频数据,可从该Box解封装出真实的媒体数据。该Box一般都会存在,但非必须。
mdat的位置比较灵活,可以位于moov之前,也可以位于moov之后,但必须和stbl中的信息保持一致。
另外,在写mp4文件的时候,对于mdat这个Atom,一般是先将Atom size填写0,待数据写完之后,再回过来填入具体大小。
mdat 可以引用外部的数据,参见 moov -> udta -> meta,这里不讨论,只讨论数据存储在本文件中的形式。
mdat 也是一个 box,拥有 box header 和 box body。
对于 box body 部分,采用一个一个 samples 的形式进行存储,即一个一个音频帧或视频帧的形式进行存储。没有同步字,没有分隔符,只能根据索引进行访问。即:
cpp
Box header + Box Data
==>
Box size + Box type + NALU + ... + NALU
NALU = NALU length + NALU Header + NALU Data
码流组织方式采用 avcc 格式,即 AUD + slice size + slice 的形式。
free(free space box)
free box 是可选的,如果存在,则通常出现在moov与mdat之间,即moov-free-mdat。
free中的数据通常为全0,其作用相当于占位符,其内容是无关紧要的,可以被忽略。该box被删除后,不会对播放产生任何影响。
在实时拍摄视频,moov数据增多时,将free分配给moov使用。因为设备录制视频时并不能预先知道视频数据大小,如果moov在mdat之前,随着拍摄mdat的数据会增加,moov数据也会增多,如果没有free预留的空间,则要不停的向后移动mdat数据以腾出moov空间。
stbl(Sample Table Box)
stbl 表在 MP4 文件中的位置:moov -> trak -> mdia -> minf -> stbl。不要因为它是一个五级容器就小看它,它是整个 track 中最重要的一个 box,其子 box 描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。想要播放一个 MP4 文件,必须根据stbl正确找到每个sample并送给解码器。stbl用来描述每个sample的信息。
MP4 中的 chunk 和 sample
在介绍stbl box之前,需要先介绍一下 MP4 中定义的sample与chunk:
- sample:ISO/IEC 14496-12 中定义 samples 之间不能共享同一个时间戳,因此,在音视频 track 中,一个 sample 代表一个视频或音频帧。
- chunk:多个 sample 的集合,实际上音视频 track 中,chunk 与 sample 一一对应。
stsd、stts、stss、ctts、stsc、stsz、stco 概述
stbl(Sample Table Box)的子 Box 的简要介绍:
- stsd(sample description box):存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关。给出视频、音频的编码、宽高、音量等信息,以及每个sample中包含多少个frame。
- 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 在文件中的偏移。
实例:
下面开始逐个讲解各子 Box 的细节。
stsd(Sample Description Box)
stsd 是个 container box,其存储了编码类型和初始化解码器需要的信息。
stsd的内容和track的类型有关,也就是和hdlr的handler_type参数有关。
字段 | 类型 | 描述 |
---|---|---|
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' 时才有 |
结束循环 |
video track的stsd body内容(不同的编码方式,stsd的子box的名称会有不同,但是box中的字段都是相同的):
Field | Comment |
---|---|
type | box类型,包括 "预定义类型"、"自定义扩展类型"。预定义类型:比如ftyp、moov、mdat等预定义好的类型;自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
size | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
data_reference_index | 当MP4文件的数据部分,可以被分割成多个片段,每一段对应一个索引,并分别通过URL地址来获取,此时,data_reference_index 指向对应的片段(比较少用到) |
width、height | 视频的宽高,单位是像素 |
horizresolution、vertresolution | 水平、垂直方向的分辨率(像素/英寸),默认是0x00480000(72dpi) |
frame_count | 一个sample中包含多少个frame,对video track来说,默认是1 |
compressorname | 仅供参考的名字,通常用于展示,占32个字节,比如 AVC Coding。第一个字节,表示这个名字实际要占用N个字节的长度。第2到第N+1个字节,存储这个名字。第N+2到32个字节为填充字节。compressorname 可以设置为0 |
depth | 位图的深度信息,比如 0x0018(24),表示不带alpha通道的图片 |
实例:
audio track的stsd body内容(不同的编码方式,stsd的子box的名称会有不同,但是box种的字段都是相同的):
Field | Comment |
---|---|
type | box类型,包括 "预定义类型"、"自定义扩展类型"。预定义类型:比如ftyp、moov、mdat等预定义好的类型;自定义扩展类型:如果type==uuid,则表示是自定义扩展类型。size(或largesize)随后的16字节,为自定义类型的值(extended_type) |
size | 包含box header在内的整个box的大小,单位是字节。当size为0或1时,需要特殊处理 |
data_reference_index | 当MP4文件的数据部分,可以被分割成多个片段,每一段对应一个索引,并分别通过URL地址来获取,此时,data_reference_index 指向对应的片段(比较少用到) |
channel_count | 声道数,取值为1或2 |
samplesize | 采样位宽,一般为8bit或16bit |
samplerate | 采样率 |
实例:
hint track的stsd body内容:
Field | Comment |
---|---|
data | hint数据 |
stts(Decoding Time to Sample Box)
stts包含了DTS到sample number的映射表,主要用来推导每个帧的时长,描述了sample时序的映射方法,通过它可以找到任何时间的sample。
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
entry_count | 32 bit | 条目个数 |
开始循环 | ||
sample_count | 32 bit | 单个entry中,具有相同时长(duration 或 sample_delta)的连续sample的个数 |
sample_delta | 32 bit | 单个 sample 的播放时长,单位为 timescale,也可以说是相邻两个sample之间dts的差值 |
结束循环 |
实例:第1个sample的时长是33333,第2个sample的时长是33334,第3-4个sample的时长是33333,以此类推(假设mdhd中timescale为1000,则实际时长需要除以1000)。
stss(Sync Sample Box)
针对视频 track,它包含关键帧所在的 sample 序号。
关键帧是为了支持随机访问。如果此表不存在,说明每一个 sample 都是一个关键帧。
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
entry_count | 32 bit | 条目个数 |
开始循环 | ||
sample_number | 32 bit | sample 计数,从 1 开始 |
结束循环 |
实例:第1帧、第151帧、第301帧...,是关键帧,以此类推。
ctts(Composition Time to Sample Box)
存储了该 track 中,每个 sample 的 pts 与 dts 时间差,cts = pts - dts。
对于只有I帧、P帧的视频来说,解码顺序、渲染顺序是一致的,此时,ctts没必要存在。
对于存在B帧的视频来说,ctts就需要存在了。当PTS、DTS不相等时,就需要ctts了,公式为 PTS(n) = DTS(n) + TTS(n) 。
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
sample_counts | 单个entry中,具有相同差值(pts-dts)的连续sample的个数 | |
sample_offsets | 从解码(dts)到渲染(pts)之间的差值 |
实例:第1个sample的pts-dts=2002,第2个sample的pts-dts=5005,第3个sample的pts-dts=2002,以此类推,真实时长还要除以mdhd中的timescale。
stsc(Sample To Chunk Box)
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
first_chunk | 当前表项中,对应的第一个chunk的序号 | |
samples_per_chunk | 同一个entry中每个chunk包含的sample数 | |
sample_description_index | chunk使用的stsd的序号,即不同chunk可以使用不同编解码信息 |
实例:
第1个entry的第一个chunk的序号是1,第2个entry的第一个chunk的序号是8225(即第1个entry包含索引值为1-8224的chunk),第3个entry的第一个chunk的索引值时8226,以此类推,最后一个entry就是从first_chunk开始一直到结束为止。
第一个entry每一个chunk包含一个sample,即第1-8224个chunk,每个chunk包含1个sample。第二个entry每一个chunk包含342个sample,即第8225的chunk个chunk包含342个sample。第三个entry每一个chunk包含382个sample,即第8226个chunk包含382个sample,以此类推。
以上所有chunk中的sample,对应的sample description的序号都是1,也就是用第一个stsd。
stsz(Sample Size Boxes)
存储了该 track 中每个 sample 的字节大小。
这个box相对来说体积比较大的。表明视频帧或者音频帧大小,FFmpeg 里面的AVPacket 的size 数据大小,就是从这个box中来的。
有两种不同的box类型:stsz、stz2。
stsz
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
sample_size | 默认的sample大小(单位是byte),通常为0。如果sample_size不为0,那么,所有的sample都是同样的大小。如果sample_size为0,那么,sample的大小可能不一样 | |
sample_count | 当前track里面的sample数目。如果 sample_size==0,那么,sample_count 等于下面entry_size中的数据个数 | |
entry_size(sample_sizes) | 单个sample的大小(如果sample_size==0的话) |
实例:
stz2
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
field_size | entry表中,每个entry_size占据的位数(bit),可选的值为4、8、16。4比较特殊,当field_size等于4时,一个字节上包含两个entry,高4位为entry[i],低4位为entry[i+1] | |
sample_count | 等于下面entry_size中的数据个数 | |
entry_size | 单个sample的大小 |
stco/co64(Chunk Offset Box)
chunk在文件中的偏移量(不是mdat中的偏移量)。
针对小文件、大文件,有两种不同的box类型,分别是stco、co64,它们的结构是一样的,只是字段长度不同。
需要注意的是一旦前面的box有了任何改变,这张表都要重新建立。
chunk_offset 指的是在文件本身中的 offset,而不是某个box内部的偏移。
在构建 MP4 文件的时候,需要特别注意 moov 所处的位置,它对于chunk_offset 的值是有影响的。有一些MP4文件的 moov 在文件末尾,为了优化首帧速度,需要将 moov 移到文件前面,此时,需要对 chunk_offset 进行改写。
字段 | 类型 | 描述 |
---|---|---|
version | 8 bit | 取 0 或 1,一般取 0 |
flags | 24 bit | |
chunk_offset | chunk 在文件中的位置(在文件本身中的 offset,而不是某个box内部的偏移) |
实例:第1个chunk的offset是48,第2个chunk的偏移是72831,以此类推。
MP4 音视频数据解析过程
这里说的解析基本是指解封装、播放的过程,都是先读取box中的内容,然后根据box中内容可以找到每一个sample也就是每一帧数据,然后进行解封装、播放。
详细过程如下:
- 通过stss确定关键帧的数据和每个关键帧的序号。
- 通过stts获取音视频流的总帧数、每一帧的dts和整体时长。
- 通过ctts获取每一帧中pts与dts的差值。
- 通过stsz获取每个sample的大小及整个流的总大小。
- 通过stco获取音视频流中chunk的数据和在文件中的位置(不是在mdat中的位置)。
- 通过stsc计算出流中chunk的数据和在文件中的位置,并通过chunk在文件中的位置及chunk中每个sample的大小,确定每个sample在文件中的位置和大小。
- 整理每个流的码流信息和时间戳信息,构成整体播放的时间轴,之后进行解码的渲染播放。
MP4 的使用场景及优缺点
使用场景:主要是点播场景。
优点:
- MP4的标准非常灵活,可扩展性比较好,有很多常见的格式是基于MP4做了一些扩展,然后被应用到比较广的范围,比如 CMAF、DASH、HLS。
- 因为MP4格式的开放性和灵活性,使得使用范围非常广泛,我们日常看电视剧,录视频时,最常见的就是MP4格式了。
缺点:
- 普通mp4情况下,会导致播放延时较高。
- 如果moov在文件尾,播放器需要遍历整个文件后才会找到moov并播放。
Q&A
metadata中的major_brand、minor_version、compatible_brands的作用都是什么?
ftyp Box Data 有三个字段:
字段 | 类型 | 描述 |
---|---|---|
major_brand | 4 bytes | 主版本号 |
minor_version | 4 bytes | 次版本号 |
compatible_brands[] | 4×n bytes | 指定兼容的版本,注意此字段是一个 list,可以包含多个版本号,每个版本号占 4 字节 |
实例:
什么是isom?
isom(ISO Base Media file)是在 MPEG-4 Part 12 中定义的一种基础文件格式,MP4、3gp、QT 等常见的封装格式,都是基于这种基础文件格式衍生的。
-
MP4 文件可能遵循的规范有mp41、mp42,而mp41、mp42又是基于isom衍生出来的。
-
- 3gp(3PP):一种文件格式,主要用于3G手机上。
QT(QuickTime):.qt文件代表苹果QuickTime媒体文件。
hint track是什么?
hint track:这个特殊的track并不包含媒体数据,而是包含了一些将其它数据track打包成流媒体的指示信息。
下面是mp4官方文档对hint track的描述:
为什么mp4要采用这种chunk-sample这种方式进行逻辑上的存储呢?
总的来说是用时间换空间,因为chunk描述的是一组sample,这些sample相同的性质可以通过chunk去统一描述,比如stsc的sample_description_index就是直接对chunk描述,如果没有chunk则需要对每一个sample都描述一遍。但是在解析sample的时候就需要获取chunk的部分信息来帮助解析,没有那么直接,需要临时建一张表。
如何区分一个track描述的是视频流还是音频流?
方法一:
- 视频:track->tkhd中width、height不为零,volume为零
- 音频:track->tkhd中width、height为零,volume不为零
方法二:
- 视频:track->mdia->hdlr中handler是vide
- 音频:track->mdia->hdlr中handler是soun
方法三:
- 视频:track->mdia->minf中有vmhd box(Video Media Information Header)
- 音频:track->mdia->minf中有smhd box(Sound Media Information Header)