You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
44 lines
2.0 KiB
44 lines
2.0 KiB
import AVFoundation
|
|
import CoreMedia
|
|
import CoreVideo
|
|
|
|
/// Hands pixel buffers to an `AVSampleBufferVideoRenderer`. Safe to call from the receive thread.
|
|
public final class VideoSink: @unchecked Sendable {
|
|
private let renderer: AVSampleBufferVideoRenderer
|
|
// Only touched from the single thread that calls `display(_:)`.
|
|
private var formatDescription: CMVideoFormatDescription?
|
|
|
|
public init(renderer: AVSampleBufferVideoRenderer) {
|
|
self.renderer = renderer
|
|
}
|
|
|
|
public func display(_ pixelBuffer: CVPixelBuffer) {
|
|
if renderer.status == .failed || renderer.requiresFlushToResumeDecoding {
|
|
renderer.flush()
|
|
}
|
|
|
|
if formatDescription.map({ !CMVideoFormatDescriptionMatchesImageBuffer($0, imageBuffer: pixelBuffer) }) ?? true {
|
|
formatDescription = nil
|
|
CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: pixelBuffer, formatDescriptionOut: &formatDescription)
|
|
}
|
|
guard let formatDescription else { return }
|
|
|
|
var timing = CMSampleTimingInfo(duration: .invalid, presentationTimeStamp: CMClockGetTime(CMClockGetHostTimeClock()), decodeTimeStamp: .invalid)
|
|
var sampleBuffer: CMSampleBuffer?
|
|
guard CMSampleBufferCreateReadyWithImageBuffer(
|
|
allocator: nil, imageBuffer: pixelBuffer, formatDescription: formatDescription,
|
|
sampleTiming: &timing, sampleBufferOut: &sampleBuffer
|
|
) == noErr, let sampleBuffer else { return }
|
|
|
|
if let attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true),
|
|
CFArrayGetCount(attachments) > 0 {
|
|
let dictionary = unsafeBitCast(CFArrayGetValueAtIndex(attachments, 0), to: CFMutableDictionary.self)
|
|
CFDictionarySetValue(dictionary,
|
|
Unmanaged.passUnretained(kCMSampleAttachmentKey_DisplayImmediately).toOpaque(),
|
|
Unmanaged.passUnretained(kCFBooleanTrue).toOpaque())
|
|
}
|
|
|
|
renderer.enqueue(sampleBuffer)
|
|
}
|
|
}
|