VideoCustomCompositor.swift:
import Foundation
import AVFoundation
import RealityKit
enum CustomCompositorError: Int, Error, LocalizedError {
case ciFilterFailedToProduceOutputImage = -1_000_001
case notSupportingMoreThanOneSources
var errorDescription: String? {
switch self {
case .ciFilterFailedToProduceOutputImage:
return "CIFilter does not produce an output image."
case .notSupportingMoreThanOneSources:
return "This custom compositor does not support blending of more than one source."
}
}
}
nonisolated
final class VideoCustomCompositor: NSObject, AVVideoCompositing, @unchecked Sendable {
private var isCancelled = false
private var request: AVAsynchronousVideoCompositionRequest?
var sourcePixelBufferAttributes: [String: any Sendable]? = [
String(kCVPixelBufferPixelFormatTypeKey): [kCVPixelFormatType_32BGRA],
String(kCVPixelBufferMetalCompatibilityKey): true // Critical!
]
var requiredPixelBufferAttributesForRenderContext: [String: any Sendable] = [
String(kCVPixelBufferPixelFormatTypeKey):[kCVPixelFormatType_32BGRA],
String(kCVPixelBufferMetalCompatibilityKey): true
]
func renderContextChanged(_ newRenderContext: AVVideoCompositionRenderContext) {
return
}
func cancelAllPendingVideoCompositionRequests() {
isCancelled = true
request?.finishCancelledRequest()
request = nil
}
func startRequest(_ request: AVAsynchronousVideoCompositionRequest) {
self.request = request
isCancelled = false // Reset cancellation status
// guard let outputPixelBuffer = request.renderContext.newPixelBuffer() else {
// print("No valid pixel buffer found. Returning.")
// request.finish(with: CustomCompositorError.ciFilterFailedToProduceOutputImage)
// return
// }
guard let requiredTrackIDs = request.videoCompositionInstruction.requiredSourceTrackIDs, !requiredTrackIDs.isEmpty else {
print("No valid track IDs found in composition instruction.")
return
}
let sourceCount = requiredTrackIDs.count
if sourceCount > 1 {
request.finish(with: CustomCompositorError.notSupportingMoreThanOneSources)
return
}
if sourceCount == 1 {
let sourceID = requiredTrackIDs[0]
let sourceBuffer = request.sourceFrame(byTrackID: sourceID.value(of: Int32.self)!)!
request.finish(withComposedVideoFrame: sourceBuffer)
}
// request.finish(withComposedVideoFrame: outputPixelBuffer)
}
}