How to Check If Cmtime Is Valid in Swift

What kinds of CMTime are invalid?

There are five possible states:

  1. +Infinity: This is similar to Float.Infinity. This is a valid value, just greater than any finite number. How might you use it? For example, imagine an API that gives you information about a time range within a video, identified by two CMTimes. You might invoke it with (-Infinity, +Infinity) to ask for information about the entire video.
  2. -Infinity: This is again similar to -Float.Infinity.
  3. Indefinite: This is similar to Float.NaN, as I understand. Use this when you don't know what value to use, like the duration of a live stream, as Apple suggests. It wouldn't be right to use infinity, for example, since a live stream doesn't go on for forever. It has a finite duration; we just don't know it yet.
  4. Invalid: This is a CMTime structure that doesn't obey the rules of CMTime. I assume that means things like a zero or negative denominator. Since CMTime is a C struct, it's not encapsulated, so someone can create one with invalid values like this. C structs can't have initialisers that throw an exception or return nil.
  5. Numeric: This is the normal case of a finite value. Use CMTIME_IS_NUMERIC to check for this. It returns false for all the weird cases above.

Check if CMTime is in CMTimeRange

CMTimeRange has a

func containsTime(_ time: CMTime) -> Bool

method, so you can simply check

if range.containsTime(time) {
// ...
}

Remark: The documentation seems to be outdated, the global
function CMTimeRangeContainsTime() is imported as a member function
to Swift.

Check CMTime Equals

I found the following way to check by converting it to seconds

if(CMTimeGetSeconds(myTime) == CMTimeGetSeconds(kCMTimeZero))
{
// do something...
}

swift and CMTimeMake

CMTime and CMTimeMake are defined in the "CoreMedia" module, therefore you have to

import CoreMedia

Then this compiles without problems:

let seconds : Int64 = 10
let preferredTimeScale : Int32 = 1
let aMovieFileOutput = AVCaptureMovieFileOutput()
let maxDuration : CMTime = CMTimeMake(seconds, preferredTimeScale)
aMovieFileOutput.maxRecordedDuration = maxDuration

Update for Swift 3:

let maxDuration = CMTime(seconds: Double(seconds), preferredTimescale: 1)

Modern way of CMTime and CMTimeRange in plist using Swift

Use PropertyListEncoder/PropertyListDecoder with a Codable model type. CMTime and CMTimeRange are not Codable conformant by default, so you need to add the conformance yourself.

extension CMTime: Codable {
enum CodingKeys: String, CodingKey {
case value
case timescale
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let value = try container.decode(CMTimeValue.self, forKey: .value)
let timescale = try container.decode(CMTimeScale.self, forKey: .timescale)
self.init(value: value, timescale: timescale)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
try container.encode(timescale, forKey: .timescale)
}
}

extension CMTimeRange: Codable {
enum CodingKeys: String, CodingKey {
case start
case duration
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let start = try container.decode(CMTime.self, forKey: .start)
let duration = try container.decode(CMTime.self, forKey: .duration)
self.init(start: start, duration: duration)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(start, forKey: .start)
try container.encode(duration, forKey: .duration)
}
}

struct Model: Codable {
let time: CMTime
let timeRange: CMTimeRange
}

let model = Model(time: CMTime.invalid, timeRange: CMTimeRange(start: CMTime(), end: CMTime()))
do {
let encodedData = try PropertyListEncoder().encode(model)
let decodedData = try PropertyListDecoder().decode(Model.self, from: encodedData)
print(decodedData)
} catch {
print(error)
}

I do not know how to properly use CMTimeRange and CMTime in Swift or Objc

I have solved the above problem with the code below.

let musicAsset = AVAsset(url: (item?.assetURL)!)

self.scwaveScrollView.waveformView.asset = musicAsset
self.scwaveScrollView.waveformView.precision = 1

var duration = CMTimeMakeWithSeconds( 1.0 * CMTimeGetSeconds(self.scwaveScrollView.waveformView.asset.duration), 100000)

self.scwaveScrollView.waveformView.timeRange = CMTimeRangeMake(CMTimeMakeWithSeconds(0, 10000), duration);
let start = self.scwaveScrollView.waveformView.timeRange.start
duration = CMTime(seconds: 15, preferredTimescale: 1)

self.scwaveScrollView.waveformView.timeRange = CMTimeRangeMake(start, duration)

CMTIME_COMPARE_INLINE in Swift 3?

CMTime is imported as a Comparable type in Swift 3, so, you have no need to use CMTIME_COMPARE_INLINE.

Assuming _previousSecondTimestamps is of type [CMTime] (*1), you can write something like this in Swift 3:

    let oneSecond = CMTime(seconds: 1.0, preferredTimescale: 1)
let oneSecondAgo = timestamp - oneSecond

while !_previousSecondTimestamps.isEmpty && _previousSecondTimestamps[0] < oneSecondAgo {
_previousSecondTimestamps.remove(at: 0)
}

*1 In Swift, you can declare an Array of CMTime directly, you have no need to use NSMutableArray containing NSValue.



Related Topics



Leave a reply



Submit