打包格式--1:FLV 格式详细说明打包格式--2:MP4 格式详细说明打包格式--3:TS 格式详细说明打包格式--4:PS 格式详细说明
1.MP4格式概述
1.1 简介
MP4 或MPEG-4 No. 14 Part (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中的原子,这意味着MP4文件是由Boxes组成的。可以理解为一个数据块,由Header+Data组成。 Data可以存储媒体元数据和实际的音视频流数据。 Box可以直接存储数据块,但也可以包含其他类型的Box。我们称这种盒子也叫集装箱盒子。
(2)Sample简单理解为抽样。对于视频来说,可以理解为一帧数据。一帧音频数据是固定周期的音频数据,可以由多个Sample数据组成。简而言之:存储媒体数据的单位是样本。
(3)Track代表样本集合。对于媒体数据来说,它是视频序列或音频序列。我们常说的音轨和视频轨就可以类比这个概念。当然,除了Video Track和Audio Track之外,还可以有非媒体数据,比如Hint Track。这类Track不包含媒体数据,但可以包含一些将其他数据封装成媒体数据的指令信息或字幕信息。简单来说:轨道是电影中可以独立操作的媒体单元。
(4)Chunk由轨道的多个连续样本组成的单元称为块。每个块在文件中都有一个偏移量。整个偏移量是从文件的开头开始计算的。在该块内,样本被连续存储。
这样就可以理解,一个MP4文件中有多个Track,一个Track是由多个Chunk组成的。每个块包含一组连续的样本。正是由于上述概念的定义,使得MP4封装格式更容易实现灵活、高效、开放的特点,所以一定要仔细理解。
2.MP4整体结构
2.1 MP4结构概览
MP4 格式是一种盒格式。盒子容器包含盒子子容器,盒子子容器包含盒子子容器。
mp4_1.PNG 一个盒子由两部分组成:盒子头和盒子体。
box header:盒子元数据,例如盒子类型、盒子大小。盒体:盒子的数据部分。实际存储的内容与box类型有关,例如mdat的body部分存储的媒体数据。
在框标题中,只有类型和大小是必填字段。当size==1时,表示存在largesize字段。如果size==0,则表示该盒子是文件的最后一个盒子。在某些框中,还有版本和标志字段。这样的盒子称为全盒子。当盒体内嵌套有其他盒子时,这样的盒子称为容器盒。
mp4box 图示如下:其中:ftyp(文件类型框):文件头,记录一些兼容性信息moov(电影框):记录媒体信息mdat(媒体数据):媒体负载完整的Box结构:mp4_2.png
每个Box携带的数据内容如下:
2.2 Box结构
MP4封装格式使用称为盒的结构来组织数据。结构如下:
+-+-+-+-+-+-+-+-+-+-+
|标题|身体|
+-+-+-+-+-+-+-+-+-+-+所有其他框在语法上都继承自这个基本框结构。
2.2.1 box header
箱体分为普通箱体和全箱体。
(1)普通的box header结构如下:
字段类型描述size4 字节整个框的大小,包括框标头type4 字节4 个ascii 值。如果是"uuid",则表示该框是用户定义的,可以忽略。 Large size8 仅当Bytessize=1 时可用字段用于扩展,例如mdat box 将需要此字段(2) fullbox 在上述基础上添加2 个新字段:
字段类型描述version1 字节版本号flags3 字节标识
2.2.2 box body
一个盒子可能包含多个其他盒子。这种类型的盒子称为集装盒。因此,盒体可以是特定盒型,也可以是其他盒体。
虽然Box 的种类很多,大概有70 多种,但并不是所有的都是必需的。一般来说,MP4文件包含必要的Box和一些可选的Box。我使用MP4info 之类的工具来分析MP4 文件。具体Box如下所示:
mp4_3.png
通过上述工具分析的结果,我们大概可以总结出MP4的以下特点:
MP4文件是由一个个的Box组成的。盒子还可以相互嵌套,排列紧凑,没有太多冗余数据。 Box类型不多,主要由必备的ftyp、moov、mdat、free、udta组成。一定是由盒子组成的,即使去掉这两个盒子,也不会对音视频的播放产生影响。 Moov一般存储媒体元数据,比较复杂且嵌套较深。后面会详细解释各个框的字段含义和组成。
2.3 ftyp(File Type Box)
ftyp通常出现在文件的开头,表示mp4文件使用的标准规格:
字段类型描述major_brand4 字节主要版本minor_version4 字节次要版本compatible_brands[]4 字节指定兼容版本。注意,该字段是一个列表,即可以包含多个4字节版本号。示例如下:
mp4_4.png
mp4_5.png
2.4 moov(Movie Box)
moov 是一个容器盒子,一个文件中只有一个,它包含的所有盒子都是用来描述媒体信息(元数据)的。 moov 的位置可以紧接在ftyp 之后出现,也可以出现在文件末尾。既然是一个容器盒子,那么除了盒子头之外,它的盒子主体就是其他的盒子。子Box:mvhd(moov header):用于简要描述所有媒体共享的一些信息。 trak:track,轨道,用于描述音频流或视频流信息。可以有多个轨道。上面出现了两次,分别表示1个音频流和1个视频流。 udta(用户数据):用户定义,可以忽略。一个示例如下:(1) 结构
mp4_6.png
(2) 数据
mp4_7.png
(3) 配料
mp4_8.png
子框:mvhd
用于简要描述所有媒体共享的一些信息。
mp4_9.png
子盒:trak
track,轨道,用于描述音频流或视频流信息。可以有多个轨道。如上图,出现了两次,分别代表一个音频流和一个视频流。
mp4_10.png
2.5 mvhd(Movie Header Box)
mvhd 作为媒体信息的一个header 出现(注意这个header 不是box header,而是moov 媒体信息的header),用来描述所有媒体共享的一些基本信息。
mvhd的语法继承自fullbox。请注意,以下示例中的版本和标志字段属于fullbox 标头。
Box Body:mp4_11.png
2.6 trak(track)
trak box是一个容器box,它的子box包含了轨道的媒体信息。一个mp4文件可以包含多个曲目,并且各个曲目是独立的。 trak 框用于描述每个媒体流。一般有两个trak,分别对应音频流和视频流。一个示例如下:mp4_12.png 其中:
tkhd(轨道头框):用于简要描述媒体流的信息,如时长、宽度等mdia(媒体框):用于详细描述媒体流的信息edts(编辑框):子box是elst(Edit List Box),用于偏移某首曲目的时间戳。
2.7 tkhd(track header box)
tkhd作为媒体信息的头出现(注意这个头不是box头,而是track媒体信息的头),用于描述track的一些基本信息。 tkhd 语法继承自fullbox。请注意,以下示例中的版本和标志字段属于fullbox 标头。
Box Body:mp4_13.png
2.8 edts(edit Box)
下面有一个elst(编辑列表框),用于偏移某个曲目的时间戳。看一下一些字段:
segment_duration:表示编辑段的时长,单位为Movie Header Box (mvhd)中的时间刻度,即segment_duration/timescale=实际时长(单位s)media_time:表示编辑段的开始时间,单位为s轨道中媒体的数量标题框(mdhd) 中的时间刻度以单位为单位。如果值为-1 (FFFFFF),则表示空编辑。轨道中的最后一次编辑不能为空。media_rate:如果编辑段的速率为0,则该编辑段相当于“停留”,即屏幕停止。图片将在media_time 点处停止并持续period_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)
定义了轨道媒体类型和样本数据,并描述了样本信息。它是一个容器盒,里面必须包含mdhd、hdlr和minf。一个示例如下:mp4_14.png
其中: mdhd(Media Header Box):用于简单描述本次媒体流的信息。 hdlr(Handler Reference Box):主要定义轨道类型。 stbl(媒体信息框):用于描述该媒体流的解码相关信息和音视频位置信息。
2.10 mdhd(Media Header Box)
mdhd作为媒体信息的头部出现(注意这个头部不是box头部,而是媒体信息的头部),用来描述媒体的一些基本信息。 mdhd 和tkhd 的内容大致相同。然而,tkhd通常会为指定的轨道设置相关的属性和内容。 MDHD 是为独立媒体设置的。 mdhd 语法继承自fullbox。请注意,以下示例中出现的版本和标志字段属于fullbox 标头。
箱体:
mp4_15.png
注:timescale与mvhd中的timescale相同,但需要注意的是,虽然含义相同,但数值可能不同。下面stts、ctts等时间戳的计算都是基于mdhd中的时间尺度。
2.11 hdlr(Handler Reference Box)
主要解释媒体播放过程信息。声明当前轨道的类型和相应的处理程序。 hdlr 语法继承自fullbox。请注意,以下示例中出现的版本和标志字段属于fullbox 标头。
Box Body:mp4_16.png
2.12 minf(Media Information box)
解释轨道媒体数据的处理程序特定信息。媒体处理程序使用此信息将媒体时间映射到媒体数据并对其进行处理。 minf也是一个容器盒子,里面需要注意的内容是stbl,这也是moov中最复杂的部分。通常,“minf”包含标题框、“dinf”和“stbl”。其中,表头框分为“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”为数据信息框,“stbl”为样本表框。
mp4_17.png
2.13 *mhd (Media Info Header Box)
可分为“vmhd”、“smhd”、“hmhd”和“nmhd”。例如,视频类型为vmhd,音频类型为smhd。
(1)vmhd图形模式:视频合成模式,为0时复制原图,否则用opcolor合成。 opcolor:图形模式使用的一组(红、绿、蓝)。
(2)smhd平衡:立体声平衡,[8.8]格式值,一般0代表中,-1.0代表所有左声道,1.0代表所有右声道。
2.14 dinf(Data Information Box)
描述了如何定位媒体信息,是一个容器盒。 “dinf”一般包含“dref”(data reference box)。 “dref”会包含几个“url”或“urn”,这些框形成一个表来定位轨迹数据。简单来说,轨道可以分为若干段,每个段可以根据“url”或“urn”指向的地址获取数据。这些片段的序列号将在示例描述中使用以形成完整的轨道。通常,当数据完全包含在文件中时,“url”或“urn”中的位置字符串为空。
2.15 stbl(Sample Table Box)
在介绍stbl box之前,我们需要介绍一下mp4中定义的sample和chunk:
sample:ISO/IEC 14496-12 定义样本不能共享相同的时间戳。因此,在音视频轨道中,一个样本代表一个视频或音频帧。chunk:多个样本的集合。事实上,在音频和视频轨道中,块与样本一一对应。
mp4_18.png stbl box是一个容器box,是整个轨道中最重要的box。其子框描述了媒体流的解码相关信息、音视频位置信息、时间戳信息等。
MP4文件的媒体数据部分在mdat框中,stbl包含这些媒体数据的索引和时间信息。
一个示例如下:mp4_19.png
其中:stsd(样本描述框):存储编码类型和初始化解码器所需的信息,与具体编解码器类型相关。 stts(time to Sample box):存储轨道的每个样本与dts之间的时间映射关系。 stss(同步样本框):对于视频轨道,关键帧所属样本的序列号。 ctts(composition time tosample box):存储轨道中每个样本的cts和dts之间的时间差。 stsc/stz2(sample to chunk box):存储轨道中每个样本和chunk之间的映射关系。 stsz(样本大小框):存储轨道中每个样本的字节大小。 stco/co64(chunk offset box):存储文件中磁道中每个chunk的偏移量。
2.16 stsd(sample description box)
主要存储编码类型以及初始化解码器所需的信息。这里以视频为例,其中包含subbox:avc1,表明是H264视频。
mp4_20.png
2.16.1 h264 stsd
对于h264视频,典型结构如下:
mp4_21.png
上面(只列出了avc1和avcC,其他框可以忽略):
avc1,是avc/h264/mpeg-4part 10视频编解码格式的名称,是一个容器盒子,但盒子体内也携带着自己的信息。
Box Body:mp4_22.pngavcC(AVC视频流定义框),存储sps pps,ISO/IEC 14496-15中定义的AVCDecoderConfigurationRecord结构
mp4_23.png 注意:在srs中,请参考srs/trunk/src/kernel/srs_kerner_codec.cpp:SrsFormat:avc_demux_sps_pps()函数来解析avcc/AVCDecoderConfigurationRecord结构体。
2.16.2 aac stsd
对于aac音频,典型结构如下:
从mp4_24.png可以看到,aac stsd的结构比较复杂,有很多box。事实上,在ISO/IEC 14496-3中,定义了AudioSpecificConfig类型,aac stsd结构体的主要信息来自于AudioSpecificConfig。
不做详细分析,可以参考srs:
解析srs/trunk/src/kernel/srs_kernel_codec.cpp:SrsFormat:audio_aac_sequence_header_demux()函数的AudioSpecificConfig结构封装了srs/trunk/src/kernel/srs_kernel_mp4.cpp:SrsM p4的aac stsd结构Encoder:flush() 函数
2.17 stts(time to sample box)
将此轨道的每个样本存储到dts 时间映射关系。包含表的压缩版本,通过该表可以将解码时间映射到样本序列号。表中的每一项是连续相同的解码时间增量(Decode Delta)的数量和解码时间增量。通过累积时间增量,可以创建完整的采样时间表。
mp4_25.png为了保存条目数,这里使用了压缩存储,即sample_count个连续样本。如果sample_delta持续时间相同,则可以用一个条目来表示。
一个音频 track 的示例如下:mp4_26.png一个视频 track 的示例如下:mp4_27.png
2.18 ctts(composition time to sample box)
存储该轨道中每个样本的pts 和dts 之间的时间差(cts=pts - dts):如果视频只有I 帧和P 帧,则这个的ctts表是不需要的,因为解码顺序和显示顺序是一致的,但是如果视频中有B帧,就需要ctts。
mp4_28.png注意:当dts 和pts 不同时,此框必须存在。如果相同,则不需要包含此框。如果box version=0,则表示所有样本都满足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)
它包含媒体中关键帧的示例表。关键帧用于支持随机访问。如果该表不存在,则每个样本都是一个关键帧。
mp4_29.png一个视频示例如下:mp4_30.png
2.20 stsc/stz2(sample to chunk box)
存储轨道中每个样本与chunk之间的映射关系。
mp4_31.png一个音频示例如下:mp4_32.png 第一组chunk的first_chunk序号为1,每个chunk中的样本数为1。由于第二组chunk的first_chunk序号为2,因此可得看到第一组块中只有一个块。第二组chunk的first_chunk序号为2,每个chunk中的样本数为2。因为第三组chunk的first_chunk序号为24,所以可以看出有22个chunk,44个样本在第二组块中。这并不是说这个视频流只有3个样本,也就是只有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.png2.22 stco/co64(chunk offset box)
Chunk Offset表存储了每个chunk在文件中的位置,这样就可以直接在文件中找到媒体数据,而不用解析box。需要注意的是一旦前面的box有了任何改变,这张表都要重新建立。好了,文章到此结束,希望可以帮助到大家。
【高效数据封装与格式化策略解析】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
封装格式确实是很重要的,能让人读代码更容易懂!
有18位网友表示赞同!
imparare a usare un formato di imballaggio può essere una grande abilità!
有15位网友表示赞同!
有时候会觉得编写的代码看着乱糟糟的,封装格式可以帮助梳理一下。
有6位网友表示赞同!
学习新的封装格式需要时间,但效果还是挺明显的!
有11位网友表示赞同!
不同语言的封装格式可能会有所区别,要根据情况来选择合适的格式。
有9位网友表示赞同!
使用好封装格式可以提升代码的可阅读性!
有19位网友表示赞同!
好的封装格式能让代码更清晰易懂,合作开发的时候非常有用!
有13位网友表示赞同!
现在很多开发框架都自带了特定的封装格式,要注意学习和应用!
有9位网友表示赞同!
除了美观,封装格式还能帮助我们更好的维护和更新代码。
有6位网友表示赞同!
对新手来说,封装格式可能有点难以理解,最好找一些教程来学习!
有9位网友表示赞同!
想写出高质量的代码,封装格式是必不可少的!
有15位网友表示赞同!
一个好的封装格式能让代码就像是一份精美的报告一样易于阅读!
有11位网友表示赞同!
使用不同的工具可以帮助我们更轻松地进行代码封装。
有13位网友表示赞同!
学习编程语言的同时,也要关注对应的封装格式规范!
有9位网友表示赞同!
封装格式不仅限于代码本身,也可以应用于数据结构的组织!
有11位网友表示赞同!
好的封装格式可以让人更容易理解代码的功能和逻辑!
有13位网友表示赞同!
封装格式能够提高软件的可维护性和可扩展性!
有9位网友表示赞同!
学习好的封装格式是一件值得投资的事情!
有17位网友表示赞同!
想要成为一名优秀的程序员,掌握良好的编码习惯和封装格式非常重要!
有9位网友表示赞同!