Skip to content

Commit

Permalink
Improve CTS calculation.
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo4405 committed Feb 9, 2025
1 parent f2ab619 commit 453ce29
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 14 deletions.
10 changes: 10 additions & 0 deletions HaishinKit/Sources/RTMP/RTMPFoundation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,13 @@ enum RTMPVideoCodec: UInt8 {
}
}
}


extension CMSampleBuffer {
func getCompositionTime(_ offset: TimeInterval) -> Int32 {
guard decodeTimeStamp.isValid else {
return 0
}
return max(0, Int32(((presentationTimeStamp - decodeTimeStamp).seconds + offset) * 1000))
}
}
10 changes: 7 additions & 3 deletions HaishinKit/Sources/RTMP/RTMPMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ struct RTMPAudioMessage: RTMPMessage {
7.1.5. Video Message (9)
*/
struct RTMPVideoMessage: RTMPMessage {
static let ctsOffset = 0.066

// MARK: RTMPMessage
let type: RTMPMessageType = .video
let streamId: UInt32
Expand Down Expand Up @@ -526,20 +528,22 @@ struct RTMPVideoMessage: RTMPMessage {
}
}

init?(streamId: UInt32, timestamp: UInt32, compositionTime: Int32, sampleBuffer: CMSampleBuffer?) {
init?(streamId: UInt32, timestamp: UInt32, sampleBuffer: CMSampleBuffer?) {
guard let sampleBuffer, let data = try? sampleBuffer.dataBuffer?.dataBytes() else {
return nil
}
self.streamId = streamId
self.timestamp = timestamp
let keyframe = !sampleBuffer.isNotSync
switch sampleBuffer.formatDescription?.mediaSubType {
case .h264?:
case .h264:
let compositionTime = sampleBuffer.getCompositionTime(Self.ctsOffset)
var buffer = Data([((keyframe ? RTMPFrameType.key.rawValue : RTMPFrameType.inter.rawValue) << 4) | RTMPVideoCodec.avc.rawValue, RTMPAVCPacketType.nal.rawValue])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
payload = buffer
case .hevc?:
case .hevc:
let compositionTime = sampleBuffer.getCompositionTime(Self.ctsOffset)
var buffer = Data([0b10000000 | ((keyframe ? RTMPFrameType.key.rawValue : RTMPFrameType.inter.rawValue) << 4) | RTMPVideoPacketType.codedFrames.rawValue, 0x68, 0x76, 0x63, 0x31])
buffer.append(contentsOf: compositionTime.bigEndian.data[1..<4])
buffer.append(data)
Expand Down
3 changes: 1 addition & 2 deletions HaishinKit/Sources/RTMP/RTMPStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -735,11 +735,10 @@ extension RTMPStream: HKStream {
if sampleBuffer.formatDescription?.isCompressed == true {
do {
let decodeTimeStamp = sampleBuffer.decodeTimeStamp.isValid ? sampleBuffer.decodeTimeStamp : sampleBuffer.presentationTimeStamp
let compositionTime = videoTimestamp.getCompositionTime(sampleBuffer)
let timedelta = try videoTimestamp.update(decodeTimeStamp)
frameCount += 1
videoFormat = sampleBuffer.formatDescription
guard let message = RTMPVideoMessage(streamId: id, timestamp: timedelta, compositionTime: compositionTime, sampleBuffer: sampleBuffer) else {
guard let message = RTMPVideoMessage(streamId: id, timestamp: timedelta, sampleBuffer: sampleBuffer) else {
return
}
doOutput(.one, chunkStreamId: .video, message: message)
Expand Down
9 changes: 0 additions & 9 deletions HaishinKit/Sources/RTMP/RTMPTimestamp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ protocol RTMPTimeConvertible {
}

private let kRTMPTimestamp_defaultTimeInterval: TimeInterval = 0
private let kRTMPTimestamp_compositiionTimeOffset = CMTime(value: 3, timescale: 30)

struct RTMPTimestamp<T: RTMPTimeConvertible> {
enum Error: Swift.Error {
Expand Down Expand Up @@ -56,14 +55,6 @@ struct RTMPTimestamp<T: RTMPTimeConvertible> {
updatedAt = kRTMPTimestamp_defaultTimeInterval
timedeltaFraction = kRTMPTimestamp_defaultTimeInterval
}

func getCompositionTime(_ sampleBuffer: CMSampleBuffer) -> Int32 {
guard sampleBuffer.decodeTimeStamp.isValid, sampleBuffer.decodeTimeStamp != sampleBuffer.presentationTimeStamp else {
return 0
}
let compositionTime = (sampleBuffer.presentationTimeStamp + kRTMPTimestamp_compositiionTimeOffset).seconds - updatedAt
return Int32(compositionTime * 1000)
}
}

extension AVAudioTime: RTMPTimeConvertible {
Expand Down

0 comments on commit 453ce29

Please sign in to comment.