MP4解析,包含moof

MP4解析块架构设计

    

1介绍

关联

a.     MP4 ISO 标准:c061988_ISO_IEC_14496-12_2012.pdf

b.    MP4中avc和hevcISO标准:W13478_1_pt15-3rd_FDIS_clean_HEVC.pdf

 

1.4部分分析工具

MP4Info

2.解析

2.1 ISO 标准Box列表

 

大家看着挺多的,其实真正用到的没有那么多。从上图可以看出,Box下可以包含0或多个SubBox,所以在解析的时候一定要注意。

下面开始介绍各个需要的Box功能,let’sGoGoGo!!!

2.2 部分Box介绍

从此开始介绍Box的功能,但只介绍对我们有意义的Box。

2.2.1 Box的基本知识

让我们来看上面2个介绍,这是Box的2个基本类型,分为Box和FullBox,其中FullBox是Box的扩展,请看下边例子:

其中size是从00 00 00 20开始到 33 67 70 38的,即是包含4字节的size和type的。

如果size = 1,即这个size是扩展的,要继续读8字节的largesize。

如果size = 0,即一直到文件末尾(目前为止的片源还没有遇见过)。

如果type = ‘uuid’,是用户扩展type,如果解析标准的,可以直接越过。

FullBox在Box上扩展4字节,包含version(1字节)和flags(3字节),会在其他Box上使用到。

其他的Box都是这2个基本类型上做的扩展,即这2个都是在Box的前端出现。

这里约定,后续的Box不写任何关于解析结构的介绍,请自己查阅c061988_ISO_IEC_14496-12_2012.pdfthx~

该解析是本人自己理解得出,如果有出现错误,请大家修改之,并拍砖, V_V

2.2.2 Box的类型介绍

下边介绍的是必须要解析的Box,并且一定一定要区分大小写

2.2.2.1 ftyp

ftyp用于初步校验该文件支持的格式,如上边的3gp8,isom等等。

但是这个格式太多了,如果要全部校验,可以说死翘翘,不太可能实现,所以我建议把ftyp略过,不去解析。

 

2.2.2.2 moov

moov,moof,mdat是3大主要Box,其中moov和mdat都可以在片源的开头和末尾如果出现moofmoov一定在moof前面,mdat也一定在moof前面。这里只介绍moov,后续会介绍moof和mdat。

moov是MP4/3GP文件最大的媒体信息Box,它不NB,是里面的信息NB。moov很简单的,主要是为了提醒大家,我里面是媒体信息了,大家注意了,就这个功能。

 

2.2.2.3 trak

trak作为moov手下第一大将,就不得不提一下了。trak是单独的媒体信息,可能是Audio/Video,或者是其他的(谁知道呢)。一个moov可以有多个trak,我就曾经遇见过一个128个trak的片源,真是恐怖啊,每个trak都仅包含一个图片的数据信息。

trak也是和moov一样,里面的信息NB,所以遇见trak大家可以直接解析里面的信息,后续还有很多这样的。

 

2.2.2.4 tkhd

tkhd是 trak header box,这个box只使用track_id,track_id 是唯一标示,并且不为0,我们

在后面找trak是就只用这个。

 

2.2.2.5 mdia

同trak。

 

2.2.2.6 mdhd

mdhd,media header,包含这个media的全部头信息,timescale和duration是需要的,timescale在后续会用到,duration是这个media的整体播放时间,很多时候都是准的,但也有不准的时候。

Bem的duration是根据解析moov时获取的最后时间去给出duration,因此是最准的。

 

2.2.2.7 hdlr

hdlr是哪个的缩写呢?是handler,你猜到没有。它的重要部分是handler_type,告诉我们这个trak是什么类型的。’vide’(Videotrack)和’soun’(Audiotrack),这2个是主要的。

 

2.2.2.8 minf

同mdia。

 

2.2.2.9 stbl

从stbl开始,我们要接触每个sample的构成了,从这往下的都是重要部分啦~

 

2.2.2.10 stsd

stsd,the sample description table,介绍关于Audio/Video的头信息数据,如H264的sps/pps,H265的sps/pps/vps,AAC的帧头数据,等等。

stsd 我们只识别 entry_count = 1,其他情况都不考虑。

根据stsd和文档W13478_1_pt15-3rd_FDIS_clean_HEVC.pdf,会解析出各种有用信息。

下面的是esds的结构:

 

利用sample description atom存储的信息可以正确的解码media sample。不同的媒体类型存储不同的sample description,例如,视频媒体,sample description就是图像的结构。第四章解释了不同媒体类型对应的sample description信息。

 

sample description atom的类型是'stsd',包含了一个sample description表。根据不同的编码方案和存储数据的文件数目,每个media可以有一个到多个sample description。sample-to-chunk atom通过这个索引表,找到合适medai中每个sampledescription。

字段

长度(字节)

描述

尺寸

4

这个atom的字节数

类型

4

stsd

版本

1

这个atom的版本

标志

3

这里为0

条目数目

4

sample descriptions的数目

Sample description

 

不同的媒体类型有不同的sample description,但是每个sample description的前四个字段是相同的,包含以下的数据成员

尺寸

4

这个sample description的字节数

数据格式

4

存储数据的格式。

保留

6

 

数据引用索引

2

利用这个索引可以检索与当前sample description关联的数据。数据引用存储在data reference atoms

Audio track的值

可以看出这个sample只有一个description,对应得的数据格式是'mp4a'14496-12定义了这种结构,mp4解码器会识别此descriptionXXX

Video track的值

可以看出这个sample只有一个description,对应得的数据格式是'mp4v'14496-12定义了这种结构,mp4解码器会识别此descriptionXXX


2.2.2.11 stts,ctts

stts和ctts是时间戳Box,每一帧的时间戳 DisplayTS = stts + ctts。

stts是必须的,ctts是非必须的。

stts的时间戳结构如下:

Sample Count       Sample-delta

  14                 10

  20                 20

怎么理解呢?

第一行,有14个时间间隔是10的sample,也就是说时间戳是0,10,20,30,40~ 130。

第二行,有20个时间间隔是20的sample,但是起始时间是前一个时间戳,这里就是130了,即150,170,190 ~~。

ctts的时间戳结构如下:

Sample Count       Sample_offset    范围索引(自定义的)

1                                                               10          (a)

1                                                               30          (b)

2                                                               0           (c)

1                                                               30          (d)

ctts要与stts一起使用,ctts这个结构是时间戳偏移和在此偏移范围内的sample个数。

根据ctts和stts可以得出每个sample的时间戳,如下:

如sample1  ts = 0 + 10     (a)

  sample2  ts = 10 + 30    (b)

 sample3  ts = 20 + 0     (c)

 sample4  ts = 30 + 0     (c)

 sample5  ts = 40 + 30    (d)

 

2.2.2.12 stsz

stsz是每个sample 的size值,如果sample_size!= 0,代表有默认size值。

 

2.2.2.13 stsc

当添加samples到media时,用chunks组织这些sample,这样可以方便优化数据获取。一个trunk包含一个或多个sample,chunk的长度可以不同,chunk内的sample的长度也可以不同。sample-to-chunkatom存储sample与chunk的映射关系。

Sample-to-chunk atoms的类型是'stsc'。它也有一个表来映射sample和trunk之间的关系,查看这张表,就可以找到包含指定sample的trunk,从而找到这个sample。

字段

长度(字节)

描述

尺寸

4

这个atom的字节数

类型

4

stsc

版本

1

这个atom的版本

标志

3

这里为0

条目数目

4

sample-to-chunk的数目

sample-to-chunk

 

sample-to-chunk表的结构

First chunk

4

这个table使用的第一个chunk序号

Samples per chunk

4

当前trunk内的sample数目

Sample description ID

4

与这些sample关联的sample description的序号

Audio track的值

可以建立这个sample-to-chunk表,共有33项。

 

Video track的值

 

可以建立这个sample-to-chunk表,共有140项。

 

First chunk

Samples per chunk

Sample description ID

1

4

1

4

3

1

5

4

1

8

3

1

9

4

1

12

3

1

13

4

1

16

3

1

17

4

1

20

3

1

21

4

1

可以建立Video tracksample-to-chunk表,共有140项。

 

First chunk

Samples per chunk

Sample description ID

1

4

1

4

3

1

5

4

1

8

3

1

9

4

1

12

3

1

13

4

1

16

3

1

17

4

1

20

3

1

21

4

1

277

4

1

280

3

1

这个表类似于行程编码,第一个first chunk减去第二个first chunk就是一共有多少个trunk包含相同的sample数目,这样通过不断的叠加,就可以得到一共有280个trunk,每个trunk包含多少个sample,以及每个trunk对应的description

 

2.2.2.14 stco,co64

Chunk offset atoms 定义了每个trunk在媒体流中的位置,它的类型是'stco'。位置有两种可能,32位的和64位的,后者对非常大的电影很有用。在一个表中只会有一种可能,这个位置是在整个文件中的,而不是在任何atom中的,这样做就可以直接在文件中找到媒体数据,而不用解释atom。需要注意的是一旦前面的atom有了任何改变,这张表都要重新建立,因为位置信息已经改变了。

字段

长度(字节)

描述

尺寸

4

这个atom的字节数

类型

4

stco

版本

1

这个atom的版本

标志

3

这里为0

条目数目

4

chunk offset的数目

chunk offset

 

字节偏移量从文件开始到当前chunk。这个表根据chunk number索引,第一项就是第一个trunk,第二项就是第二个trunk

大小

4

每个sample的大小

Audio track的值

 

Video track的值

 

 

2.2.2.15 stss

sync sampleatom确定media中的关键帧。对于压缩的媒体,关键帧是一系列压缩序列的开始帧,它的解压缩是不依赖于以前的帧。后续帧的解压缩依赖于这个关键帧。

sync sampleatom可以非常紧凑的标记媒体内的随机存取点。它包含一个sample序号表,表内的每一项严格按照sample的序号排列,说明了媒体中的哪一个sample是关键帧。如果此表不存在,说明每一个sample都是一个关键帧,是一个随机存取点。

Sync sample atoms 的类型是'stss'。

字段

长度(字节)

描述

尺寸

4

这个atom的字节数

类型

4

stss

版本

1

这个atom的版本

标志

3

这里为0

条目数目

4

sync sample的数目

sync sample

 

sync sample表的结构

Sample序号

4

是关键帧的sample序号

Video track的值

可以看出这个video片断共有35个关键帧。


2.2.2.16 mvex

同moov,一般在有moof时出现,是moof的扩展,但是在moov中。

 

2.2.2.17 trex

trak的扩展属性,在mvex中,可以解析出moof 中所有traf都拥有的default参数,这个有时候很重要

 

2.2.2.18 mdat

mdat是数据段,根据上面解析出的sample信息就可以解析出数据了。

一个文件可以有多个mdat,每个mdat可以保护Audio/Video交织流,或者只包含Audio/Video一种流。

 

2.2.2.19 moof

moof是movie segment,是分段形式解析,如同hls中的ts分片一样。

moof如同moov一样,但moov一定要有,但moof可能没有。每个moof后边都会接一个mdat数据段。

 

2.2.2.20 traf

traf同trak。

 

2.2.2.21 tfhd

tfhd是traf的header,可以解析出现当前traf的default参数,也可能使用trex的参数。

解析tfhd获取trak_id获取唯一标示进行匹配,然后根据vflag的flag获取可能的值。

 

#define BASE_DATA_OFFSET_PRESENT 0x000001

#define SAMPLE_DESCRIPTION_INDEX_PRESENT0x000002

#define DEFAULT_SAMPLE_DURATION_PRESENT0x000008

#define DEFAULT_SAMPLE_SIZE_PRESENT0x000010

#define DEFAULT_SAMPLE_FLAGS_PRESENT0x000020

 

   if (flag & BASE_DATA_OFFSET_PRESENT)

    {

       // read 8 bits

       traf->baseDataOffset = Get8Bits(bitStream);

    }

 

       traf->sampleDescriptionIndex= flag & SAMPLE_DESCRIPTION_INDEX_PRESENT ? Get4Bits(bitStream) :trex->defaultSampleDescriptionIndex;

   traf->defaultSampleDuration = flag &DEFAULT_SAMPLE_DURATION_PRESENT ? Get4Bits(bitStream) :trex->defaultSampleDuration;

   traf->defaultSampleSize = flag & DEFAULT_SAMPLE_SIZE_PRESENT ?Get4Bits(bitStream) : trex->defaultSampleSize;

   traf->defaultSampleFlag = flag & DEFAULT_SAMPLE_FLAGS_PRESENT ?Get4Bits(bitStream) : trex->defaultSampleFlag;

 

2.2.2.22 tfdt

tfdt获取该segment的起始播放时间,因为A/V的segment起始时间可能不一样,需要这个去同步。

 

2.2.2.22 trun

trun是moof中最重要的部分,就像stsl一样,可以解析出每个sample的信息。

#define DATA_OFFSET_PRESENT        0x000001

#define FIRST_SAMPLE_FALGS_PRESENT 0x000004

#define SAMPLE_DURATION_PRESENT    0x000100

#define SAMPLE_SIZE_PRESENT        0x000200

#define SAMPLE_FLAGS_PRESENT       0x000400

#defineSAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT 0x000800  // cts

 

#define SAMPLE_FLAG_IS_NON_SYNC 0x00010000

#define SAMPLE_FLAG_DEPENDS_YES 0x01000000

 

注意1:获取vflag中的flag,flag是用于识别要获取哪些参数的。还有sample Count

注意2: 获取data offset, 数据的起始位置 = start position(moof,包括4byte size) + data offset

注意3:每次获取的duration都要进行如下操作,包括stts和ctts的,

Duration = Get4Bits() * 1000 / mdhd.timeScale。

注意4:如果是Video,

key = !(sampleFlag &(SAMPLE_FLAG_IS_NON_SYNC | SAMPLE_FLAG_DEPENDS_YES))

sampleFlag 是FIRST_SAMPLE_FALGS_PRESENT 和SAMPLE_FLAGS_PRESENT 获取的。

注意5:每个sample的offset (n)= offset(n-1) + size(n)

注意6:每个sample的pts(n) = dts + ctts,dts += duration

 



[mp4文件格式]获取mp4文件信息1 - 计算电影长度 wqyuwss 发表于 2007-5-1 4:42:00

方法1

从mvhd - movie header atom中找到time scale和duration,duration除以time scale即是整部电影的长度。

time scale相当于定义了标准的1秒在这部电影里面的刻度是多少。

例如audio track的time scale = 8000, duration = 560128,所以总长度是70.016,video track的timescale = 600, duration = 42000,所以总长度是70

方法2

首先计算出共有多少个帧,也就是sample(从sample size atoms中得到),然后

整部电影的duration = 每个帧的duration之和(从Time-to-sample atoms中得出)

例如audio track共有547个sample,每个sample的长度是1024,则总duration是560128,电影长度是70.016;video track共有1050个sample,每个sample的长度是40,则总duration是42000,电影长度是70

 

[mp4文件格式]获取mp4文件信息2 - 计算电影图像宽度和高度  wqyuwss 发表于 2007-5-1 4:44:00 从tkhd– track headeratom中找到宽度和高度即是。

 

[mp4文件格式]获取mp4文件信息3 - 计算电影声音采样频率  wqyuwss 发表于 2007-5-1 4:44:00

从tkhd – track header atom中找出audio track的timescale即是声音的采样频率。

 

[mp4文件格式]获取mp4文件信息4 - 计算视频帧率 wqyuwss 发表于 2007-5-1 4:45:00

首先计算出整部电影的duration,和帧的数目然后

帧率 = 整部电影的duration / 帧的数目

 

[mp4文件格式]获取mp4文件信息5 - 计算电影的比特率 wqyuwss 发表于 2007-5-1 4:46:00

整部电影的尺寸除以长度,即是比特率,此电影的比特率为846623/70 = 12094 bps

 

[mp4文件格式]获取mp4文件信息6 - 查找sample  wqyuwss 发表于 2007-5-1 4:47:00

当播放一部电影或者一个track的时候,对应的media handler必须能够正确的解析数据流,对一定的时间获取对应的媒体数据。如果是视频媒体, mediahandler可能会解析多个atom,才能找到给定时间的sample的大小和位置。具体步骤如下:

1.确定时间,相对于媒体时间坐标系统

2.检查time-to-sample atom来确定给定时间的sample序号。

3.检查sample-to-chunk atom来发现对应该sample的chunk。

4.从chunk offset atom中提取该trunk的偏移量。

5.利用sample size atom找到sample在trunk内的偏移量和sample的大小。

 

例如,如果要找第1秒的视频数据,过程如下:

1.  第1秒的视频数据相对于此电影的时间为600

2.  检查time-to-sampleatom,得出每个sample的duration是40,从而得出需要寻找第600/40 = 15 + 1 = 16个sample

3.  检查sample-to-chunkatom,得到该sample属于第5个chunk的第一个sample,该chunk共有4个sample

4.  检查chunkoffset atom找到第5个trunk的偏移量是20472

5.  由于第16个sample是第5个trunk的第一个sample,所以不用检查sample size atom,trunk的偏移量即是该sample的偏移量20472。如果是这个trunk的第二个sample,则从sample size atom中找到该trunk的前一个sample的大小,然后加上偏移量即可得到实际位置。

6.  得到位置后,即可取出相应数据进行解码,播放

 

 

[mp4文件格式]获取mp4文件信息7 - 查找关键帧 wqyuwss 发表于 2007-5-1 4:48:00

查找过程与查找sample的过程非常类似,只是需要利用sync sample atom来确定key frame的sample序号

  1. 确定给定时间的sample序号
  2. 检查sync sample atom来发现这个sample序号之后的key frame
  3. 检查sample-to-chunk atom来发现对应该sample的chunk
  4. 从chunk offset atom中提取该trunk的偏移量
  5. 利用sample size atom找到sample在trunk内的偏移量和sample的大小

 

[mp4文件格式]获取mp4文件信息8 - Random access  wqyuwss 发表于 2007-5-1 5:04:00

Seeking主要是利用sample table box里面包含的子box来实现的,还需要考虑edit list的影响。

可以按照以下步骤seek某一个track到某个时间T,注意这个T是以movie header box里定义的time scale为单位的:

  1. 如果track有一个edit list,遍历所有的edit,找到T落在哪个edit里面。将Edit的开始时间变换为以movie time scale为单位,得到EST,T减去EST,得到T',就是在这个edit里面的duration,注意此时T'是以movie的time scale为单位的。然后将T'转化成track媒体的time scale,得到T''。T''与Edit的开始时间相加得到以track媒体的time scale为单位的时间点T'''。
  2. 这个track的time-to-sample表说明了该track中每个sample对应的时间信息,利用这个表就可以得到T'''对应的sample NT
  3. sample NT可能不是一个random access point,这样就需要其他表的帮助来找到最近的random access point。一个表是sync sample表,定义哪些sample是random access point。使用这个表就可以找到指定时间点最近的sync sample。如果没有这个表,就说明所有的sample都是synchronization points,问题就变得更容易了。另一个shadow sync box可以帮助内容作者定义一些特殊的samples,它们不用在网络中传输,但是可以作为额外的random access point。这就改进了random access,同时不会影响正常的传输比特率。这个表指出了非random access point和random access point之间的关系。如果要寻找指定sample之前最近的shadow sync sample,就需要查询这个表。总之,利用sync sample和shadow sync表,就可以seek到NT之前的最近的access point sample Nap
  4. 找到用于access point的sample Nap之后,利用sample-to-chunk表来确定sample位于哪个chunk内。
  5. 找到chunk后,使用chunk offset找到这个chunk的开始位置。
  6. 使用sample-to-chunk表和sample size表中的数据,找到Nap在此chunk内的位置,再加上此chunk的开始位置,就找到了Nap在文件中的位置。
 


你可能感兴趣的:(MP4解析)