How do I update a CALayer with a CVPixelBuffer/IOSurface? How do I update a CALayer with a CVPixelBuffer/IOSurface? swift swift

How do I update a CALayer with a CVPixelBuffer/IOSurface?


Maybe I'm wrong, but I think you are you updating your NSView on a background thread. (I suppose that the callback to updateFrame is on a background thread)

If I'm right, when you want to update the NSView, convert your pixelBuffer to whatever you want (NSImage?), and then dispatch it on the main thread.

Pseudocode (I don't work often with CVPixelBuffer so I'm not sure this is the right way to convert to an NSImage)

let ciImage = CIImage(cvImageBuffer: pixelBuffer)let context = CIContext(options: nil)let width = CVPixelBufferGetWidth(pixelBuffer)let height = CVPixelBufferGetHeight(pixelBuffer)let cgImage = context.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: width, height: height))let nsImage = NSImage(cgImage: cgImage, size: CGSize(width: width, height: height))DispatchQueue.main.async {    // assign the NSImage to your NSView here}

Another catch: I did some tests, and it seems that you cannot assign an IOSurface directly to the contents of a CALayer.

I tried with this:

    let textureImageWidth = 1024    let textureImageHeight = 1024    let macPixelFormatString = "ARGB"    var macPixelFormat: UInt32 = 0    for c in macPixelFormatString.utf8.reversed() {       macPixelFormat *= 256       macPixelFormat += UInt32(c)    }    let ioSurface = IOSurfaceCreate([kIOSurfaceWidth: textureImageWidth,                    kIOSurfaceHeight: textureImageHeight,                    kIOSurfaceBytesPerElement: 4,                    kIOSurfaceBytesPerRow: textureImageWidth * 4,                    kIOSurfaceAllocSize: textureImageWidth * textureImageHeight * 4,                    kIOSurfacePixelFormat: macPixelFormat] as CFDictionary)!    IOSurfaceLock(ioSurface, IOSurfaceLockOptions.readOnly, nil)    let test = CIImage(ioSurface: ioSurface)    IOSurfaceUnlock(ioSurface, IOSurfaceLockOptions.readOnly, nil)        v1?.layer?.contents = ioSurface

Where v1 is my view. No effect

Even with a CIImage no effect (just last few lines)

    IOSurfaceLock(ioSurface, IOSurfaceLockOptions.readOnly, nil)    let test = CIImage(ioSurface: ioSurface)    IOSurfaceUnlock(ioSurface, IOSurfaceLockOptions.readOnly, nil)        v1?.layer?.contents = test

If I create a CGImage it works

    IOSurfaceLock(ioSurface, IOSurfaceLockOptions.readOnly, nil)    let test = CIImage(ioSurface: ioSurface)    IOSurfaceUnlock(ioSurface, IOSurfaceLockOptions.readOnly, nil)        let context = CIContext.init()    let img = context.createCGImage(test, from: test.extent)    v1?.layer?.contents = img


If you have some IBActions that update it then create an observed variable with the didSet block and whenever the IBAction is triggered, change its value. Also remember to write the code you want to run when updated in that block.

I'd suggest making the variable an Int, set its default value to 0 and add 1 to it every time it updates.

And you can cast the NSView into an NSImageView for the part where you ask about showing the IMAGE data on an NSView so that does the job.


You need to convert the pixel buffer to CGImage and convert it to a layer so that you can change the layer of the main view.Please try this code

@objcfunc updateFrame(pixelBuffer: CVPixelBuffer) {    guard let surface = CVPixelBufferGetIOSurface(pixelBuffer)?.takeUnretainedValue() else {        print("pixelbuffer isn't IOsurface backed! noooooo!")        return;    }    void *baseAddr = CVPixelBufferGetBaseAddress(pixelBuffer);    size_t width = CVPixelBufferGetWidth(pixelBuffer);    size_t height = CVPixelBufferGetHeight(pixelBuffer);    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();    CGContextRef cgContext = CGBitmapContextCreate(baseAddr, width, height, 8, CVPixelBufferGetBytesPerRow(pixelBuffer), colorSpace, kCGImageAlphaNoneSkipLast);    CGImageRef cgImage = CGBitmapContextCreateImage(cgContext);    CGContextRelease(cgContext);        let outputImage = UIImage(cgImage: outputCGImage, scale: 1, orientation: img.imageOrientation)    let newLayer:CGLayer = CGLayer.init(cgImage: outputImage)    self.layer = newLayer        CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);    CVPixelBufferRelease(pixelBuffer);}