MP4文件格式的相关内容

参考链接

MP4文件格式相关内容

  • MP4文件由许多box组成,每个box包含不同的信息, 这些box以树形结构的方式组织。
  • 以下是主要box的简要说明:

  • 根节点之下,主要包含三个节点:ftyp、moov、mdat。
    • ftyp:文件类型。描述遵从的规范的版本。
    • moov box:媒体的metadata信息,保存了音视频数据的时空信息。
    • mdat:具体的媒体数据。
    • 说明:在 mp4 中默认写入字节序是 Big-Endian的。

2. mp4文件基本信息

分析mp4文件的工具:

  • mp4box.js:一个在线解析mp4的工具。
  • bento4:包含mp4dump、mp4edit、mp4encrypt等工具。
  • MP4Box:类似于bento4,包含很全面的工具。
  • mp4info.exe: windows平台图形界面展示mp4基本信息的工具。

  • mvhd针对整个影片,tkhd针对单个track,mdhd针对媒体,vmhd针对视频,smhd针对音频,可以认为是从 宽泛 > 具体,前者一般是从后者推导出来的。 

mp4文件基本信息

  • audio信息:
    • smplrate:sample rate(采样率)。
    • channel:通道个数。
    • bitrate:比特率。
    • audiosamplenum:音频sample的个数。
  • video信息:
    • width、height:视频的宽/高。
    • bitrate:比特率(码率),秒为单位。等于视频总的大小/时长。
    • frames:视频帧数。
    • fps:帧率(frame per second)。
    • total_time:时间长度,ms为单位。等于duration/timescale。
    • timescale:时间的粒度,1000表示1000个单位为1s。
    • duration:时间粒度的个数。
    • videosamplenum:视频sample的个数。

3. 封装格式重要概念

box

  • mp4文件由若干个box组成。下面是box结构的一个示意图。

  • box由header和body组成,header指明box的size和type。size是包含box header的整个box的大小。
  • box type,通常是4个ASCII码的字符如“ftyp”、“moov”等,这些box type都是已经预定义好的,表示固定的含义。如果是“uuid”,表示该box为用户自定义扩展类型,如果box type是未定义的,应该将其忽略。
  • 如果header中的size为1,则表示box长度需要更多的bits位来描述,在后面会有一个64bits位的largesize用来描述box的长度。如果size为0,表示该box为文件的最后一个box,文件结尾(同样只存在于“mdat”类型的box中)。
  • 只有“mdat”类型的box才可能会用到large size
  • size后面紧跟着的32位为box type,一般是4个字符,如“ftyp”、“moov”等,这些box type都是预定义好的,分别表示固定的意义。如果是“uuid”,表示box为用户的扩展类型,如果未定义box type 需要将其忽略
  • box中可以包含box,这种box称为container box。
  • box分为两种,Box和Fullbox。FullBox 是 Box 的扩展,Header 中增加了version 和 flags字段,分别定义如下:
aligned(8) class Box (unsigned int(32) boxtype,
    optional unsigned int(8)[16] extended_type) {
    unsigned int(32) size;
    unsigned int(32) type = boxtype;
    if (size==1) {
        unsigned int(64) largesize;
    } else if (size==0) {
    // box extends to end of file
    }
    if (boxtype==‘uuid’) {
        unsigned int(8)[16] usertype = extended_type;
    }
}
  • FullBox有version和flags字段,
aligned(8) class FullBox(unsigned int(32) boxtype, unsigned int(8) v, bit(24) f)
extends Box(boxtype) {
    unsigned int(8) version = v;
    bit(24) flags = f;
}

MP4box

ftyp box

  • 该box有且只有1个,并且只能被包含在文件层,而不能被其他box包含。该box应该被放在文件的最开始,指示该MP4文件应用的相关信息。
  • “ftyp” body依次包括1个32位的major brand(4个字符),1个32位的minor version(整数)和1个以32位(4个字符)为单位元素的数组compatible brands。这些都是用来指示文件应用级别的信息。
  • major_brand:比如常见的 isom、mp41、mp42、avc1、qt等。它表示“最好”基于哪种格式来解析当前的文件。举例,major_brand 是 A,compatible_brands 是 A1,当解码器同时支持 A、A1 规范时,最好使用A规范来解码当前媒体文件,如果不支持A规范,但支持A1规范,那么,可以使用A1规范来解码;
  • minor_version:提供 major_brand 的说明信息,比如版本号,不得用来判断媒体文件是否符合某个标准/规范;
  • compatible_brands:文件兼容的brand列表。比如 mp41 的兼容 brand 为 isom。通过兼容列表里的 brand 规范,可以将文件 部分(或全部)解码出来;
  • 在实际使用中,不能把 isom 做为 major_brand,而是需要使用具体的brand(比如mp41),因此,对于 isom,没有定义具体的文件扩展名、mime type。

  • 下面是常见的几种brand,以及对应的文件扩展名、mime type,更多brand可以参考 这里 。

补充

关于AVC/AVC1

  • 在讨论 MP4 规范时,提到AVC,有的时候指的是“AVC文件格式”,有的时候指的是"AVC压缩标准(H.264)",这里简单做下区分。
    • AVC文件格式:基于 ISO基础文件格式 衍生的,使用的是AVC压缩标准,可以认为是MP4的扩展格式,对应的brand 通常是 avc1,在MPEG-4 PART 15 中定义。
    • AVC压缩标准(H.264):在MPEG-4 Part 10中定义。
    • ISO基础文件格式(Base Media File Format) 在 MPEG-4 Part 12 中定义。

FREE(可选的)

  • free是可选的,如果存在,则通常出现在moov与mdat之间,即moov-free-mdat。
  • free中的数据通常为全0,其作用相当于占位符,在实时拍摄视频,moov数据增多时分配给moov使用。
  • 因为设备录制视频时并不能预先知道视频数据大小,如果moov在mdat之前,随着拍摄mdat的数据会增加,moov数据也会增多,如果没有free预留的空间,则要不停的向后移动mdat数据以腾出moov空间。
  •    “free”中的内容是无关紧要的,可以被忽略。该box被删除后,不会对播放产生任何影响。

moov box

  • moov box 是一个 container box 该box包含了文件媒体的元数据信息,具体内容信息由子box诠释。同File Type Box一样,该box有且只有一个,且只被包含在文件层。一般情况下,“moov”会紧随“ftyp”出现。
  • 可以看到这个demo 中有 mvhd、trak、udta 三种 box 一般情况下 “moov”中会包含1个“mvhd”和若干个“trak”。其中“mvhd”为header box,一般作为“moov”的第一个子box出现。“trak”包含了一条音、视频轨/流/track的相关信息,也是一个container box。
  • 该box是解析MP4文件里面最重要的一个box,它包含了音视频数据的编码格式、音视频数据样本,chunks的大小、存储位置也即偏移offset、时间戳单位、DTS,CTS(PTS),解码时间、显示时间等等…
  • moov box中记录的每帧音视频数据位置信息,实际上都在mdat box中,通过解析moov box来获取到每帧音视频数据具体位置后,使得播放器能方便的拖拉进度条。

mvhd box (Movie Header Box)

  • mvhd 描述了与具体音频或视频流无关的文件整体信息,其中的duration/timescale的值即为单位为秒的媒体时长。
  • 创建时间、修改时间、时间度量标尺、可播放时长等信息

字段 字节数 意义
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 视频变换矩阵,一般忽略不计
pre-defined 24
next track id 4 下一个track使用的id号

补充

  •  timescale:一秒包含的时间单位(整数)。举个例子,如果timescale等于1000,那么,一秒包含1000个时间单位(后面track等的时间,都要用这个来换算,比如track的duration为10,000,那么,track的实际时长为10,000/1000=10s);
  • next_track_ID:32位整数,非0,一般可以忽略不计。当要添加一个新的track到这个影片时,可以使用的track id,必须比当前已经使用的track id要大。也就是说,添加新的track时,需要遍历所有track,确认可用的track id;

trak box (Track Box)

  • trak也是一个container box,其子box包含了该track的媒体数据引用和描述。一个MP4文件中的媒体可以包含多个track,且至少有一个track,这些track之间彼此独立,有自己的时间和空间信息。“trak”必须包含一个“tkhd”和一个“mdia”,此外还有很多可选的box(略)
  • track表示一些sample集合,对于媒体数据来说,track表示一个视频或者音频序列
  • 一系列子box描述了每个媒体轨道的具体信息
  • hint track并不包含媒体数据,而是包含将一些其他数据track打包成流媒体的指示信息
  • sample对于非hint track来说,video sample 表示视频帧,或者一组连续视频帧,audio sample即为一段连续的压缩音频,统称为sample,对于hint track,sample定义了一个或者多个流媒体的格式
  • sample table指明sample的时序和物理布局的表
  • chunk 一个track的几个sample组成的单元
  • MP4文件中 媒体内容在moov的box中,一个moov包含多个track,每个track就是一个随时间变化的媒体序列,track里每个时间单位是一个sample,sample是按照时间顺序排列。注意,一帧音频可以分解为多个音频sample,所以音频一般用sample作为单位,而不用帧

tkhd(track header box)

  • tkhd 描述的该track的,如果是视频会有宽、高信息、 还有文件创建时间、修改时间等。

字段 字节数 意义
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的时间长度;当前track的完整时长(需要除以timescale得到具体秒数)
reserved 8 保留位
layer 2 视频层,默认为0,值小的在上层;视频轨道的叠加顺序,数字越小越靠近观看者,比如1比2靠上,0比1靠上
alternate group 2 track分组信息,默认为0表示该track未与其他track有群组关系;当前track的分组ID,alternate_group值相同的track在同一个分组里面。同个分组里的track,同一时间只能有一个track处于播放状态。当alternate_group为0时,表示当前track没有跟其他track处于同个分组。一个分组里面,也可以只有一个track
volume 2 [8.8] 格式,如果为音频track,1.0(0x0100)表示最大音量;否则为0
reserved 2 保留位
matrix 36 视频变换矩阵
width 4
height 4 高,均为 [16.16] 格式值,与sample描述中的实际画面大小比值,用于播放时的展示宽高

补充

  • flags:按位或操作获得,默认值是7(0x000001 | 0x000002 | 0x000004),表示这个track是启用的、用于播放的 且 用于预览的。
    • Track_enabled:值为0x000001,表示这个track是启用的,当值为0x000000,表示这个track没有启用;
    • Track_in_movie:值为0x000002,表示当前track在播放时会用到;
    • Track_in_preview:值为0x000004,表示当前track用于预览模式;

mdia (Track Media Structure)  

  • mdia box 描述了这条音视频轨/流(trak)的媒体数据样本的主要信息,对播放器来说是一个很重要的box
  • “mdia”也是个container box,其子box的结构和种类还是比较复杂的。先来看一个“mdia”的实例结构树图。

扫描二维码关注公众号,回复: 14625796 查看本文章

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

mdhd (Media Header Box)

  • 当前音/视频轨/流(trak)的总体信息, 该box中有duration字段和timescale字段,duration/timescale的值即为当前流的时长。
  • hdlr 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 同前表
duration 4 track的时间长度
language 2 媒体语言码。最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义)
pre-defined 2

 Handler Reference Box(hdlr)

  • “hdlr”解释了媒体的播放过程信息,该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的取值包括:
    • vide(0x76 69 64 65),video track;
    • soun(0x73 6f 75 6e),audio track;
    • hint(0x68 69 6e 74),hint track;
  • name为utf8字符串,对handler进行描述,比如 L-SMASH Video Handler(参考 这里)。
  • “hdlr”的字节实例如下图,各字段已经用颜色区分开:

  • stsd box的子box用于保存该流的编码类型

  • avcC box指定了该流的编码类型为H264,储了解码所需的SPS、PPS信息。
  • stsc stsz stco三个box用于保存每帧视频或音频数据在文件中的保存位置。
  • stts stss ctts三个box用于保存媒体数据和时间戳的对应关系。
  • 在同级的stbl的样本表box里面可以查到对应的样本 描述信息(stsd),时序信息(stts),样本的大小信息(stsz),样本到chunk的映射信息(stsc),chunk的位置信息(stco)等等 

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。下面分别介绍。
  •    下图为“minf”部分字节实例,其中红色为box header,蓝色为“smhd”,绿色为“dinf”,黄色为一部分“stbl”。

Media Information Header Box(vmhd、smhd、hmhd、nmhd)

  • Video Media Header 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}
  • Sound Media Header Box(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
  • Hint Media Header Box(hmhd)  略
  • Null Media Header Box(nmhd)  非视音频媒体使用该box,略

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数据已包含在文件中。

Sample Table Box(stbl)

  •    “stbl”几乎是普通的MP4文件中最复杂的一个box了,首先需要回忆一下sample的概念。sample是媒体数据存储的单位,存储在media的chunk中,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”包含了编码的信息,其存储的信息随媒体类型不同而不同。

Sample Description Box(stsd)

  • 给出视频、音频的编码、宽高、音量等信息,以及每个sample中包含多少个frame   
  • 存储了编码类型和初始化解码器需要的信息。有与特定的track-type相关的信息,相同的track-type也会存在不同信息的情况如使用不一样的编码标准。
  • 结构如下:

  • box header和version字段后会有一个entry count字段,根据entry的个数,每个entry会有type信息,如“vide”、“sund”等,根据type不同sample description会提供不同的信息,例如对于video track,会有“VisualSampleEntry”类型信息,对于audio track会有“AudioSampleEntry”类型信息。
  • 视频的编码类型、宽高、长度,音频的声道、采样等信息都会出现在这个box中。

Time To Sample Box(stts)

  •    结构如下:

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

Sample Size Box(stsz)

  • 每个sample的size(单位是字节)   ,根据 sample_size 字段,可以知道当前track包含了多少个sample(或帧)。
  • 结构如下:

  • “stsz” 定义了每个sample的大小,包含了媒体中全部sample的数目和一张给出每个sample大小的表。这个box相对来说体积是比较大的。
  • 有两种不同的box类型,stsz、stz2。

stsz:

  • sample_size:默认的sample大小(单位是byte),通常为0。如果sample_size不为0,那么,所有的sample都是同样的大小。如果sample_size为0,那么,sample的大小可能不一样。
  • sample_count:当前track里面的sample数目。如果 sample_size==0,那么,sample_count 等于下面entry的条目;
  • entry_size:单个sample的大小(如果sample_size==0的话)

stz2:

  • field_size:entry表中,每个entry_size占据的位数(bit),可选的值为4、8、16。4比较特殊,当field_size等于4时,一个字节上包含两个entry,高4位为entry[i],低4位为entry[i+1];
  • sample_count:等于下面entry的条目;
  • entry_size:sample的大小。

Sample To Chunk Box(stsc)

  •    结构如下:

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

Sync Sample Box(stss)

  • 结构如下:

  

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

Chunk Offset Box(stco)

  • thunk在文件中的偏移   
  • 结构如下:

  • “stco” 定义了每个thunk在媒体流中的位置。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。在一个表中只会有一种可能,这个位置是在整个文 件中的,而不是在任何box中的,这样做就可以直接在文件中找到媒体数据,而不用解释box。需要注意的是一旦前面的box有了任何改变,这张表都要重新 建立,因为位置信息已经改变了。
  • 针对小文件、大文件,有两种不同的box类型,分别是stco、co64,它们的结构是一样的,只是字段长度不同。
  • chunk_offset 指的是在文件本身中的 offset,而不是某个box内部的偏移。
  • 在构建mp4文件的时候,需要特别注意 moov 所处的位置,它对于chunk_offset 的值是有影响的。有一些MP4文件的 moov 在文件末尾,为了优化首帧速度,需要将 moov 移到文件前面,此时,需要对 chunk_offset 进行改写。

PTS和DTS的计算

I P B 帧的概念

  • 在音视频中,为了提高压缩效率,会将每帧画面压缩为不同类型的视频帧数据。
  • I帧表示关键帧,包含有一帧画面的完整信息,解码时只需要本帧数据就可以解码出完整的一帧画面。
  • P帧表示前向参考帧,它保存了本帧与上一帧的差异信息,它不能单独解码,需要根据上一帧的画面加上本帧保存的差值来获取本帧的完整画面。
  • B帧为双向参考帧,它解码时需要依赖它之前和之后的帧来获取最终的画面
  • 因为B帧需要依赖它后面的帧来进行解码,所以它的解码顺序就必然和显示顺序不能保持一致,这时就需要解码时间戳(DTS)和显示时间戳(PTS)来共同决定一帧视频数据何时解码,然后何时显示了。
  • 举个例子
  • 一小段视频帧序列如下 :
    • type : I — B — B — P — B — B — P
    • PTS : 0.33 0.67 1.00 1.33 1.67 2.00 2.33
    • DTS : 0.00 0.67 1.00 0.33 1.67 2.00 1.33
    • PTS >= DTS
  • 根据mp4 stts和ctts 可以得到DTS和PTS

stts(Decoding Time to Sample Box)

  • stts 可以计算出每个sample的dts,其中sample_delta为该sample的dts相对于上一个smaple的差值,
  • stts包含了DTS到sample number的映射表,主要用来推导每个帧(sample)的时长。
  • 那么此样本数据的dts为 :   0   1000 2000 3000 4000 ···
  • entry_count:stts 中包含的entry条目数;
  • sample_count:单个entry中,具有相同时长(duration 或 sample_delta)的连续sample的个数。
  • sample_delta:sample的时长(以timescale为计量)

ctts(Composition Time to Sample Box)

  • Composition Time 构成时间目前我直接理解的PTS。。
  • ctts 有每个sample的构成时间(Composition Time)和解码时间(DTS)之间的差值(CTTS)即图中的composition_offset。
  • 如果不存在ctts,则代表该流不存在B帧,那么PTS就直接等于DTS。
  • 帧解码到渲染的时间差值,通常用在B帧的场景,对于存在B帧的视频来说,ctts就需要存在了。当PTS、DTS不相等时,就需要ctts了,公式为 CT(n) = DT(n) + CTTS(n) 。
  • 对于只有I帧、P帧的视频来说,解码顺序、渲染顺序是一致的,此时,ctts没必要存在。

timescale

  • 最后就是关于单位,你可以看到图中样本的单位都是以1000为单位浮动,实际上真实DTS和PTS时间是需要除以mdia/mdhd中的timescale。这里是30000。
  • 有了这些,我们就可以在ctts里面计算出pts了 :
 else if (box_type_equa(uint32_to_str(bh.type, sbuffer), "ctts")) {
        uint32_t version = 0;
        read_net_bytes_to_host_uint32(&box[8], &version);
        if(version != 0) {
            LOG_E("ctts unsupport version :%d ", version)
            return;
        }

        uint32_t entry_cnt = 0;
        read_net_bytes_to_host_uint32(&box[12], &entry_cnt);
        char buf[128] = {0};
        tree_childs_insert_with_val(tree, "version", uint32_to_ascii(version, buf));
        tree_childs_insert_with_val(tree, "entry_cnt", uint32_to_ascii(entry_cnt, buf));
        
        uint32_t i = 0, j = 0, num = 0, pos = 16;
        for (i = 0; i < entry_cnt; i++) {
            uint32_t sample_cnt;
            read_net_bytes_to_host_uint32(&box[pos], &sample_cnt);
            pos += 4;
        
            uint32_t sample_offset;
            read_net_bytes_to_host_uint32(&box[pos], &sample_offset);
            pos += 4;

            for (j = 0; j < sample_cnt; j++) {
                PushBack_Array(pts_array, At_Array(dts_array, num++) + sample_offset);
                float dt, pt = 0.0;
                printf("dts : %9.3f ms | pts : %9.3f ms | \n", At_Array(dts_array, num - 1) / (mdhd_time_scale * 1.0), At_Array(pts_array, num - 1) / (mdhd_time_scale * 1.0));
            }

stss (Sync Sample Box)

  • stss 里面存放了关键帧的序号(I帧),跳转时,需要从关键帧开始解码,否则会花屏。
  • 哪些sample是关键帧
  • mp4文件中,关键帧所在的sample序号。如果没有stss的话,所有的sample中都是关键帧。
  • entry_count:entry的条目数,可以认为是关键帧的数目;
  • sample_number:关键帧对应的sample的序号;(从1开始计算)


stsz (Sample Size Boxes):

  • 顾名思义,样本大小.

 

 stsc (Sample To Chunk Box):

  • 媒体数据的样本是被打包进chunks(块)的,chunks和样本(samples)的大小不固定,该box用于说明chunks关联样本的信息。
  • 每个thunk中包含几个sample
  • entry_count:有多少个表项(每个表项,包含first_chunk、samples_per_chunk、sample_description_index信息);
    • first_chunk 该入口第一个chunks的索引(index).
    • samples_per_chunk 样本数量/chunks.
    • sample_description_index:指向 stsd 中 sample description 的索引值(参考stsd小节);
  • sample 以 chunk 为单位分成多个组。chunk的size可以是不同的,chunk里面的sample的size也可以是不同的

前面描述比较抽象,这里看个例子,这里表示的是:

  • 序号1~15的chunk,每个chunk包含15个sample;
  • 序号16的chunk,包含30个sample;
  • 序号17以及之后的chunk,每个chunk包含28个sample;
  • 以上所有chunk中的sample,对应的sample description的索引都是1;
first_chunk samples_per_chunk sample_description_index
1 15 1
16 30 1
17 28 1

stco (Chunk Offset Box)

  • 描述每个chunks相对文件的偏移量。
  • 如图 第一个chunks即前10个样本(此例), samples.1起始地址为 423257, samples.1的地址则为 423257 + 140798 = 564055, 依此类推…
  • 有了这些即可计算出音视频的时间和空间信息了


mdat box

  • Meida Data Box 媒体数据box 位于顶层,定义是一个字节数组,用来存储媒体数据。该box数量可以为0个,也可以有多个(当媒体数据全部为外部文件引用时),数据直接跟在box type字段后面,具体数据结构的意义需要参考metadata(主要在sample table中描述)。
  • 实际媒体数据。我们最终解码播放的数据都在这里面
  • 该box包含于文件层,可以有多个,也可以没有(当媒体数据全部为外部文件引用时),用来存储媒体数据。数据直接跟在box type字段后面,具体数据结构的意义需要参考metadata(主要在sample table中描述)。

 

mehd(Movie Extends Header Box)

  • mehd是可选的,用来声明影片的完整时长(fragment_duration)。如果不存在,则需要遍历所有的fragment,来获得完整的时长。对于fmp4的场景,fragment_duration一般没办法提前预知。

trex(Track Extends Box)

  • 用来给 fMP4 的 sample 设置各种默认值,比如时长、大小等
  • 字段含义如下:

    • track_id:对应的 track 的 ID,比如video track、audio track 的ID;
    • default_sample_description_index:sample description 的默认 index(指向stsd);
    • default_sample_duration:sample 默认时长,一般为0;
    • default_sample_size:sample 默认大小,一般为0;
    • default_sample_flags:sample 的默认flag,一般为0;
    • 老版本规范里,前6位都是保留位,新版规范里,只有前4位是保留位。is_leading 含义不是很直观,下一小节会专门讲解下。
    • reserved:4 bits,保留位;
    • is_leading:2 bits,是否 leading sample,可能的取值包括:
      • 0:当前 sample 不确定是否 leading sample;(一般设为这个值)
      • 1:当前 sample 是 leading sample,并依赖于 referenced I frame 前面的 sample,因此无法被解码;
      • 2:当前 sample 不是 leading sample;
      • 3:当前 sample 是 leading sample,不依赖于 referenced I frame 前面的 sample,因此可以被解码;
    • sample_depends_on:2 bits,是否依赖其他sample,可能的取值包括:
      • 0:不清楚是否依赖其他sample;
      • 1:依赖其他sample(不是I帧);
      • 2:不依赖其他sample(I帧);
      • 3:保留值;
    • sample_is_depended_on:2 bits,是否被其他sample依赖,可能的取值包括:
      • 0:不清楚是否有其他sample依赖当前sample;
      • 1:其他sample可能依赖当前sample;
      • 2:其他sample不依赖当前sample;
      • 3:保留值;
    • sample_has_redundancy:2 bits,是否有冗余编码,可能的取值包括:
      • 0:不清楚是否存在冗余编码;
      • 1:存在冗余编码;
      • 2:不存在冗余编码;
      • 3:保留值;
    • sample_padding_value:3 bits,填充值;
    • sample_is_non_sync_sample:1 bits,不是关键帧;
    • sample_degradation_priority:16 bits,降级处理的优先级(一般针对如流传过程中出现的问题);

is_leading

  • 为方便讲解,下面的 leading frame 对应 leading sample,referenced frame 对应 referenced samle。
  • 以 H264编码 为例,H264 中存在 I帧、P帧、B帧。由于 B帧 的存在,视频帧的 解码顺序、渲染顺序 可能不一致。
  • mp4文件的特点之一,就是支持随机位置播放。比如,在视频网站上,可以拖动进度条快进。
  • 很多时候,进度条定位的那个时刻,对应的不一定是 I帧。为了能够顺利播放,需要往前查找最近的一个 I帧,如果可能的话,从最近的 I帧 开始解码播放(也就是说,不一定能从前面最近的I帧播放)。
  • 将上面描述的此刻定位到的帧,称作 leading frame。leading frame 前面最近的一个 I 帧,叫做 referenced frame。
  • 回顾下 is_leading 为 1 或 3 的情况,同样都是 leading frame,什么时候可以解码(decodable),什么时候不能解码(not decodable)?
  • 我没看懂

1、is_leading 为 1 的例子: 如下所示,帧2(leading frame) 解码依赖 帧1、帧3(referenced frame)。在视频流里,从 帧2 往前查找,最近的 I帧 是 帧3。哪怕已经解码了 帧3,帧2 也解不出来。

2、is_leading 为 3 的例子: 如下所示,此时,帧2(leading frame)可以解码出来。

moof

  • moof是个container box,相关 metadata 在内嵌box里,比如 mfhd、 tfhd、trun 等。

mfhd(Movie Fragment Header Box)

  • 结构比较简单,sequence_number 为 movie fragment 的序列号。根据 movie fragment 产生的顺序,从1开始递增。

traf(Track Fragment Box)

  • 对 fmp4 来说,数据被氛围多个 movie fragment。一个 movie fragment 可包含多个track fragment(每个 track 包含0或多个 track fragment)。
  • 每个 track fragment 中,可以包含多个该 track 的 sample。 每个 track fragment 中,包含多个 track run,每个 track run 代表一组连续的 sample。

tfhd(Track Fragment Header Box)

  • tfhd 用来设置 track fragment 中 的 sample 的 metadata 的默认值。
  • sample_description_index、default_sample_duration、default_sample_size 没什么好讲的,这里只讲解下 tf_flags、base_data_offset。
  • 首先是 tf_flags,不同 flag 的值如下(同样是求按位求或) :
    • 0x000001 base‐data‐offset‐present:存在 base_data_offset 字段,表示 数据位置 相对于整个文件的 基础偏移量。
    • 0x000002 sample‐description‐index‐present:存在 sample_description_index 字段;
    • 0x000008 default‐sample‐duration‐present:存在 default_sample_duration 字段;
    • 0x000010 default‐sample‐size‐present:存在 default_sample_size 字段;
    • 0x000020 default‐sample‐flags‐present:存在 default_sample_flags 字段;
    • 0x010000 duration‐is‐empty:表示当前时间段不存在sample,default_sample_duration 如果存在则为0 ,;
    • 0x020000 default‐base‐is‐moof:如果 base‐data‐offset‐present 为1,则忽略这个flag。如果 base‐data‐offset‐present 为0,则当前 track fragment 的 base_data_offset 是从 moof 的第一个字节开始计算;
    • sample 位置计算公式为 base_data_offset + data_offset,其中,data_offset 每个 sample 单独定义。如果未显式提供 base_data_offset,则 sample 的位置的通常是基于 moof 的相对位置。
    • 举个例子,比如 tf_flags 等于 57,表示 存在 base_data_offset、default_sample_duration、default_sample_flags。

  • base_data_offset 为 1263 (ftyp、moov 的size 之和为 1263)。

 trun(Track Fragment Run Box)

  • 前面听过,track run 表示一组连续的 sample,其中:
    • sample_count:sample 的数目;
    • data_offset:数据部分的偏移量;
    • first_sample_flags:可选,针对当前 track run中 第一个 sample 的设置;
  • tr_flags 如下,大同小异:
    • 0x000001 data‐offset‐present:存在 data_offset 字段;
    • 0x000004 first‐sample‐flags‐present:存在 first_sample_flags 字段,这个字段的值,只会覆盖第一个 sample 的flag设置;当 first_sample_flags 存在时,sample_flags 则不存在;
    • 0x000100 sample‐duration‐present:每个 sample 都有自己的 sample_duration,否则使用默认值;
    • 0x000200 sample‐size‐present:每个 sample 都有自己的 sample_size,否则使用默认值;
    • 0x000400 sample‐flags‐present:每个 sample 都有自己的 sample_flags,否则使用默认值;
    • 0x000800 sample‐composition‐time‐offsets‐present:每个 sample 都有自己的 sample_composition_time_offset;
    • 0x000004 first‐sample‐flags‐present,覆盖第一个sample的设置,这样就可以把一组sample中的第一个帧设置为关键帧,其他的设置为非关键帧;
  • 举例如下,tr_flags 为 2565。此时,存在 data_offset 、first_sample_flags、sample_size、sample_composition_time_offset。

补充

  • moofbox,这个box是视频分片的描述信息。并不是MP4文件必须的部分,但在我们常见的可在线播放的MP4格式文件中(例如Silverlight Smooth Streaming中的ismv文件)确是重中之重
  • mfra box,一般在文件末尾,媒体的索引文件,可通过查询直接定位所需时间点的媒体数据。
    • 附:Smooth Streaming中ismv文件结构,文件分为了多个Fragments,每个Fragment中包含moof和mdat。这样的结构符合渐进式播放需求。(mdat及其描述信息逐步传输,收齐一个Fragment便可播放其中的mdat)。
  • mp4和fmp4的区别
  • fMP4 跟普通 mp4 基本文件结构是一样的。普通mp4用于点播场景,fmp4通常用于直播场景。
    • 它们有以下差别:
      • 普通mp4的时长、内容通常是固定的。fMP4 时长、内容通常不固定,可以边生成边播放;
      • 普通mp4完整的metadata都在moov里,需要加载完moov box后,才能对mdat中的媒体数据进行解码渲染;
      • fMP4中,媒体数据的metadata在moof box中,moof 跟 mdat (通常)结对出现。moof 中包含了sample duration、sample size等信息,因此,fMP4可以边生成边播放;
    • 怎么判断mp4文件是普通mp4,还是fMP4呢?一般可以看下是否存在存在mvex(Movie Extends Box)。
      • 当存在mvex时,表示当前文件是fmp4(非严谨)。此时,sample相关的metadata不在moov里,需要通过解析moof box来获得。
  • sample:
    • video sample 即为一帧或者一组连续的视频帧
    • audio sample 即为一段连续的音频
    • sample table 指明sample时序和物理布局的表

猜你喜欢

转载自blog.csdn.net/CHYabc123456hh/article/details/125100494