理解CMTime ( 翻译 )

最近看到一篇关于CMTime的文章,感觉讲得通俗易懂,就想着翻译一下,我尽量在语义正确的情况下按照原著来翻译,原文章在这里。

准确的看待时间

大部分人是不需要精确的知道时间的。举一个极端的,如果你是奥运会百米运动员,你可能会关心你和时间记录之间的微秒差距.但就现状来说,目前的时间表示就已经很不错了。但是在多媒体中,我们可能会关心很长的 ( 几天或几周 ) 或很短的 ( 十几微秒 ) 的时间。

如果我们想表示一个视频文件中的某一瞬间,如35:06,我们可能会用双精度浮点数来表示它,比如:

NSTimeInterval t = 2106.0。大部分时候这样是没错的,但是我们想把时间分的特别细的时候这个就不行了。我所了解的大部分平台 ( sizeof(NSTimeInterval) == sizeof(Float64) == sizeof(double) == 8 ),一个浮点数,如double,只能粗略的表示小数点后16位。

浮点数在某些时候重复操作 ( 如加减乘除等 ) 会遇到一些问题,比如在进行一系列的运算之后,浮点数表示的时间可能就和准确时间产生较大区别,就可能在同步多媒体流时产生一些错误。

比如将一百万个0.0000001相加,运算结果可能会变成1.0000000000079181,产生这个问题的原因是浮点数不能准确的表示10的-6次方。这个错误不是那么明显,但是如果你在HTTP流中以每秒上千次的方式运行,还是有可能会出现错误的。

这就导致我们必须废除以前的浮点计数法,寻找一个新方法来准确的表示时间。

将时间看作有理数

在Mac和iOS平台上有很多数据结构来表示时间,在iOS4和Mac OSX 10.7中,苹果引入了一种新的数据格式:CMTime 和 CMTimeRange。如果你理解了CMTime,那么你自然而然就会理解CMTimeRange,所以我们不讨论后者了。

CMTime是一种C函数结构体,有4个成员。

typedef struct { CMTimeValue value; CMTimeScale timescale; CMTimeFlags flags; CMTimeEpoch epoch; } CMTime;

这一小节我们将关注value和timescale,flags也是一个重要成员,我们稍后会提到它。flags的各种值会表示时间戳的正无穷或负无穷,或已经被中间计算为一个结果。这样的数据结构显然比浮点数具有更强的表示力,并且有很多优势。

我们来思考一下CMTime是如何表示时间的。我们需要知道value和timescale都是64位或32的integer,通过前面浮点数的例子我们就能够知道value被存为integer的原因。每个timescale的值,我们通过64位能精确的表示900亿亿的正值和小数点后19位。

timescale到底代表什么喃?它表示1秒的时间被分成了多少份。因为整个CMTime的精度是由它控制的所以它显的尤为重要。例如,当timescale为1的时候,CMTime不能表示1秒一下的时间和1秒内的增长。相同的,当timescale为1000的时候,每秒钟便被分成了1000份,CMTime的value便代表了多少毫秒。

怎么去选取适当的timescale喃?苹果的对视频的建议是timescale=600,因为600是常见视频每秒帧数 ( 24,25,30FPS ) 的公倍数,如果是音频,你可以用60,000或更高的timescale。用64位integer的value来计算,你仍能够表示580万年内的每1/60,000增长,这是非常精确的。

这种方式的时间戳是简单的t.value / t.timescale。你能够使用CMTimeGetSeconds来精确的将CMTime转换成

Float64。

不是很方便的便利方法

CMTimeGetSeconds或许是有用的,但是它有双面性。CMTimeMakeWithSeconds对新手来说并不友好,这是方法的样式:

CMTime CMTimeMakeWithSeconds ( Float64 seconds, int32_t preferredTimeScale );

当我第一次使用它的时候,它并没有如我所愿。现在我们已经了解了CMTime的所有内部成员,我希望你能看出我在哪里出的错。我想表示0.5秒,这样我就能每0.5秒收到AVPlayer播放MP3的callback,我这样使用这个方法:

CMTime interval = CMTimeMakeWithSeconds(0.5, 1);

如果你一路看过来,你应该能知道,interval其实是0,而不是0.5。用一个整数来带表示一个半整数明显是说不通的,但是我们确实需要这样做的嘛。我们的值被约等于了0因为我们timescale不是足够的精确。

Core Media的API包括了CMTime的构造、比较、和算术运算。虽然用CMTime来进行算术运算是复杂的,但是如果你想精确的计算你就需要这么做。每种方法都会进行值溢出检测和凑整,并且会把CMTime的flags设置为适当的值。

用CMTimeAdd来将2个CMTime进行相加;用CMTimeCompare来对2个time进行比较 ( 该方法的返回值可以用C语言标注库中的qsort来进行比较 );找出2个时间中的较大值,可以用CMTimeMaximum。阅读documentation来查找更多的全面的方法。

无限大:设置flag

最后一节是CMTime表示无穷、模糊值和凑整。模糊值表示它的value是未知的,你有可能会的到这样一个时间戳在你访问一个还没被创建好的对象的时间的时候。flags的值能够被用来和下面这些值相比较检查来判断时候发生了异常:

  • kCMTimeFlags_PositiveInfinity
  • kCMTimeFlags_NegativeInfinity
  • kCMTimeFlags_Indefinite

并不是每个人都喜欢按位或操作,苹果给出了一些宏来代表这些flags:

  • CMTIME_IS_POSITIVE_INFINITY
  • CMTIME_IS_NEGATIVE_INFINITY
  • CMTIME_IS_INDEFINITE

这些宏能够确保这些时间是有问题的还是合法的。如果不合法,它会返回NO。

-这有一些比较不同类别的时间的清单:

-无效时间与无效时间是相等的,比其他的时间都大;

-正无穷大时间比非法时间小,和本身相等,比其他时间大;

-模糊时间比非法时间小,小于正无穷时间,和本身相等,比其他时间大;

-负无穷大时间比其他时间小,等于自身;

用途

我没有时间去详细的讨论CMTime在你的项目的实际用途了。但是你读了这篇文章,你应该对CMTime有了一定的了解了。AVFoudation库中有很多类 ( 如:AVPlayer、AVAssetReader ) 使用CMTime去表示时间。

ps:

翻译的可能会有问题,如果您不怜赐教,我会及时改正,谢谢观看。

你可能感兴趣的:(理解CMTime ( 翻译 ))