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は0になる
@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が0になるので、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)
// 開始から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で2種類
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
}