11import AVFoundation
2- import CoreImage
32import CoreVideo
4- import Vision
53
64final class CameraVideoCompositor : NSObject , AVVideoCompositing , @unchecked Sendable {
75 var sourcePixelBufferAttributes : [ String : any Sendable ] ? {
@@ -236,22 +234,7 @@ final class CameraVideoCompositor: NSObject, AVVideoCompositing, @unchecked Send
236234 $0. containsTime ( compositionTime)
237235 }
238236
239- let rawWebcamImage = createImage ( from: webcamBuffer, colorSpace: colorSpace)
240- var mirrorHandledBySegmentation = false
241- let webcamImage : CGImage ? = {
242- guard let raw = rawWebcamImage else { return nil }
243- if case . none = instruction. cameraBackgroundStyle { return raw }
244- if let segmented = processWebcamSegmentation (
245- webcamBuffer: webcamBuffer,
246- webcamImage: raw,
247- instruction: instruction,
248- colorSpace: colorSpace
249- ) {
250- mirrorHandledBySegmentation = true
251- return segmented
252- }
253- return raw
254- } ( )
237+ let webcamImage = createImage ( from: webcamBuffer, colorSpace: colorSpace)
255238
256239 if let webcamImage {
257240 if isCamFullscreen {
@@ -260,7 +243,7 @@ final class CameraVideoCompositor: NSObject, AVVideoCompositing, @unchecked Send
260243 let fitRect = AVMakeRect ( aspectRatio: webcamAspect, insideRect: fullRect)
261244 context. saveGState ( )
262245 drawBackground ( in: context, rect: fullRect, instruction: instruction, colorSpace: colorSpace)
263- if instruction. cameraMirrored && !mirrorHandledBySegmentation {
246+ if instruction. cameraMirrored {
264247 context. translateBy ( x: fitRect. midX, y: 0 )
265248 context. scaleBy ( x: - 1 , y: 1 )
266249 context. translateBy ( x: - fitRect. midX, y: 0 )
@@ -321,7 +304,7 @@ final class CameraVideoCompositor: NSObject, AVVideoCompositing, @unchecked Send
321304 context. saveGState ( )
322305 context. addPath ( innerPath)
323306 context. clip ( )
324- if instruction. cameraMirrored && !mirrorHandledBySegmentation {
307+ if instruction. cameraMirrored {
325308 context. translateBy ( x: insetRect. midX, y: 0 )
326309 context. scaleBy ( x: - 1 , y: 1 )
327310 context. translateBy ( x: - insetRect. midX, y: 0 )
@@ -339,7 +322,7 @@ final class CameraVideoCompositor: NSObject, AVVideoCompositing, @unchecked Send
339322 context. saveGState ( )
340323 context. addPath ( path)
341324 context. clip ( )
342- if instruction. cameraMirrored && !mirrorHandledBySegmentation {
325+ if instruction. cameraMirrored {
343326 context. translateBy ( x: drawRect. midX, y: 0 )
344327 context. scaleBy ( x: - 1 , y: 1 )
345328 context. translateBy ( x: - drawRect. midX, y: 0 )
@@ -353,64 +336,6 @@ final class CameraVideoCompositor: NSObject, AVVideoCompositing, @unchecked Send
353336 }
354337 }
355338
356- private static func processWebcamSegmentation(
357- webcamBuffer: CVPixelBuffer ,
358- webcamImage: CGImage ,
359- instruction: CompositionInstruction ,
360- colorSpace: CGColorSpace
361- ) -> CGImage ? {
362- let request = VNGeneratePersonSegmentationRequest ( )
363- request. qualityLevel = . accurate
364-
365- let handler = VNImageRequestHandler ( cvPixelBuffer: webcamBuffer, options: [ : ] )
366- try ? handler. perform ( [ request] )
367-
368- guard let maskBuffer = request. results? . first? . pixelBuffer else { return nil }
369-
370- let ciContext = CIContext ( options: [ . useSoftwareRenderer: false ] )
371- let foreground = CIImage ( cvPixelBuffer: webcamBuffer)
372- let maskImage = CIImage ( cvPixelBuffer: maskBuffer) . resized ( to: foreground. extent. size)
373- let size = foreground. extent. size
374-
375- let background : CIImage
376- switch instruction. cameraBackgroundStyle {
377- case . none:
378- return nil
379- case . transparent:
380- background = CIImage ( color: CIColor ( red: 0 , green: 0 , blue: 0 , alpha: 0 ) ) . cropped ( to: foreground. extent)
381- case . solidColor( let color) :
382- background = CIImage ( color: CIColor ( red: color. r, green: color. g, blue: color. b, alpha: color. a) ) . cropped ( to: foreground. extent)
383- case . gradient( let id) :
384- if let grad = CIImage . renderGradientCIImage ( presetId: id, size: size) {
385- background = grad
386- } else {
387- background = CIImage ( color: CIColor . black) . cropped ( to: foreground. extent)
388- }
389- case . image:
390- if let bgCGImage = instruction. cameraBackgroundImage {
391- background = CIImage ( cgImage: bgCGImage) . resizedToFill ( size)
392- } else {
393- background = CIImage ( color: CIColor . black) . cropped ( to: foreground. extent)
394- }
395- }
396-
397- guard let blendFilter = CIFilter ( name: " CIBlendWithMask " ) else { return nil }
398- blendFilter. setValue ( foreground, forKey: kCIInputImageKey)
399- blendFilter. setValue ( background, forKey: kCIInputBackgroundImageKey)
400- blendFilter. setValue ( maskImage, forKey: kCIInputMaskImageKey)
401-
402- guard var output = blendFilter. outputImage else { return nil }
403-
404- if instruction. cameraMirrored {
405- output =
406- output
407- . transformed ( by: CGAffineTransform ( scaleX: - 1 , y: 1 ) )
408- . transformed ( by: CGAffineTransform ( translationX: size. width, y: 0 ) )
409- }
410-
411- return ciContext. createCGImage ( output, from: output. extent)
412- }
413-
414339 private static func aspectFillRect( imageSize: CGSize , in rect: CGRect ) -> CGRect {
415340 let imageAspect = imageSize. width / max( imageSize. height, 1 )
416341 let rectAspect = rect. width / max( rect. height, 1 )
0 commit comments