CMTime

QTKit では、QTTime で時間の処理を行っていたが、AVFoundation の AVKit では CMTime を使う。

QTTime は、timeValue, timeScale, flags でできていたが、CMTime は value, timescale, flags, epoch から成る。QTTime からの変換時は、timeValue を value に timeScale を timescale(s が小文字)に代える必要がある。

CMTime (cmTime) から value, timescale を取り出したい場合は、次のようにする。

timeValue = cmTime.value

timeScale = cmTime.timescale

(timeValue = qtTime.timeValue)

(timeScale = qtTime.timeScale)

CMTime は、CMTimeMake で CMTime value もしくは、CMTimeMakeWithSeconds で秒数と timescale を指定して作ることができる(他にもあるが)。

cmTime = CMTimeMake(value,timescale)

cmTime = CMTimeMakeWithSeconds(seconds,timescale)

(qtTime = QTMakeTime(timeValue,timeScale))

QuickTime は d:hh:mm:ss.xxx という書式で時間を扱うこともあったので、QTTimeFromString という便利なのがあったのだが、CMTime にはどうもないらしい。なので、とりあえず hh:mm:ss.xxx の時間表時を CMTime にするために、次のようなスクリプトを書いてみた。もしちゃんとした方法が見つかったり、もっといい方法があったら、追記するか書き換えるつもり。

timeCodeText.match(/[(\d+?):(\d+?):(\d+?)\.(\d+)\]/)

cmTimeValue = ($1.to_i * 3600 + $2.to_i * 60 + $3.to_i) * 1000 + $4.to_i

cmTime = CMTimeMake(cmTimeValue,1000)

cmTime = CMTimeMakeWithSeconds(initTime/1000.0,playerItem.timescale)

何をしているかというと、hh:mm:ss.xxx という形式を match で後方参照で取り出して CMTime value を計算しているだけ。ここでは、テキストの時間表示の time scale が 1000 ということにしているが、QTTime でよくある 600 の場合は 1000 の所を 600 に代えればいけるはず。そして、CMTimeMake で、timescale を 1000 にして作るか、1000.0 で割ったものが秒数になるので、それと、扱っているメディアファイルから作った AVPlayerItem の timescale を使って、CMTimeMakeWithSeconds で CMTime にしている。

CMTime から情報を得るものには、次のようなものがある。

absoluteTime = CMTimeAbsoluteValue(cmTime)

seconds = CMTimeGetSeconds(cmTime)

newTime = CMTimeConvertScale(cmTime,newTimeScale,CMTimeRoundingMethod)

CMTimeShow(cmTime)

(newTime = QTMakeTimeScaled(time,timeScale))

CMTimeAbsoluteValue(cmTime) - value が正の場合は CMTime そのものが返る。負の値の場合には正の値が返る(はず)。

CMTimeGetSeconds(cmTime) - CMTime の秒数が返ってくる。つまり value/timescale の値。64-bit float で返ると書いてあるが、RubyMotion だと float かな?

CMTimeConvertScale(cmTime,newTimeScale,CMTimeRoundingMethod) - kCMTimeRoundingMethod_RoundHalfAwayFromZero (1), kCMTimeRoundingMethod_RoundTowardZero (2), kCMTimeRoundingMethod_RoundAwayFromZero (3), kCMTimeRoundingMethod_QuickTime (4), kCMTimeRoundingMethod_RoundTowardPositiveInfinity (5), kCMTimeRoundingMethod_RoundTowardNegativeInfinity (6)。

CMTimeShow(cmTime) - どのように使うのかはわからないが、中身を見るもの?という感じで、value/timescale = seconds (それぞれ実際の値)というのが返ってきた。

2 つの CMTime を比較する場合は、次の方法がある。

comparison = CMTimeCompare(cmTime1,cmTime2)

largerCMTime = CMTimeMaximum(cmTime1,cmTime2)

smallerCMTime = CMTimeMinimum(cmTime1,cmTime2)

(QTTimeCompare(qtTime1,qtTime2))

CMTimeCompare(cmTime1,cmTime2) - 1: cmTime1 > cmTime2, -1: cmTime1 < cmTime2, 0: cmTime1 = cmTime2。QTTimeCompare と同じっぽい。

CMTimeMaximum(cmTime1,cmTime2) - 2 つの CMTime のうち大きい方が返る。

CMTimeMinimum(cmTime1,cmTime2) - 2 つの CMTime のうち小さい方が返る。

newCMTime = CMTimeAdd(cmTime1,cmTime2)

newCMTime = CMTimeSubtract(cmTime1,cmTime2)

newCMTime = CMTimeMultiply(cmTime1,multiplier)

QTTimeIncrement(qtTime1,qtTime2)

QTTimeDecrement(qtTime1,qtTime2)

CMTimeAdd(cmTime1,cmTime2) - 2 つの CMTime を加算する。QTTimeIncrement と同じっぽい。

CMTimeSubtract(cmTime1,cmTime2) - cmTime1 から cmTime2 を引く。QTTimeDecrement と同じっぽい。

CMTimeMultiply(cmTime,multiplier) - cmTime を multiplier で乗算する。multiplier は 32-bit integer。つまり、32-bit で扱える範囲の integer。

QTTimeRange に対応するのが CMTimeRange。CMTimeRange は start と duration が CMTime で保持されている。

startTime = cmTimeRange.start

duration = cmTimeRange.duration

(startTime = qtTimeRange.time)

(duration = qtTimeRange.duration)

CMTimeRange を得るには、CMTimeRangeMake で、start と duration を CMTime で指定する。もしくは、CMTimeRangeFromTimeToTime で、start と end を CMTime で指定する。QTTimeRange のときも同じで、QTMakeTimeRange で start にあたる time と duration を指定した。

timeRange = CMTimeRangeMake(start,duration)

timeRange = CMTimeRangeFromTimeToTime(start,end)

(timeRange = QTMakeTimeRange(time,duration))

CMTimeRange の情報を得る方法。

endTime = CMTimeRangeGetEnd(cmRange)

crossedRange = CMTimeRangeGetIntersection(cmRange1,cmRange2)

(endTime = QTTimeRangeEnd(qtRange))

(crossedRange = QTIntersectionTimeRange(qtRange1,qtRange2))

CMTimeRangeGetEnd(cmRange) - cmRange の終わりの時間が CMTime で得られる。

CMTimeRangeGetIntersection(cmRange1,cmRange2) - cmRange1 と cmRange2 が重なる部分が CMTimeRange として返る。

CMTimeRange が同じかどうか、time もしくは range を含むかどうかのチェックができる。

true/false = CMTimeRangeEqual(cmRange1,cmRange2)

true/false = CMTimeRangeContainsTime(cmRange,cmTime)

true/false = CMTimeRangeContainsTimeRange(cmRange1,cmRange2)

(true/false = QTEqualTimeRanges(qtRange1,qtRange2))

(true/false = QTTimeInTimeRange(time,qtRange))

CMTimeRangeEqual(cmRange1,cmRange2) - cmRange1 と cmRange2 が等しいかどうかのチェック。

CMTimeRangeContainsTime(cmRange,cmTime) - cmTime が cmRange に含まれているかどうかのチェック。

CMTimeRangeContainsTimeRange(cmRange1,cmRange2) - cmRange2 が cmRange1 に含まれているかどうかのチェック。

2 つの range で長さが異なる場合に、range が変換された場合に指定した CMTime および duration が取る値を返す方法もある。

mappedTime = CMTimeMapTimeFromRangeToRange(cmTime,cmRange1,cmRange2)

mappedDuration = CMTimeMapDurationFromRangeToRange(duration,cmRange1,cmRange2)

CMTimeMapTimeFromRangeToRange(cmTime,cmRange1,cmRange2) - cmRange1 が cmRange2 に変換された場合(トラックを追加するなどして cmRange1 を cmRange2 に合わせる場合など)に、cmRange1 での cmTime の値が、cmRange2 になった場合に取る値を CMTime で返す。

CMTimeMapDurationFromRangeToRange(cmTime,cmRange1,cmRange2) - cmRange1 が cmRange2 に変換された場合(トラックを追加するなどして cmRange1 を cmRange2 に合わせる場合など)に、cmRange1 での duration (CMTime) が、cmRange2 になった場合に取る値を CMTime で返す。上とほぼ同じ?