CMTime

CMTime


转载改编自:

https://www.jianshu.com/p/f02aad2e7ff5

https://blog.csdn.net/fengzhixinfei/article/details/80729346


一、CMTime

1、基本说明

CMTime 是 Core Media 定义的一种时间数据类型,是一个C语言的结构体,定义如下:

CMTime定义是一个C语言的结构体,定义如下:

typedef struct
{
    CMTimeValue value;  //64位有符号整型变量,作为分子
    CMTimeScale timescale;   //32位有符号整型变量,作为分母
    // value/timescale = 对应时间-秒
    CMTimeFlags flags; //位掩码,表示时间的指定状态,比如判定诗句是否有效、不确定或是是否出现舍入等
    CMTimeEpoch epoch;      
} CMTime; 
  public typealias CMTimeValue = Int64
  public typealias CMTimeScale = Int32

CMTime是以分数的形式表示时间,

  • value表示分子
  • timescale表示分母
  • flags是位掩码,表示时间的指定状态。

这里value,timescale是分别以64位32位整数来存储的,这样可以避免double类型带来的精度丢失。

另外,通过用64位整数来表示分子,我们可以为每个timescale表示90亿个不同的正值,最多19位唯一的十进制数字。


timescale

那么timescale又是什么? 它表示每秒分割的“切片”数。 CMTime的整体精度就是受到这个限制的。比如:
如果timescale为1,则不能有对象表示小于1秒的时间戳,并且时间戳以1秒为增量。类似的,如果timescale是1000,则每秒被分割成1000个,并且该value表示我们要显示的毫秒数。

所以当你试图表示0.5秒的时候,你千万不能这么写:

CMTime interval = CMTimeMakeWithSeconds(0.5, 1);

这里interval实际上是0 而不是0.5。

所以为了能让你选择合理的时间尺度确保不被截断,Apple建议我们使用600。如果你需要对音频文件进行更精确的所以,你可以把timescale设为60,000或更高。这里64位 value的好处就是,你仍然可以用这种方式来明确的表示580万年的增量,即1/60,000秒。

所以,这里可以得出结论:

timescale只是为了保证时间精度而设置的帧率,并不一定是视频最后实际的播放帧率。


CMTime 和 NSTimeInterval

假设我们需要在视频文件中精确地指定一个时刻,比如 35:06。通常的方法是把时间表示为一个双精度的浮点数据,比如:NSTimeInterval t = 2106.0;

这个方法在大多数情况下是没有问题的。但是当我们把非常长的时间段划分成非常小的切片时,就会出现问题。

不直接进行浮点类型的运算,而是把一个double类型可以容纳大约 16 位有效数字(十进制)的 8个字节的内存空间(在其他通用平台上,sizeof(NSTimeInterval) == sizeof(Float64) == sizeof(double) == 8)。 再次普及double浮点型数据的换算过程和推算原理

浮点数存在一个大问题:重复操作(加法,乘法等)导致不精确的积累,于是在视频时长很长的时候这个差异会被无限放大,从而在同步多个媒体流时可能导致错误。

这里举个栗子。一百万个0.000001相加,结果约为1.0000000000079181。该错误是由于1e-6不能以我们使用的double类型精确的表示,所以我们改为使用二进制近似位,它的低有效位不同。这并不是一个大问题,但是当你在运行一个HTTP流服务器的时候,那么你可能会无限期的每秒去积累这种不精确度。

这就促使我们去找到一种更精确表达时间的方式,通过消除double类型和他们固有的不精确性(不说他们的硬编码舍入行为)。


2、创建

1)CMTimeMake()

CMTime CMTimeMake(int64_t value,int32_t timescale)

创建一个代表 3秒 的CMTime表达式(value/timescale):

CMTime ts1 = CMTimeMake(3, 1);
CMTime ts2 = CMTimeMake(1800, 600);
CMTime ts3 = CMTimeMake(132300, 44100);

//打印时间结果
CMTimeShow(ts1);
CMTimeShow(ts2);
CMTimeShow(ts3);

//打印结果
{3/1 = 3.000}
{1800/600 = 3.000}
{132300/44100 = 3.000}

Float64 seconds = 5;   // 时长
int32_t preferredTimeScale = 600;  // 帧率
CMTime inTime = CMTimeMakeWithSeconds(seconds, preferredTimeScale);
CMTimeShow(inTime);  // 

int64_t value = 10000;  // 视频的帧数
int32_t preferredTimeScale = 600;  // 每秒的帧数
CMTime inTime = CMTimeMake(value, preferredTimeScale);
CMTimeShow(inTime);  //  10000/600 = 16.667

在处理视频内容时常见的时间刻度为 600,这是大部分常用视频帧率24FPS、25FPS、30FPS的公倍数。

音频常见的时间刻度就是采样率,如44.1kHz(44100)、48kHz(48000)。


2)CMTimeMakeWithSeconds()
CMTime CMTimeMakeWithSeconds( Float64 seconds, int32_t preferredTimescale)
CMTime t = CMTimeMakeWithSeconds(5, 1); // 5 seconds

CMTimeShow(t);
//{5/1 = 5.000}

3)CMTimeMakeFromDictionary
NSDictionary *timeData = @{(id)kCMTimeValueKey : @2,
                           (id)kCMTimeScaleKey : @1,
                           (id)kCMTimeFlagsKey : @1,
                           (id)kCMTimeEpochKey : @0};
CMTime t = CMTimeMakeFromDictionary((__bridge CFDictionaryRef)timeData); 

CMTimeShow(t);
//{2/1 = 2.000} 

3、加减 CMTimeAdd & CMTimeSubtract

1、加减

CMTime ts1 = CMTimeMake(3, 1);
CMTime ts2 = CMTimeMake(5, 2);

CMTime result;
result = CMTimeAdd(ts1, ts2);
CMTimeShow(result);
//{11/2 = 5.500}

result = CMTimeSubtract(ts1, ts2);
CMTimeShow(result);
//{1/2 = 0.500}

4、比较 CMTimeCompare

CMTimeCompare()

CMTime t1 = CMTimeMake(300, 100); // 3 seconds
CMTime t2 = CMTimeMakeWithSeconds(5, 1); // 5 seconds

int32_t r  = CMTimeCompare(t1, t2);
NSLog(@"compare: %d",r);
//t1 < t2   => -1
//t1 == t2  => 0
//t1 > ts   => 1

CMTIME_COMPARE_INLINE()

NSLog(@"t1 > t2 => %@",CMTIME_COMPARE_INLINE(t1, >, t2) ? @"YES":@"NO");
// t1 > t2 => NO

5、验证CMTIME_IS_VALID()

NSLog(@"ti is valid :%@",CMTIME_IS_VALID(t1) ? @"YES":@"NO");
// ti is valid :YES

6、CMTime 转换为秒 CMTimeGetSeconds

定义

Float64 CMTimeGetSeconds(CMTime time)

使用

CMTime t1 = CMTimeMake(3001, 100);
NSLog(@"second : %f",CMTimeGetSeconds(t1));
//second : 30.010000

7、转换为对象NSValue valueWithCMTime

+ (NSValue *)valueWithCMTime:(CMTime)time;

CMTime structTime = CMTimeMake(1, 3);

NSValue *valueTime = [NSValue valueWithCMTime:structTime];
NSLog(@"%@", valueTime);
//CMTime: {1/3 = 0.333}

structTime = [valueTime CMTimeValue];
CMTimeShow(structTime);
//{1/3 = 0.333}

8、转换字典NSDictionary CMTimeCopyAsDictionary & CMTimeMakeFromDictionary

 CMTime structTime = CMTimeMake(1, 3);
NSDictionary *timeDict = CFBridgingRelease(CMTimeCopyAsDictionary(structTime, NULL));
NSLog(@"%@", timeDict);

structTime = CMTimeMakeFromDictionary((__bridge CFDictionaryRef)(timeDict));
CMTimeShow(structTime);

/*
 {
    epoch = 0;
    flags = 1;
    timescale = 3;
    value = 1;
}
{1/3 = 0.333}
*/

二、CMTimeRange

1、说明

时间范围的数据类型:CMTimeRange。

typedef struct
{
    CMTime          start;  // 时间范围的起点
    CMTime          duration;  // 时间范围的持续时间
} CMTimeRange; 

2、创建

1)CMTimeRangeMake()
CMTimeRange CMTimeRangeMake( 
CMTime start, 
CMTime duration) 
根据开始时间点与持续时间确定时间范围。

//创建一个时间范围,从时间轴的5秒位置开始,持续时长5秒
CMTime duration = CMTimeMake(5, 1);
CMTimeRange range = CMTimeRangeMake(duration, duration);
CMTimeRangeShow(range);

//{{5/1 = 5.000}, {5/1 = 5.000}}

2)CMTimeRangeFromTimeToTime()

CMTimeRange CMTimeRangeFromTimeToTime( CMTime start, CMTime end )

根据起始时间点和终止时间点,确定时间范围。

CMTime beginTime = CMTimeMake(5, 1);
CMTime endTime = CMTimeMake(12, 1);
CMTimeRange rangeTime = CMTimeRangeFromTimeToTime(beginTime, endTime);
CMTimeRangeShow(rangeTime);
// {{5/1 = 5.000}, {7/1 = 7.000}}


3、运算

1)取交叉时间范围 CMTimeRangeGetIntersection
CMTimeRange intersetionRange = CMTimeRangeGetIntersection(range, rangeTime);
CMTimeRangeShow(intersetionRange);	// {{7/1 = 7.000}, {3/1 = 3.000}}

2)取时间范围总和 CMTimeRangeGetUnion
CMTimeRange unionRange = CMTimeRangeGetUnion(range, rangeTime);
CMTimeRangeShow(unionRange);  // {{5/1 = 5.000}, {7/1 = 7.000}}

4、转换为对象NSValue

+ (NSValue *)valueWithCMTimeRange:(CMTimeRange)timeRange

CMTimeRange structTimeRange = CMTimeRangeMake(kCMTimeZero, kCMTimePositiveInfinity);

NSValue *valueTimeRange = [NSValue valueWithCMTimeRange:structTimeRange];
NSLog(@"%@", valueTimeRange);	//CMTimeRange: {{0/1 = 0.000}, {+INFINITY}}

structTimeRange = [valueTimeRange CMTimeRangeValue];
CMTimeRangeShow(structTimeRange);	//{{0/1 = 0.000}, {+INFINITY}}

5、转换成字典NSDictionary CMTimeRangeCopyAsDictionary & CMTimeRangeMakeFromDictionary

NSDictionary *timeRangeDict = CFBridgingRelease(CMTimeRangeCopyAsDictionary(structTimeRange, NULL));
NSLog(@"%@", timeRangeDict);

structTimeRange = CMTimeRangeMakeFromDictionary((__bridge CFDictionaryRef)(timeRangeDict));
CMTimeRangeShow(structTimeRange);
/*
 {
    duration =     {
        epoch = 0;
        flags = 5;
        timescale = 0;
        value = 0;
    };
    start =     {
        epoch = 0;
        flags = 1;
        timescale = 1;
        value = 0;
    };
}
{{0/1 = 0.000}, {+INFINITY}}
*/


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值