Files
HaishinKit.swift/RTMPHaishinKit/Sources/RTMP/RTMPTimestamp.swift
2025-07-13 12:43:53 +09:00

80 lines
2.3 KiB
Swift

import AVFoundation
import CoreMedia
import Foundation
protocol RTMPTimeConvertible {
var seconds: TimeInterval { get }
}
private let kRTMPTimestamp_defaultTimeInterval: TimeInterval = 0
struct RTMPTimestamp<T: RTMPTimeConvertible> {
enum Error: Swift.Error {
case invalidSequence
}
private var startedAt = kRTMPTimestamp_defaultTimeInterval
private var updatedAt = kRTMPTimestamp_defaultTimeInterval
private var timedeltaFraction: TimeInterval = kRTMPTimestamp_defaultTimeInterval
mutating func update(_ value: T) throws -> UInt32 {
guard updatedAt < value.seconds else {
throw Error.invalidSequence
}
if startedAt == 0 {
startedAt = value.seconds
updatedAt = value.seconds
return 0
}
var timedelta = (value.seconds - updatedAt) * 1000
timedeltaFraction += timedelta.truncatingRemainder(dividingBy: 1)
if 1 <= timedeltaFraction {
timedeltaFraction -= 1
timedelta += 1
}
updatedAt = value.seconds
return UInt32(timedelta)
}
mutating func update(_ message: some RTMPMessage, chunkType: RTMPChunkType) {
switch chunkType {
case .zero:
if startedAt == 0 {
startedAt = TimeInterval(message.timestamp) / 1000
updatedAt = TimeInterval(message.timestamp) / 1000
} else {
updatedAt = TimeInterval(message.timestamp) / 1000
}
default:
updatedAt += TimeInterval(message.timestamp) / 1000
}
}
mutating func clear() {
startedAt = kRTMPTimestamp_defaultTimeInterval
updatedAt = kRTMPTimestamp_defaultTimeInterval
timedeltaFraction = kRTMPTimestamp_defaultTimeInterval
}
}
extension AVAudioTime: RTMPTimeConvertible {
var seconds: TimeInterval {
AVAudioTime.seconds(forHostTime: hostTime)
}
}
extension RTMPTimestamp where T == AVAudioTime {
var value: AVAudioTime {
return AVAudioTime(hostTime: AVAudioTime.hostTime(forSeconds: updatedAt))
}
}
extension CMTime: RTMPTimeConvertible {
}
extension RTMPTimestamp where T == CMTime {
var value: CMTime {
return CMTime(seconds: updatedAt, preferredTimescale: 1000)
}
}