40 lines
1.3 KiB
Swift
40 lines
1.3 KiB
Swift
|
|
import Foundation
|
||
|
|
|
||
|
|
/// Non-drop-frame timecode. Frame count + fps.
|
||
|
|
public struct Timecode: Sendable, Equatable, CustomStringConvertible {
|
||
|
|
public var frames: Int
|
||
|
|
public var fps: Int
|
||
|
|
|
||
|
|
public init(frames: Int, fps: Int) {
|
||
|
|
self.frames = frames
|
||
|
|
self.fps = fps
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Parse HH:MM:SS:FF.
|
||
|
|
public static func parse(_ s: String, fps: Int) -> Timecode? {
|
||
|
|
let parts = s.split(separator: ":")
|
||
|
|
guard parts.count == 4,
|
||
|
|
let h = Int(parts[0]), let m = Int(parts[1]),
|
||
|
|
let sec = Int(parts[2]), let f = Int(parts[3]),
|
||
|
|
m < 60, sec < 60, f < fps else { return nil }
|
||
|
|
return Timecode(frames: ((h * 3600 + m * 60 + sec) * fps) + f, fps: fps)
|
||
|
|
}
|
||
|
|
|
||
|
|
public var description: String {
|
||
|
|
let totalSeconds = frames / fps
|
||
|
|
let f = frames % fps
|
||
|
|
let h = totalSeconds / 3600
|
||
|
|
let m = (totalSeconds % 3600) / 60
|
||
|
|
let s = totalSeconds % 60
|
||
|
|
return String(format: "%02d:%02d:%02d:%02d", h, m, s, f)
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func + (l: Timecode, r: Timecode) -> Timecode {
|
||
|
|
Timecode(frames: l.frames + r.frames, fps: l.fps)
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func - (l: Timecode, r: Timecode) -> Timecode {
|
||
|
|
Timecode(frames: max(0, l.frames - r.frames), fps: l.fps)
|
||
|
|
}
|
||
|
|
}
|