swiftmacoscvpixelbuffercmsamplebuffer

Modifying CVPixelBuffer


I'm using the method below to add drawings to a pixel buffer, then append it to an AVAssetWriterInputPixelBufferAdaptor.

It works on my Mac mini (macOS 12 beta 7), but the drawingHandler draws nothing on my MacBook (macOS 11.5.2).

Is there anything wrong with this code?

#if os(macOS)
import AppKit
#else
import UIKit
#endif

import CoreMedia

extension CMSampleBuffer {
    
    func pixelBuffer(drawingHandler: ((CGRect) -> Void)? = nil) -> CVPixelBuffer? {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(self) else {
            return nil
        }
        guard let drawingHandler = drawingHandler else {
            return pixelBuffer
        }
        guard CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) == kCVReturnSuccess else {
            return pixelBuffer
        }
        
        let data = CVPixelBufferGetBaseAddress(pixelBuffer)
        let width = CVPixelBufferGetWidth(pixelBuffer)
        let height = CVPixelBufferGetHeight(pixelBuffer)
        let bitsPerComponent = 8
        let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let imageByteOrderInfo = CGImageByteOrderInfo.order32Little
        let imageAlphaInfo = CGImageAlphaInfo.premultipliedFirst
        
        if let ctx = CGContext(data: data,
                               width: width,
                               height: height,
                               bitsPerComponent: bitsPerComponent,
                               bytesPerRow: bytesPerRow,
                               space: colorSpace,
                               bitmapInfo: imageByteOrderInfo.rawValue | imageAlphaInfo.rawValue)
        {
            // Push
            #if os(macOS)
            let graphCtx = NSGraphicsContext(cgContext: ctx, flipped: false)
            NSGraphicsContext.saveGraphicsState()
            NSGraphicsContext.current = graphCtx
            #else
            UIGraphicsPushContext(ctx)
            #endif
            
            let rect = CGRect(x: 0, y: 0, width: width, height: height)
            drawingHandler(rect)
            
            // Pop
            #if os(macOS)
            NSGraphicsContext.restoreGraphicsState()
            #else
            UIGraphicsPopContext()
            #endif
        }
        
        CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
        
        return pixelBuffer
    }
    
}

Solution

  • Change the lock flags.

    let lockFlags = CVPixelBufferLockFlags(rawValue: 0)
    
    guard CVPixelBufferLockBaseAddress(pixelBuffer, lockFlags) == kCVReturnSuccess else {
        return pixelBuffer
    }
    
    // ...
    
    CVPixelBufferUnlockBaseAddress(pixelBuffer, lockFlags)