深入理解视频文件封装格式之MP4(五)获取Metadata实例-视频文件格式

前景回顾

上节大致浏览了MP4中的每个box是如何解析的,并把数据放到了一个结构体中,下面就要看看怎么组织和使用这些数据,这里介绍几个获取数据的方法原理的实例

深入理解视频文件封装格式之MP4(五)获取Metadata实例

1.计算电影长度

方法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的time scale = 600, duration = 42000,所以总长度是70

方法2:首先计算出共有多少个帧,也就是sample(从sample size atoms(stsz)中得到),然后整部电影的duration = 每个帧的duration之和(从Time-to-sample atoms(stts)中得出)例如audio track共有547个sample,每个sample的长度是1024,则总duration是560128,电影长度是70.016;video track共有1050个sample,每个sample的长度是40,则总duration是42000,电影长度是70。具体的实现方法是什么呢?

status->totalTime.seconds = this->movff.duration/this->movff.time_scale;

2.计算电影声音采样频率

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

3.计算电影图像宽度和高度

从tkhd – track header atom中找到宽度和高度即是

4.计算视频帧率

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

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

5.计算电影的比特率

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

比特率就是文件长度除以时长,比如源文件是17 MiB,时长是4分43秒,先将MiB转换成b,17(MiB)*1024*1024*8 = 14260633(b),随后将时间转换成秒 4分43秒 = 283秒,最后用文件长比上时长 (142606336 / 283) * 1000 = 503.909..四舍五入后约等于 504(Kbps),这里需要注意两点一个就是MiB和MB的区别,前者是以1024进位,而后者是以1000进位,另一个就是为什么我没有将MiB换算成Kib直接除以283秒计算比特率,原因就在于网络传输一般都是以1000进位的,如果不彻底将文件长度从MiB换算成b再计算的话,就会因为Kib中的K是以1024进位,而Kbps是中的K是以1000进位的原因而影响到最后计算出来的比特率的准确性

6.查找sample

当播放一部电影或者一个track的时候,对应的media handler必须能够正确的解析数据流,对一定的时间获取对应的媒体数据。如果是视频媒体, media handler可能会解析多个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-sample atom,得出每个sample的duration是40,从而得出需要寻找第600/40 = 15 + 1 = 16个sample

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

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

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

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

这个实例其实就是告诉我们seek的方法

7.查找关键帧

查找过程与查找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的大小

8.Random access (seeking)

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在文件中的位置

这里只是原理性的总结,没有参考具体实例分析,可以加深对box解析的理解。

本节完(五)

推荐阅读