网站首页 > 开源技术 正文
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子容器。
一个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结构:
每个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以下几个特点:
- 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 版本号 |
一个示例如下:
2.4 moov(Movie Box)
- moov是一个container box,一个文件只有一个,其包含的所有box用于描述媒体信息(metadata)。
- moov的位置可以紧随着 ftyp 出现,也可以出现在文件末尾。
- 由于是一个 container box,所以除了 box header,其 box body 就是其它的 box。
领音视频开发学习资料→C/C++音视频开发学习路线+资料 - 知乎
子Box:
- mvhd(moov header):用于简单描述一些所有媒体共享的信息。
- trak:即 track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。
- udta(user data):用户自定义,可忽略。
一个示例如下:
(1)结构
(2)数据
(3)成分
子Box:mvhd
??用于简单描述一些所有媒体共享的信息。
子Box:trak
??track,轨道,用于描述音频流或视频流信息,可以有多个轨道,如上出现了 2 次,分别表示一路音频和一路视频流。
2.5 mvhd(Movie Header Box)
??mvhd 作为媒体信息的 header 出现(注意此header不是box header,而是moov媒体信息的header),用于描述一些所有媒体共享的基本信息。
??mvhd 语法继承自fullbox,注意下述示例出现的version和flags字段属于fullbox header。
Box Body:
2.6 trak(track)
- trak box 是一个 container box,其子 box 包含了该 track 的媒体信息。
- 一个 mp4 文件可以包含多个 track,track之间是独立的,trak box 用于描述每一路媒体流。
- 一般情况下有两个trak,分别对应音频流和视频流。
一个示例如下:
其中:
- 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:
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。
一个示例如下:
其中:
- 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:
注:timescale同mvhd中的timescale,但是需要注意虽然意义相同,但是值有可能不同,下边stts,ctts等时间戳的计算都是以mdhd中的timescale。
2.11 hdlr(Handler Reference Box)
- 主要解释了媒体的播放过程信息。声明当前track的类型,以及对应的处理器(handler)。
- hdlr 语法继承自 fullbox,注意下述示例出现的 version 和 flags 字段属于 fullbox header。
Box Body:
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。
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 一一对应。
??stbl box是一个container box,是整个track中最重要的一个box,其子box描述了该路媒体流的解码相关信息、音视频位置信息、时间戳信息等。
??MP4文件的媒体数据部分在mdat box里,而stbl则包含了这些媒体数据的索引以及时间信息。
一个示例如下:
其中:
- 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的视频。
2.16.1 h264 stsd
??对于h264视频,典型结构如下:
其上(只列出 avc1 与 avcC,其余 box 可忽略):
- avc1,是 avc/h264/mpeg-4 part 10视频编解码格式的代称,是一个 container box,但是 box body 也携带自身的信息。
Box Body:
avcC(AVC Video Stream Definition Box),存储 sps && pps,即在 ISO/IEC 14496-15 中定义的 AVCDecoderConfigurationRecord 结构
注:在 srs 中,解析 avcc/AVCDecoderConfigurationRecord 结构解析参见 srs/trunk/src/kernel/srs_kerner_codec.cpp::SrsFormat::avc_demux_sps_pps() 函数。
2.16.2 aac stsd
??对于 aac 音频,典型结构如下:
可以看到,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表。
??这里为了节约条目的个数,采用了压缩存储的方式,即 sample_count 个连续的 sample 如果 sample_delta 时长一样,那么用一个条目就能表示了。
一个音频 track 的示例如下:
一个视频 track 的示例如下:
2.18 ctts(composition time to sample box)
- 存储了该 track 中,每个 sample 的 pts 与 dts 时间差(cts = pts - dts):
- 如果一个视频只有I帧和P帧,则ctts这个表就不需要了,因为解码顺序和显示顺序是一致的,但是如果视频中存在B帧,则需要ctts。
注意:
- 此 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都是一个关键帧。
一个视频示例如下:
2.20 stsc/stz2(sample to chunk box)
??存储了该 track 中每个 sample 与 chunk 的映射关系。
一个音频示例如下:
- 第一组 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中来的。
2.22 stco/co64(chunk offset box)
- Chunk Offset表存储了每个chunk在文件中的位置,这样就可以直接在文件中找到媒体数据,而不用解析box。
- 需要注意的是一旦前面的box有了任何改变,这张表都要重新建立。
stco 有两种形式,如果你的视频过大的话,就有可能造成 chunkoffset 超过 32bit 的限制。所以,这里针对大 Video 额外创建了一个 co64 的 Box。它的功效等价于 stco,也是用来表示 sample 在 mdat box 中的位置。只是,里面 chunk_offset 是 64bit 的。
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 的形式。
猜你喜欢
- 2024-09-30 「导入问题」大疆或苹果拍摄的 mov 视频无法导入Pr (Premiere)
- 2024-09-30 FLV、MP4、TS合成音视频实战(1)(ts文件合成视频)
- 2024-09-30 视频太大没法上传?视频压缩神器——小丸工具箱!
- 2024-09-30 深入剖析MediaCodec解码器的基本原理及使用「建议新手收藏」
- 2024-09-30 Excel催化剂开源-音视频文件元数据提取-分辨率,时长,采样率等
- 2024-09-30 推荐!Universal Media Server - 连接你的世界,畅享无限媒体
- 2024-09-30 Premiere技巧|视频文件的故障排除
- 2024-09-30 Qt/C++音视频开发59-使用mdk-sdk组件/原qtav作者/性能凶残跨平台
- 2024-09-30 从零开始打造云端AI管理调度平台(番外篇_1)文件自动入库更新
- 2024-09-30 Qt音视频开发14-mpv读取和控制(疾病预防控制综合治理平台)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)