CMTimeについてのメモ epoch, timescale, CMTimeRange

CMTimeについてPlayGroundで色々試してみました。

# 構造

CMTimeは時間や時刻を表す構造体、基本は　seconds = value/timeScale

```cmtime_struct.swift
//: Playground - noun: a place where people can play

import CoreMedia
import CoreFoundation

/*:
	@typedef	CMTime
	@abstract	valueとtimeScaleでvalue/timeScaleで秒をあらわす
 */

/*: 
    public struct CMTime {
        
        public var value: CMTimeValue // Int64
        public var timescale: CMTimeScale // Int32
        public var flags: CMTimeFlags // Valid等のフラグ(CMTimeFlags)
        public var epoch: CMTimeEpoch // Int64 同一時刻でもループしているなどで実際にはことなるものの違いを表す値として使う。
    }
*/
```

# 簡単な使い方

以下に簡単な例を実例で

```cmtime_usadge.swift

/*:
	@function	CMTimeMake
	@abstract	valueとtimescaleを持った適切なCMTimeを作成する.  Epochは０になる
	@result		CMTime
 */

// 簡単な使い方
do {
    let t : CMTime = CMTimeMake(43, 10)
    
    // value/timescale = seconds.なので
    // 43/10 = 4.3秒
    
    let sec:Float64 = t.seconds // 4.3
    t.epoch // 0
    
    // 性質
    t.isValid // true
    t.isIndefinite // false
    t.isPositiveInfinity // false
    t.isNegativeInfinity // false
    t.isIndefinite // false
    t.isNumeric // true
    t.hasBeenRounded // false 丸められているか false
    
    
    // ログ
    CMTimeShow(t) // {43/10 = 4.300}

    // 文字列
    String(CMTimeCopyDescription(kCFAllocatorDefault,t)) // "Optional({43/10 = 4.300})"
    
    // 辞書変換
    // ["flags": 1, "value": 43, "timescale": 10, "epoch": 0]
    let d : CFDictionaryRef = CMTimeCopyAsDictionary(t, kCFAllocatorDefault)! 

    t == CMTimeMakeFromDictionary(d) // true

    let dn : NSDictionary = d
    dn[String(kCMTimeValueKey)] // 43
    dn[String(kCMTimeScaleKey)] // 10
    dn[String(kCMTimeEpochKey)] // 0
    dn[String(kCMTimeFlagsKey)] // 1

    
    
    
    
    
    // 秒数指定でつくる
    let t2 = CMTime(seconds: 10.5, preferredTimescale: 100)
    t2.value // 1050
    t2.timescale // 100
    t2.seconds // 10.5
    

    
}

//: timeスケールが0だと..
do {
    let t : CMTime = CMTimeMake(43, 0)
    t.value // 0
    t.timescale // 0
    t.seconds // nan
    t.isValid // false
    t.isIndefinite // false
    t.isPositiveInfinity // false
    t.isNegativeInfinity // false
    t.isIndefinite // false
    t.isNumeric // false
    t.hasBeenRounded // false 丸められているか(四捨五入等, CMTimeRoundingMethod)
}

//: 比較
do {
    let t1 : CMTime = CMTimeMake(43, 10)
    let t2 : CMTime = CMTimeMake(430,100)
    let t3 : CMTime = CMTimeMake(20, 10)
    
    t1 == t2 // true
    t1 > t3 // true
    
    t1.seconds // 4.3
    t3.seconds // 2
    (t1 - t3).seconds // 2.3
}


//: 演算
do {
    
    // 足し算
    let t1 : CMTime = CMTimeMake(10,100)
    let t2 : CMTime = CMTimeMake(20,100)
    let t3 : CMTime = CMTimeMake(1,10)
    
    (t1 + t2).value // 30
    (t1 + t2).timescale // 100
    (t1 + t2).seconds // 0.3
    
    (t1 + t3).value // 20
    (t1 + t3).timescale // 100
    (t1 + t3).seconds // 0.2
    
    // 掛け算
    let ti = CMTimeMultiply(CMTimeMake(10, 100), 3)
    ti.value // 30
    ti.timescale // 100
    ti.seconds // 0.3
    
    // 掛け算(float)
    let tf = CMTimeMultiplyByFloat64(CMTimeMake(10, 100), 1.5)
    tf.value // 150000000
    tf.timescale // 1000000000
    tf.seconds // 0.15
    
    // 掛け算(有理数)
    let tr = CMTimeMultiplyByRatio(CMTimeMake(10,100), 2, 3)
    tr.value // 20
    tr.timescale // 300
    tr.seconds // 0.06666666666666667
    
    // 比較
    t1 == t2 // true
    t1 > t3 // true
    
    // 引き算
    t1.seconds // 4.3
    t3.seconds // 2
    (t1 - t3).seconds // 2.3
    
    // エポック違い
    var t4 = CMTimeMakeWithEpoch(10, 100, 0)
    var t5 = CMTimeMakeWithEpoch(20, 100, 1)
    
    (t4 + t5).value // 30
    (t4 + t5).timescale // 100
    (t4 + t5).seconds // 0.3
    (t4 + t5).epoch // 1
    
    
}

do {
    // 最大、最小
    CMTimeMinimum(CMTimeMake(10,100), CMTimeMake(20,100)).value // 10
    CMTimeMaximum(CMTimeMake(10,100), CMTimeMake(20,100)).value // 20
    
    CMTimeMinimum(CMTimeMake(-10,100), CMTimeMake(-20,100)).value // -20
    CMTimeMaximum(CMTimeMake(-10,100), CMTimeMake(-20,100)).value // -10
    
    // 絶対値
    CMTimeAbsoluteValue(CMTimeMake(-10, 100)).value // 10
}

//: epoch
do {
    
    // 同一時刻epoch違い
    var t1 : CMTime = CMTimeMake(43, 10)
    var t2 : CMTime = CMTimeMake(43, 10)
    
    t1.epoch = 0
    t2.epoch = 1

    
    t1 == t2 // false
    t1 > t2 // false
    t1 < t2 // true
    
    // 時刻が過ぎている方がepoch小さい場合
    var t3 : CMTime = CMTimeMake(43, 10)
    var t4 : CMTime = CMTimeMake(40,10)
    t3.epoch = 0
    t4.epoch = 1
    
    t3 == t4 // false
    t3 > t4 // false
    t3 < t4 // true
    
}

```

# スケール変更

secondsを変えないでtimescaleを変更するので、valueが小数になる場合がある。これをどう丸めるのかを指定する。


```cmtime_scale.swift


//: # スケール変更(CMTimeRoundingMethod)


//: 四捨五入

do {
    let t : CMTime = CMTimeMake(104, 100).convertScale(10, method:.RoundHalfAwayFromZero)
    104 * (10 / 100) as Float // 10.4
    t.value // 10
    t.timescale // 10
    t.seconds // 1
    t.hasBeenRounded // true
}

do {
    let t : CMTime = CMTimeMake(104, 100).convertScale(110, method:.RoundHalfAwayFromZero)
    104 * (110 / 100) as Float // 114.4
    t.value // 114 (114.4が切り捨てられた)
    t.timescale // 110
    t.seconds // 1.036363636363636
}

do {
    let t : CMTime = CMTimeMake(105, 100).convertScale(10, method:.RoundHalfAwayFromZero)
    105 * (10 / 100) as Float // 10.5
    t.value // 11
    t.timescale // 10
    t.seconds // 1.1
}

do {
    let t : CMTime = CMTimeMake(105, 100).convertScale(110, method:.RoundHalfAwayFromZero)
    105 * (110 / 100)  as Float // 115.5
    t.value // 116 (115.5が繰り上がった)
    t.timescale // 110
    t.seconds // 1.054545454545454
}

do {
    let t : CMTime = CMTimeMake(-104, 100).convertScale(10, method: .RoundHalfAwayFromZero)
    -104 * (10 / 100) as Float // -10.4
    t.value // -10
    t.timescale // 10
    t.seconds // -1
}

do {
    let t = CMTimeMake(-105, 100).convertScale(10, method: .RoundHalfAwayFromZero)
    -105 * (10 / 100) as Float // -10.5
    t.value // -11
    t.timescale // 10
    t.seconds // -1.1
}


//: 切り上げ

do {
    let t = CMTimeMake(104, 100).convertScale(10, method:.RoundAwayFromZero)
    104 * (10 / 100) as Float // 10.4
    t.value // 11
    t.timescale // 10
    t.seconds // 1.1
}

do {
    let t = CMTimeMake(104, 100).convertScale(110, method:.RoundAwayFromZero)
    104 * (110 / 100) as Float // 114.4
    t.value // 115 (114.4が切り上げられた)
    t.timescale // 110
    t.seconds // 1.045454545454545
}

do {
    let t = CMTimeMake(105, 100).convertScale(10, method:.RoundAwayFromZero)
    105 * (10 / 100) as Float // 10.5
    t.value // 11
    t.timescale // 10
    t.seconds // 1.1
}

do {
    let t = CMTimeMake(105, 100).convertScale(110, method:.RoundAwayFromZero)
    105 * (110 / 100)  as Float // 115.5
    t.value // 116 (115.5が繰り上がった)
    t.timescale // 110
    t.seconds // 1.054545454545454
}

do {
    let t = CMTimeMake(100, 100).convertScale(10, method:.RoundAwayFromZero)
    100 * (10 / 100)  as Float // 10
    t.value // 10
    t.timescale // 10
    t.seconds // 1
}

do {
    let t = CMTimeMake(100, 100).convertScale(110, method:.RoundAwayFromZero)
    100 * (110 / 100)  as Float // 110
    t.value // 110
    t.timescale // 110
    t.seconds // 1
}

do {
    let t = CMTimeMake(-104, 100).convertScale(10, method:.RoundAwayFromZero)
    -104 * (10 / 100)  as Float // -10.4
    t.value // -11
    t.timescale // 10
    t.seconds // -1.1
}


//: 切り捨て
do {
    let t = CMTimeMake(104, 100).convertScale(10, method:.RoundTowardZero)
    t.value // 10
    t.timescale // 10
    t.seconds // 1
}

do {
    let t = CMTimeMake(104, 100).convertScale(110, method:.RoundTowardZero)
    t.value // 114 (114.4が切り捨てられた)
    t.timescale // 110
    t.seconds // 1.036363636363636
}

do {
    let t = CMTimeMake(105, 100).convertScale(10, method:.RoundTowardZero)
    t.value // 10
    t.timescale // 10
    t.seconds // 1
}

do {
    let t = CMTimeMake(105, 100).convertScale(110, method:.RoundTowardZero)
    t.value // 115 (115.5が切り捨てられた)
    t.timescale // 110
    t.seconds // 1.045454545454545
}

do {
    let t = CMTimeMake(-104, 100).convertScale(10, method: .RoundTowardZero)
    -104 * (10 / 100) as Float // -10.4
    t.value // -10
    t.timescale // 10
    t.seconds // -1
}



/*: 
### QuickTime
    もし、より小さなスケールへの変換ならRoundTowardZeroをつかう。
    もし、より大きなスケールへの変換ならRoundAwayFromZeroをつかう。
    また、 負の数を0には決して丸めない。その場合、つねに、-1/newTimesCale(絶対値をとると一番小さい負の値)を返す。
 */
do {
    let t = CMTimeMake(103, 100).convertScale(10, method:.QuickTime)
    103*(10/100) as Float // 10.3
    t.value // 10 切り捨てられた (より小さなスケールなので切り捨て)
    t.timescale // 10
    t.seconds // 1
}

do {
    let t = CMTimeMake(103, 100).convertScale(110, method:.QuickTime)
    103*(110/100) as Float // 113.3
    t.value // 114 切り上げられた (より大きなスケールなので切りあげ）
    t.timescale // 110
    t.seconds // 1.036363636363636
}

do {
    let t = CMTimeMake(105, 100).convertScale(10, method:.QuickTime)
    105*(10/100) as Float // 10.5
    t.value // 10 切り捨てられた (より小さなスケールなので切り捨て)
    t.timescale // 10
    t.seconds // 1
}

do {
    let t = CMTimeMake(105, 100).convertScale(110, method:.QuickTime)
    105*(110/100) as Float // 115.5
    t.value // 116 切り上げられた (より大きなスケールなので切り上げ)
    t.timescale // 110
    t.seconds // 1.054545454545454
}

do {
    let t = CMTimeMake(1, 100).convertScale(10, method: .QuickTime)
    1*(10/100) as Float // 0.1
    t.value // 0
}

do {
    let t = CMTimeMake(-101, 100).convertScale(10, method: .QuickTime)
    -101*(10/100) as Float // -10.1
    t.value // -1 // より小さなスケールなので切り捨て
}

do {
    let t = CMTimeMake(-101, 100).convertScale(110, method: .QuickTime)
    -101*(110/100) as Float // -111.1
    t.value // -1 // より大きなスケールなので切り上げ
}
    
do {
    let t = CMTimeMake(-2, 100).convertScale(10, method: .QuickTime)
    
    CMTimeMake(-2, 100).value // -1
    CMTimeMake(-2, 100).timescale // 100
    CMTimeMake(-2, 100).seconds // -0.01
    
    -2*(10/100) as Float // -0.2
    t.value // -1
    t.timescale
    t.seconds
    
    // スケールが小さくなったので切り捨てられるがvalueが０になるので、valueが-1に変更されている。
}


//: 負の方向へ丸める
do {
    let t = CMTimeMake(103, 100).convertScale(10, method:.RoundTowardNegativeInfinity)
    103*(10/100) as Float // 10.3
    t.value // 10 負の方向へ丸められた
    t.timescale // 10
    t.seconds // 1
    
}

do {
    let t = CMTimeMake(-103, 100).convertScale(10, method:.RoundTowardNegativeInfinity)
    -103*(10/100) as Float // -10.3
    t.value // -11 負の方向へ丸められた
    t.timescale // 10
    t.seconds // -1.1
    
    // 参考 切り捨てとの違い
    do {
        let t = CMTimeMake(103, 100).convertScale(10, method:.RoundTowardZero)
        103*(10/100) as Float // 10.3
        t.value // 10
        t.timescale // 10
        t.seconds // 1
    }
    
    do {
        let t = CMTimeMake(-103, 100).convertScale(10, method:.RoundTowardZero)
        -103*(10/100) as Float // -10.3
        t.value // -10
        t.timescale // 10
        t.seconds // -1
    }
    
}


//: 正の方向へ丸める
do {
    let t = CMTimeMake(103, 100).convertScale(10, method:.RoundTowardPositiveInfinity)
    103*(10/100) as Float // 10.3
    t.value // 11 正の方向へ丸められた
    t.timescale // 10
    t.seconds // 1.1
    
}

do {
    let t = CMTimeMake(-103, 100).convertScale(10, method:.RoundTowardPositiveInfinity)
    -103*(10/100) as Float // -10.3
    t.value // -10 正の方向へ丸められた
    t.timescale // 10
    t.seconds // -1
    
    // 参考 切り上げとの違い
    do {
        let t = CMTimeMake(103, 100).convertScale(10, method:.RoundAwayFromZero)
        103*(10/100) as Float // 10.3
        t.value // 11
        t.timescale // 10
        t.seconds // 1.1
    }
    
    do {
        let t = CMTimeMake(-103, 100).convertScale(10, method:.RoundAwayFromZero)
        -103*(10/100) as Float // -10.3
        t.value // -11
        t.timescale // 10
        t.seconds // -1.1
    }
    
}



```

# その他

```cmtime_other.swift



//: 0秒
do {
    let t : CMTime = kCMTimeZero
}

//: -∞
do {
    let tp = kCMTimePositiveInfinity
    let tn = kCMTimeNegativeInfinity

    tp.isValid // true
    tp.isPositiveInfinity // true
    tn.isNegativeInfinity // true
    
    tp.seconds // inf
    
}

```

# CMTimeRange

CMTimeRangeの使い方

## CMTimeRangeの作成

rangeは開始時間と幅の組、それぞれをCMTimeで設定する。
開始時間と終了時間の組をstart,endとしたとき、[start, end)となってendはこの範囲に含まれない事に注意。

```cmtimarange_sample.swift:

//: CMTimeRangeの作成
do {

    // 10秒から20秒間
    let s = CMTimeMake(100, 10)
    let d = CMTimeMake(200, 10)
    let range1 = CMTimeRange(start: s, duration: d)
    
    // 10秒から30秒まで
    let e = CMTimeMake(300, 10)
    let range2 = CMTimeRange(start: s, end:e)
    
    
    // ログ
    CMTimeRangeShow(range1) // {{100/10 = 10.000}, {200/10 = 20.000}}
    
    // 文字列
    CMTimeRangeCopyDescription(kCFAllocatorDefault,range1)

    range1 == range2 // true

    
    range1.isValid // true
    range1.isIndefinite // false
    range1.isEmpty // false

    e == range1.end // true
    e == range2.end // true
    
    d == range1.duration // true
    d == range2.duration // true
    
    
    // 10秒から無限
    let i = CMTimeRange(start:s, end:kCMTimePositiveInfinity)
    CMTimeShow(i.start) // {100/10 = 10.000}
    CMTimeShow(i.duration) // {+INFINITY}
    CMTimeShow(i.end) // {+INFINITY}
    i.isValid // true
    i.isIndefinite // false
    i.isEmpty // false
    
}

```

## CMTimeRangeの演算
### union

両者を含む最小のrangeをとることに注意

```cmtimerange_union.swift:

//: CMTimeRangeの演算


do {
    print("CMTimeRangeのunion")
    

    let t10 = CMTimeMake(100, 10)
    let t20 = CMTimeMake(200, 10)
    let t30 = CMTimeMake(300, 10)

    let r10_20 = CMTimeRange(start: t10, end: t20)
    let r20_30 = CMTimeRange(start: t20, end: t30)
    
    let r10_30 = CMTimeRange(start: t10, end: t30)
    
    CMTimeRangeShow(r10_20)// {{100/10 = 10.000}, {100/10 = 10.000}}
    CMTimeRangeShow(r20_30)// {{200/10 = 20.000}, {100/10 = 10.000}}
    CMTimeRangeShow(r10_30)// {{100/10 = 10.000}, {200/10 = 20.000}}
    
    // unionは合併
    let u1 : CMTimeRange = r10_20.union(r20_30)
    CMTimeRangeShow(u1)// {{100/10 = 10.000}, {200/10 = 20.000}

    r10_30 == r10_20.union(r20_30) // true
    
    
    let t0 = CMTimeMake(0, 10)
    let t1000 = CMTimeMake(10000,10)

    // 開始から１０秒
    let r0_10 = CMTimeRange(start: t0, duration: t10)
    
    // 1000秒後から10秒
    let r1000_1010 = CMTimeRange(start: t1000, duration: t10)
    
    CMTimeRangeShow(r0_10)// {{0/10 = 0.000}, {100/10 = 10.000}}
    CMTimeRangeShow(r1000_1010)// {{10000/10 = 1000.000}, {100/10 = 10.000}}
    
    let t1010 = t1000 + t10
    
    let r0_1010 = CMTimeRange(start: t0, end: t1010)
   
    // unionといっているが区間の合併の意味になっていなく
    // ここでは[0,10) + [1000,1010) = [0, 1010)　となっている。
    r0_1010 == r0_10.union(r1000_1010) // true
}
```

### intersection

共通部分を取る

```cmtimerange_intersection.swift:

do {
    print("CMTimeRangeのintersection")
    
    let t10 = CMTimeMake(100, 10)
    let t100 = CMTimeMake(1000, 10)
    
    let t50 = CMTimeMake(500, 10)
    let t150 = t100 + t50
    
    let r10_100 = CMTimeRange(start: t10, end: t100)
    let r50_150 = CMTimeRange(start: t50, end: t150)
    
    CMTimeRangeShow(r10_100) // {{100/10 = 10.000}, {900/10 = 90.000}}
    CMTimeRangeShow(r50_150) // {{500/10 = 50.000}, {1000/10 = 100.000}}
    
    // intersectは交叉
    let r = r10_100.intersection(r50_150)
    
    CMTimeRangeShow(r) // {{500/10 = 50.000}, {500/10 = 50.000}}
    
    let r50_100 = CMTimeRange(start: t50, end: t100)
    
    r50_100 == r // true
    
    
    
    // 全く交叉しない場合
    let r2 = CMTimeRange(start: t10, end: t50).intersection(CMTimeRange(start:t100, end:t150))
    CMTimeRangeShow(r2) // {{0/1 = 0.000}, {0/1 = 0.000}}

    r2.isValid // true
    r2.isIndefinite // false
    r2.isEmpty // true

}

```

### 包含

包含はtimeとrangeで２種類

``` cmtimerange_condlude.swift:
do {
    print("CMTimeRangeの包含")
    
    let t0 = CMTimeMake(0,10)
    let t10 = CMTimeMake(100, 10)
    let t20 = t10 + t10
    let t30 = t20 + t10
    
    CMTimeRangeShow(CMTimeRange(start: t0, end: t30)) // {{0/10 = 0.000}, {300/10 = 30.000}}
    
    print(" CMTimeが含まれているか")
    
    CMTimeRange(start: t0, end: t30).containsTime(t0) // true
    CMTimeRange(start: t0, end: t30).containsTime(t10) // true
    CMTimeRange(start: t0, end: t30).containsTime(t30) // false 最後尾は含まれない(半開区間になっている)

    print(" CMTimeRangeが含まれているか")
    CMTimeRange(start: t0, end: t30).containsTimeRange(CMTimeRange(start: t10, end: t20)) // true
    CMTimeRange(start: t0, end: t10).containsTimeRange(CMTimeRange(start: t20, end: t30)) // false
    CMTimeRange(start: t0, end: t30).containsTimeRange(CMTimeRange(start: t20, end: t30)) // true
    
    
}
```