Skip to content

Commit

Permalink
Miguration #1603
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo4405 committed Oct 14, 2024
1 parent efd2a76 commit 11932b9
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 43 deletions.
4 changes: 2 additions & 2 deletions Sources/Codec/VTSessionMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum VTSessionMode {
height: Int32(videoCodec.settings.videoSize.height),
codecType: videoCodec.settings.format.codecType,
encoderSpecification: nil,
imageBufferAttributes: videoCodec.imageBufferAttributes(.compression) as CFDictionary?,
imageBufferAttributes: videoCodec.makeImageBufferAttributes(.compression) as CFDictionary?,
compressedDataAllocator: nil,
outputCallback: nil,
refcon: nil,
Expand Down Expand Up @@ -43,7 +43,7 @@ enum VTSessionMode {
allocator: kCFAllocatorDefault,
formatDescription: formatDescription,
decoderSpecification: nil,
imageBufferAttributes: videoCodec.imageBufferAttributes(.decompression) as CFDictionary?,
imageBufferAttributes: videoCodec.makeImageBufferAttributes(.decompression) as CFDictionary?,
outputCallback: nil,
decompressionSessionOut: &session
)
Expand Down
42 changes: 24 additions & 18 deletions Sources/Codec/VideoCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,17 @@ final class VideoCodec {
}
var needsSync = true
var passthrough = true
var outputStream: AsyncStream<CMSampleBuffer> {
AsyncStream<CMSampleBuffer> { continuation in
self.continuation = continuation
}
}
var frameInterval = VideoCodec.frameInterval
var expectedFrameRate = MediaMixer.defaultFrameRate
/// The running value indicating whether the VideoCodec is running.
private var startedAt: CMTime = .zero
private var continuation: AsyncStream<CMSampleBuffer>.Continuation?
private var isInvalidateSession = true
private var presentationTimeStamp: CMTime = .zero
private(set) var isRunning = false
private(set) var inputFormat: CMFormatDescription? {
didSet {
Expand All @@ -45,15 +53,6 @@ final class VideoCodec {
}
}
private(set) var outputFormat: CMFormatDescription?
var outputStream: AsyncStream<CMSampleBuffer> {
AsyncStream<CMSampleBuffer> { continuation in
self.continuation = continuation
}
}
private var startedAt: CMTime = .zero
private var continuation: AsyncStream<CMSampleBuffer>.Continuation?
private var isInvalidateSession = true
private var presentationTimeStamp: CMTime = .invalid

func append(_ sampleBuffer: CMSampleBuffer) {
guard isRunning else {
Expand All @@ -68,18 +67,22 @@ final class VideoCodec {
session = try VTSessionMode.compression.makeSession(self)
}
}
guard let session else {
guard let session, let continuation else {
return
}
if let continuation {
if sampleBuffer.formatDescription?.isCompressed == true {
try session.convert(sampleBuffer, continuation: continuation)
} else {
if useFrame(sampleBuffer.presentationTimeStamp) {
try session.convert(sampleBuffer, continuation: continuation)
}
}
} catch {
logger.error(error)
}
}

func imageBufferAttributes(_ mode: VTSessionMode) -> [NSString: AnyObject]? {
func makeImageBufferAttributes(_ mode: VTSessionMode) -> [NSString: AnyObject]? {
switch mode {
case .compression:
var attributes: [NSString: AnyObject] = [:]
Expand All @@ -96,14 +99,17 @@ final class VideoCodec {
}
}

private func willDropFrame(_ presentationTimeStamp: CMTime) -> Bool {
private func useFrame(_ presentationTimeStamp: CMTime) -> Bool {
guard startedAt <= presentationTimeStamp else {
return true
return false
}
guard Self.frameInterval < frameInterval else {
guard self.presentationTimeStamp < presentationTimeStamp else {
return false
}
return presentationTimeStamp.seconds - self.presentationTimeStamp.seconds <= frameInterval
guard Self.frameInterval < frameInterval else {
return true
}
return frameInterval <= presentationTimeStamp.seconds - self.presentationTimeStamp.seconds
}

#if os(iOS) || os(tvOS) || os(visionOS)
Expand Down Expand Up @@ -164,7 +170,7 @@ extension VideoCodec: Runner {
needsSync = true
inputFormat = nil
outputFormat = nil
presentationTimeStamp = .invalid
presentationTimeStamp = .zero
continuation?.finish()
startedAt = .zero
#if os(iOS) || os(tvOS) || os(visionOS)
Expand Down
34 changes: 21 additions & 13 deletions Sources/RTMP/RTMPStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -739,15 +739,19 @@ extension RTMPStream: HKStream {
switch sampleBuffer.formatDescription?.mediaType {
case .video:
if sampleBuffer.formatDescription?.isCompressed == true {
let decodeTimeStamp = sampleBuffer.decodeTimeStamp.isValid ? sampleBuffer.decodeTimeStamp : sampleBuffer.presentationTimeStamp
let compositionTime = videoTimestamp.getCompositionTime(sampleBuffer)
let timedelta = videoTimestamp.update(decodeTimeStamp)
frameCount += 1
videoFormat = sampleBuffer.formatDescription
guard let message = RTMPVideoMessage(streamId: id, timestamp: timedelta, compositionTime: compositionTime, sampleBuffer: sampleBuffer) else {
return
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 {
return
}
doOutput(.one, chunkStreamId: .video, message: message)
} catch {
logger.warn(error)
}
doOutput(.one, chunkStreamId: .video, message: message)
} else {
outgoing.append(sampleBuffer)
if sampleBuffer.formatDescription?.isCompressed == false {
Expand Down Expand Up @@ -775,12 +779,16 @@ extension RTMPStream: HKStream {
public func append(_ audioBuffer: AVAudioBuffer, when: AVAudioTime) {
switch audioBuffer {
case let audioBuffer as AVAudioCompressedBuffer:
let timedelta = audioTimestamp.update(when)
audioFormat = audioBuffer.format
guard let message = RTMPAudioMessage(streamId: id, timestamp: timedelta, audioBuffer: audioBuffer) else {
return
do {
let timedelta = try audioTimestamp.update(when)
audioFormat = audioBuffer.format
guard let message = RTMPAudioMessage(streamId: id, timestamp: timedelta, audioBuffer: audioBuffer) else {
return
}
doOutput(.one, chunkStreamId: .audio, message: message)
} catch {
logger.warn(error)
}
doOutput(.one, chunkStreamId: .audio, message: message)
default:
outgoing.append(audioBuffer, when: when)
if audioBuffer is AVAudioPCMBuffer && audioSampleAccess {
Expand Down
26 changes: 16 additions & 10 deletions Sources/RTMP/RTMPTimestamp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,31 @@ private let kRTMPTimestamp_defaultTimeInterval: TimeInterval = 0
private let kRTMPTimestamp_compositiionTimeOffset = CMTime(value: 3, timescale: 30)

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) -> UInt32 {
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
} else {
var timedelta = (value.seconds - updatedAt) * 1000
timedeltaFraction += timedelta.truncatingRemainder(dividingBy: 1)
if 1 <= timedeltaFraction {
timedeltaFraction -= 1
timedelta += 1
}
updatedAt = value.seconds
return 0.0 <= timedelta ? UInt32(timedelta) : UInt32.min
}
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) {
Expand Down

0 comments on commit 11932b9

Please sign in to comment.