“本文转载自:
1.[小夕nike]MP4 封装格式
2.[weixin_41422027] 音视频封装:MP4结构概述和分析工具推荐"
本文相关系列:
封装格式--1:FLV格式详解
封装格式--2:MP4格式详解
封装格式--3:TS格式详解
封装格式--4:PS格式详解
1.MP4格式概述
1.1 简介
MP4或称MPEG-4第14部分(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等
1.2 术语
为了后面能比较规范的了解这种文件格式,这里需要了解下面几个概念和术语,这些概念和术语是理解好MP4媒体封装格式和其操作算法的关键。
(1)Box
这个概念起源于QuickTime中的atom,也就是说MP4文件就是由一个个Box组成的,可以将其理解为一个数据块,它由Header+Data组成,Data 可以存储媒体元数据和实际的音视频码流数据。Box里面可以直接存储数据块但是也可以包含其它类型的Box,我们把这种Box又称为container box。
(2)Sample
简单理解为采样,对于视频可以理解为一帧数据,音频一帧数据就是一段固定时间的音频数据,可以由多个Sample数据组成,简而言之:存储媒体数据的单位是sample。
(3)Track
表示一些sample的集合,对于媒体数据而言就是一个视频序列或者音频序列,我们常说的音频轨和视频轨可以对照到这个概念上。当然除了Video Track和Audio Track还可以有非媒体数据,比如Hint Track,这种类型的Track就不包含媒体数据,可以包含一些将其他数据打包成媒体数据的指示信息或者字幕信息。简单来说:Track 就是电影中可以独立操作的媒体单位。
(4)Chunk
一个track的连续几个sample组成的单元被称为chunk,每个chunk在文件中有一个偏移量,整个偏移量从文件头算起,在这个chunk内,sample是连续存储的。
这样就可以理解为MP4文件里面有多个Track,一个Track又是由多个Chunk组成,每个Chunk里面包含着一组连续的Sample,正是因为定义了上述几个概念,MP4这种封装格式才容易实现灵活、高效、开放的特性,所以要仔细理解。
2.MP4整体结构
2.1 MP4结构概览
MP4格式是一个box的格式,box容器套box子容器,box子容器再套box子容器。
mp4_1.PNG
一个box由两部分组成:box header、box body。
box header:box的元数据,比如box type、box size。
box body:box的数据部分,实际存储的内容跟box类型有关,比如mdat中body部分存储的媒体数据。
box header中,只有type、size是必选字段。当size==1时,存在largesize字段。如果size==0,表示该box为文件的最后一个box。在部分box中,还存在version、flags字段,这样的box叫做Full Box。当box body中嵌套其他box时,这样的box叫做container box。
mp4box 图示如下:
其中:
ftyp(file type box):文件头,记录一些兼容性信息
moov(movie box):记录媒体信息
mdat(media data):媒体负载
完整的Box结构:
mp4_2.png
每个Box承载的数据内容如下:
2.2 Box结构
mp4 封装格式采用称为 box 的结构来组织数据。结构如下:
+-+-+-+-+-+-+-+-+-+-+
| header | body |
+-+-+-+-+-+-+-+-+-+-+
其它所有 box 都在语法上继承自此基本 box 结构。
2.2.1 box header
box 分为普通 box 和 fullbox。
(1)普通 box header 结构如下:
字段
类型
描述
size
4 Bytes
包含 box header 的整个 box 的大小
type
4 Bytes
4 个 ascii 值,如果是 "uuid",则表示此 box 为用户自定义,可忽略
large size
8 Bytes
size=1 时才有的字段,用于扩展,例如 mdat box 会需要此字段
(2)fullbox 在上面的基础上新增了 2 个字段:
字段
类型
描述
version
1 Bytes
版本号
flags
3 Bytes
标识
2.2.2 box body
一个box可能会包含其它多个box,此种box称为container box。因此box body可能是一种具体box类型,也有可能是其它box。
虽然Box的类型非常多,大概有70多种,但是并不是都是必须的,一般的MP4文件都是含有必须的Box和个别非必须Box,我用MP4info这种工具分析了一首MP4的文件,具体的Box显示如下:
mp4_3.png
通过上述工具分析出来的结果,我们大概可以总结出MP4以下几个特点:
MP4文件就是由一个个Box组成,其中Box还可以相互嵌套,排列紧凑没有多的冗余数据;
Box类型并没有很多,主要是由必须的ftyp、moov、mdat组成,还有free,udta非必须box组成即去掉这两种box对于播放音视频也没有啥影响。
Moov一般存储媒体元数据,比较复杂嵌套层次比较深,后面会详细解释各个box的字段含义和组成。
2.3 ftyp(File Type Box)
ftyp 一般出现在文件的开头,用来指示该 mp4 文件使用的标准规范:
字段
类型
描述
major_brand
4 bytes
主版本
minor_version
4 bytes
次版本
compatible_brands[]
4 bytes
指定兼容的版本,注意此字段是一个 list,即可以包含多个 4 bytes 版本号
一个示例如下:
mp4_4.png
mp4_5.png
2.4 moov(Movie Box)
moov是一个container box,一个文件只有一个,其包含的所有box用于描述媒体信息(metadata)。
moov的位置可以紧随着 ftyp 出现,也可以出现在文件末尾。
由于是一个 container box,所以除了 box header,其 box body 就是其它的 box。
子Box:
mvhd(moov header):用于简单描述一些所有媒体共享的信息。
trak:即 track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。
udta(user data):用户自定义,可忽略。
一个示例如下:
(1)结构
mp4_6.png
(2)数据
mp4_7.png
(3)成分
mp4_8.png
子Box:mvhd
用于简单描述一些所有媒体共享的信息。
mp4_9.png
子Box:trak
track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。
mp4_10.png
2.5 mvhd(Movie Header Box)
mvhd 作为媒体信息的 header 出现(注意此header不是box header,而是moov媒体信息的header),用于描述一些所有媒体共享的基本信息。
mvhd 语法继承自fullbox,注意下述示例出现的version和flags字段属于fullbox header。
Box Body:
mp4_11.png
2.6 trak(track)
trak box 是一个 container box,其子 box 包含了该 track 的媒体信息。
一个 mp4 文件可以包含多个 track,track之间是独立的,trak box 用于描述每一路媒体流。
一般情况下有两个trak,分别对应音频流和视频流。
一个示例如下:
mp4_12.png
其中:
tkhd(track header box):用于简单描述该路媒体流的信息,如时长,宽度等。
mdia(media box):用于详细描述该路媒体流的信息
edts(edit Box):子Box为elst(Edit List Box),它的作用是使某个track的时间戳产生偏移。
2.7 tkhd(track header box)
tkhd 作为媒体信息的header出现(注意此 header 不是 box header,而是 track 媒体信息的 header),用于描述一些该track的基本信息。
tkhd语法继承自fullbox,注意下述示例出现的version和flags 字段属于fullbox header。
Box Body:
mp4_13.png
2.8 edts(edit Box)
它下边有一个elst(Edit List Box),它的作用是使某个track的时间戳产生偏移。看一下一些字段:
segment_duration: 表示该edit段的时长,以Movie Header Box(mvhd)中的timescale为单位,即 segment_duration/timescale = 实际时长(单位s)
media_time: 表示该edit段的起始时间,以track中Media Header Box(mdhd)中的timescale为单位。如果值为-1(FFFFFF),表示是空edit,一个track中最后一个edit不能为空。
media_rate: edit段的速率为0的话,edit段相当于一个”dwell”,即画面停止。画面会在media_time点上停止segment_duration时间。否则这个值始终为1。
需要注意的问题:
为使PTS从0开始,media_time字段一般设置为第一个CTTS的值,计算PTS和DTS的时候,他们分别都减去media_time字段的值就可以将PTS调整为从0开始的值。
如果media_time是从一个比较大的值,则表示要求PTS值大于该值时画面才进行显示,这时应该将第一个大于或等于该值的PTS设置为0,其他的PTS和DTS也相应做调整。
2.9 mdia(media box)
定义了track媒体类型以及sample数据,描述sample信息。
它是一个container box,它必须包含mdhd,hdlr 和 minf。
一个示例如下:
mp4_14.png
其中:
mdhd(Media Header Box):用于简单描述该路媒体流的信息。
hdlr(Handler Reference Box):主要定义了 track 类型。
stbl(Media Information Box):用于描述该路媒体流的解码相关信息和音视频位置等信息。
2.10 mdhd(Media Header Box)
mdhd 作为媒体信息的header出现(注意此header不是box header,而是media媒体信息的header),用于描述一些该media的基本信息。
mdhd和tkhd ,内容大致都是一样的。不过tkhd通常是对指定的track设定相关属性和内容。而mdhd 是针对于独立的media来设置的。
mdhd语法继承自fullbox,注意下述示例出现的 version 和 flags 字段属于fullbox header。
Box Body:
mp4_15.png
注:timescale同mvhd中的timescale,但是需要注意虽然意义相同,但是值有可能不同,下边stts,ctts等时间戳的计算都是以mdhd中的timescale。
2.11 hdlr(Handler Reference Box)
主要解释了媒体的播放过程信息。声明当前track的类型,以及对应的处理器(handler)。
hdlr 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
Box Body:
mp4_16.png
2.12 minf(Media Information box)
解释 track 媒体数据的 handler-specific 信息,media handler用这些信息将媒体时间映射到媒体数据并进行处理。minf 同样是个 container box,其内部需要关注的内容是 stbl,这也是 moov 中最复杂的部分。
一般情况下,“minf”包含一个header box,一个“dinf”和一个“stbl”,其中,header box根据track type(即media handler type)分为“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”为data information box,“stbl”为sample table box。
mp4_17.png
2.13 *mhd (Media Info Header Box)
可分为“vmhd”、“smhd”、“hmhd”和“nmhd”,比如视频类型则为vmhd,音频类型为smhd。
(1)vmhd
graphics mode:视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成。
opcolor:一组(red,green,blue),graphics modes使用。
(2)smhd
balance:立体声平衡,[8.8] 格式值,一般为0表示中间,-1.0表示全部左声道,1.0表示全部右声道。
2.14 dinf(Data Information Box)
描述了如何定位媒体信息,是一个container box。
“dinf”一般包含一个“dref”(data reference box)。
“dref”下会包含若干个“url”或“urn”,这些box组成一个表,用来定位track数据。简单的说,track可以被分成若干段,每一段都可以根据“url”或“urn”指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track。一般情况下,当数据被完全包含在文件中时,“url”或“urn”中的定位字符串是空的。
2.15 stbl(Sample Table Box)
在介绍stbl box之前,需要先介绍一下mp4中定义的sample与chunk:
sample:ISO/IEC 14496-12 中定义 samples 之间不能共享同一个时间戳,因此,在音视频 track 中,一个 sample 代表一个视频或音频帧。
chunk:多个 sample 的集合,实际上音视频 track 中,chunk 与 sample 一一对应。
mp4_18.png
stbl box是一个container box,是整个track中最重要的一个box,其子box描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。
MP4文件的媒体数据部分在mdat box里,而stbl则包含了这些媒体数据的索引以及时间信息。
一个示例如下:
mp4_19.png
其中:
stsd(sample description box):存储了编码类型和初始化解码器需要的信息,并与具体编解码器类型有关。
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 在文件中的偏移。
2.16 stsd(sample description box)
主要存储了编码类型和初始化解码器需要的信息。这里以视频为例,包含子box:avc1,表示是H264的视频。
mp4_20.png
2.16.1 h264 stsd
对于h264视频,典型结构如下:
mp4_21.png
其上(只列出 avc1 与 avcC,其余 box 可忽略):
avc1,是 avc/h264/mpeg-4 part 10视频编解码格式的代称,是一个 container box,但是 box body 也携带自身的信息。
Box Body:
mp4_22.png
avcC(AVC Video Stream Definition Box),存储 sps && pps,即在 ISO/IEC 14496-15 中定义的 AVCDecoderConfigurationRecord 结构
mp4_23.png
注:在 srs 中,解析 avcc/AVCDecoderConfigurationRecord 结构解析参见 srs/trunk/src/kernel/srs_kerner_codec.cpp::SrsFormat::avc_demux_sps_pps() 函数。
2.16.2 aac stsd
对于 aac 音频,典型结构如下:
mp4_24.png
可以看到,aac stsd 结构比较复杂,box 众多。实际上,在 ISO/IEC 14496-3 中,定义了 AudioSpecificConfig 类型,aac stsd 结构主要信息就来自 AudioSpecificConfig。
具体不做分析,可以参看 srs 中:
解析 AudioSpecificConfig 结构的 srs/trunk/src/kernel/srs_kerner_codec.cpp::SrsFormat::audio_aac_sequence_header_demux() 函数
封装 aac stsd 结构的 srs/trunk/src/kernel/srs_kernel_mp4.cpp::SrsMp4Encoder::flush() 函数
2.17 stts(time to sample box)
存储了该 track 每个 sample 到 dts 的时间映射关系。
包含了一个压缩版本的表,通过这个表可以从解码时间映射到sample序号。表中的每一项是连续相同的编码时间增量(Decode Delta)的个数和编码时间增量。通过把时间增量累加就可以建立一个完整的time to sample表。
mp4_25.png
这里为了节约条目的个数,采用了压缩存储的方式,即 sample_count 个连续的 sample 如果 sample_delta 时长一样,那么用一个条目就能表示了。
一个音频 track 的示例如下:
mp4_26.png
一个视频 track 的示例如下:
mp4_27.png
2.18 ctts(composition time to sample box)
存储了该 track 中,每个 sample 的 pts 与 dts 时间差(cts = pts - dts):
如果一个视频只有I帧和P帧,则ctts这个表就不需要了,因为解码顺序和显示顺序是一致的,但是如果视频中存在B帧,则需要ctts。
mp4_28.png
注意:
此 box 在 dts 和 pts 不一样的情况下必须存在,如果一样,不用包含此 box。
如果 box 的 version=0,意味着所有 sample 都满足 pts >= dts,因而差值用一个无符号的数字表示。只要存在一个 pts < dts,那么必须使用 version=1、有符号差值来表示。
关于 ctts 的生成,可以参看 srs/trunk/src/kernel/srs_kernel_mp4.cpp::SrsMp4SampleManager::write_track() 函数 pts、dts、cts 满足公式:pts - dts = cts。
2.19 stss(sync sample box)
它包含media中的关键帧的sample表。关键帧是为了支持随机访问。如果此表不存在,说明每一个sample都是一个关键帧。
mp4_29.png
一个视频示例如下:
mp4_30.png
2.20 stsc/stz2(sample to chunk box)
存储了该 track 中每个 sample 与 chunk 的映射关系。
mp4_31.png
一个音频示例如下:
mp4_32.png
第一组 chunk 的 first_chunk 序号为 1,每个 chunk 的 sample 个数为 1,因为第二组 chunk 的 first_chunk 序号为 2,可知第一组 chunk 中只有一个 chunk。
第二组 chunk 的 first_chunk 序号为 2,每个 chunk 的 sample 个数为 2,因为第三组 chunk 的 first_chunk 序号为 24,可知第二组 chunk 中有 22 个 chunk,有 44 个 sample。
这个并不是说,这个视频流只有3个sample,也就是只有3帧,不可能的,而是第三,第四行省略了,也就是说,第三跟第四,等等,后面的chunk 里面都只有1个sample,跟第二个chunk一样。本视频流有239个chunk。因为本视频流一共240帧,第一个chunk里面有2帧,后面的都是1帧,所以计算出来只有239个chunk。
2.21 stsz(sample size box)
包含sample的数量和每个sample的字节大小,这个box相对来说体积比较大的。表明视频帧或者音频帧大小,FFmpeg 里面的AVPacket 的size 数据大小,就是从这个box中来的。
mp4_33.png
2.22 stco/co64(chunk offset box)
Chunk Offset表存储了每个chunk在文件中的位置,这样就可以直接在文件中找到媒体数据,而不用解析box。
需要注意的是一旦前面的box有了任何改变,这张表都要重新建立。
mp4_34.png
stco 有两种形式,如果你的视频过大的话,就有可能造成 chunkoffset 超过 32bit 的限制。所以,这里针对大 Video 额外创建了一个 co64 的 Box。它的功效等价于 stco,也是用来表示 sample 在 mdat box 中的位置。只是,里面 chunk_offset 是 64bit 的。
需要注意,这里 stco 只是指定的每个 Chunk 在文件中的偏移位置,并没有给出每个 Sample 在文件中的偏移。想要获得每个 Sample 的偏移位置,需要结合 Sample Size box 和 Sample-To-Chunk 计算后取得。
2.23 udta(user data box)
用户自定义数据。
2.24 free(free space box)
“free”中的内容是无关紧要的,可以被忽略。该box被删除后,不会对播放产生任何影响。
Ftyp可以是free或skip。
2.25 mdat(media data box)
mdat就是具体的编码后的数据。
mdat 也是一个 box,拥有 box header 和 box body。
mdat 可以引用外部的数据,参见 moov --> udta --> meta,这里不讨论,只讨论数据存储在本文件中的形式。
对于 box body 部分,采用一个一个 samples 的形式进行存储,即一个一个音频帧或视频帧的形式进行存储。
码流组织方式采用 avcc 格式,即 AUD + slice size + slice 的形式。
3.参考资料
一文读懂MP4封装格式
封装格式分析-MP4
音视频封装:MP4结构概述和分析工具推荐
MP4 封装格式
5分钟入门MP4文件格式