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.
Musubi/Packages/MusubiKit/Sources/VideoOutput/VideoSink.swift

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)
}
}

Powered by TurnKey Linux.