Stuck on CGDataProviderCreateWithData

What I'd like to do is provide a CVPixelBuffer as the dataInfo argument to CGDataProviderCreateWithData that has an initializer:

init?(dataInfo info: UnsafeMutableRawPointer?,
data: UnsafeRawPointer,
size: Int,
releaseData: @escaping CGDataProviderReleaseDataCallback)


My best (and probably wrong) approach to convert to a UnsafeRawPointer is:

let pixelBuffer: CVPixelBuffer
...
let ptr: UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating: &pixelBuffer)


However, the releaseData callback function is defined as:

typealias CGDataProviderReleaseDataCallback = (UnsafeMutableRawPointer?, UnsafeRawPointer, Int) -> Void


I cannot think of any way to get the CVPixelBuffer back from the UnsafeMutableRawPointer.


Clearly I need help!

Accepted Reply

Either I’ve completely misread this thread or you folks are working way too hard (-:

My reading of this thread is that you’re trying to associate a Swift object with a C object via that C object’s ‘info’ pointer (info, cookie, refCon, whatever). If so, the correct approach is to use Unmanaged and OpaquePointer:

  • You use Unmanaged.passRetained(_:) to get a retained Unmanaged reference to your Swift Object

  • You convert that to OpaquePointer using toOpaque(), which you can then pass to the ‘info’ pointer of the C API

  • You use Unmanaged.fromOpaque(_:) to get back to your Unmanaged reference from that ‘info’ pointer

  • You use either takeRetainedValue() or takeUnretainedValue() to get back to the original Swift object, depending on whether you want to release your unmanaged reference or not

  • If you use takeUnretainedValue(), you can later call release() to clean up

You can see an end-to-end example of this technique in this thread.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

If "rawPointer" is the UnsafeMutableRawPointer? passed as the first callback parameter, you should be able to recover the CVPixelBuffer reference like this:


     if let pixelBuffer = rawPointer?.assumingMemoryBound (to: CVPixelBuffer.self) { …


When you're passing the pixel buffer into the initalizer, try just saying "&pixelBuffer". The Swift compiler is generally able to create the UnsafeMutableRawPointer for you, so it looks cleaner at the call site.

One question - re object ownership. Using "&pixelBuffer" I assume does not transfer ownership, so I need a strong reference to "pixelBuffer". When I later "reconstruct" the pixels buffer (using assumingMemoryBound), then I aso assume when that pixelbuffer goes out of scope, it has no affect on ownership.


If the above is true, I could (possibly) put the pixelBuffer into a set, then using the reconstructed object remove it from the set? Or...

In Swift, the life of CF objects is managed automatically, so you have to consider whether you own a reference to the pixel buffer that lasts long enough to make the passed pointer valid during the callback. (In Obj-C, it was easier, because you could just CFRetain the pixel buffer when you pass it in, and CFRelease it when you're done with it.)


I just ran into a similar issue with a context (void*) pointer passed into a didEnd…-style method, where I used this CFRetain/CFRelease approach in Obj-C and needed a new answer in Swift. What I came up with is this:


private struct PresentErrorContext {
     let completionHandler: ((Bool) -> Void)?
}

@objc private func didPresentError (didRecover: Bool, contextInfo rawContextInfo: UnsafeMutableRawPointer?) {
// Invoke the completion handler
     if let contextInfo = rawContextInfo?.assumingMemoryBound (to: PresentErrorContext.self) {
     if let completionHandler = contextInfo.pointee.completionHandler {
          completionHandler (didRecover) }
          contextInfo.deinitialize ()
          contextInfo.deallocate (capacity: 1)
     }
}

func presentErrorAsSheet (_ error: Error, completionHandler: (Bool) -> Void) {
     // Allocate some memory for the contextAllocate some memory for the context
     let contextInfo = UnsafeMutablePointer<PresentErrorContext>.allocate (capacity: 1)
     contextInfo!.initialize (to: PresentErrorContext (completionHandler: completionHandler))
     // Display the error sheet
     presentError (error, modalFor: window!,  delegate: self, 
          didPresent: #selector (didPresentError(didRecover:contextInfo:)), 
          contextInfo: contextInfo)
}


That is, I allocated a little bit of memory from the heap to hold the PresentErrorContext structure, and wrapped the actual reference inside that structure. The point is that this memory is not reference counted, hence won't disappear until explicitly deallocated — Swift's own special malloc.


In my case, it was a completion handler reference being passed. In your case, you should be able to do something similar with the pixel buffer reference.


Note: I tried this to make sure it works, but I didn't verify that it's not leaking memory, so use at your own risk. Also, there might be cleverer ways to do this.

Either I’ve completely misread this thread or you folks are working way too hard (-:

My reading of this thread is that you’re trying to associate a Swift object with a C object via that C object’s ‘info’ pointer (info, cookie, refCon, whatever). If so, the correct approach is to use Unmanaged and OpaquePointer:

  • You use Unmanaged.passRetained(_:) to get a retained Unmanaged reference to your Swift Object

  • You convert that to OpaquePointer using toOpaque(), which you can then pass to the ‘info’ pointer of the C API

  • You use Unmanaged.fromOpaque(_:) to get back to your Unmanaged reference from that ‘info’ pointer

  • You use either takeRetainedValue() or takeUnretainedValue() to get back to the original Swift object, depending on whether you want to release your unmanaged reference or not

  • If you use takeUnretainedValue(), you can later call release() to clean up

You can see an end-to-end example of this technique in this thread.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Quinn - thanks again for the help!


Using your post I re-wrote the function and verified that its all working (if I change the closure to use 'takeUnretained' everything breaks since the buffer is never released). Note that in this example the use of the 'info' point and call back function is overkill - YMMV.


// let pixelBuffer: CVPixelBuffer
let fooU: Unmanaged = Unmanaged.passRetained(pixelBuffer)
let foo: UnsafeMutableRawPointer = fooU.toOpaque()
/* Either "bar" works */
/* let bar: @convention(c) (UnsafeMutableRawPointer?, UnsafeRawPointer, Int) -> Swift.Void = { */
let bar: CGDataProviderReleaseDataCallback = {
    (_ pixelPtr: UnsafeMutableRawPointer?, _ data: UnsafeRawPointer, _ size: Int) in
    if let pixelPtr = pixelPtr {
        let ptr: Unmanaged<CVPixelBuffer> = Unmanaged.fromOpaque(pixelPtr)
        let pixelBuffer: CVPixelBuffer = ptr.takeRetainedValue()
        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        DispatchQueue.main.async {
            print("UNLOCKED IT!")
        }
    }
}
let val: CVReturn = CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
if  val == kCVReturnSuccess,
    let sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer),
    let provider = CGDataProvider(dataInfo: foo, data: sourceBaseAddr, size: sourceRowBytes * height, releaseData: bar)
{
    let colorspace = CGColorSpaceCreateDeviceRGB()
    let image = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: sourceRowBytes,
                    space: colorspace, bitmapInfo: bitmapInfo, provider: provider, decode: nil,
                    shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
    /* done in callback CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) */
    return image
} else {
    return nil
}

>> Either I’ve completely misread this thread …


No, but what drives me crazy is that Unmanaged (and to some extent OpaquePointer) is basically undiscoverable. Since the last time you weighed in on something to do with C pointer interoperability, I've kept this on my Safari "Favorites" bar:


https://swift.org/migration-guide/se-0107-migrate.html


(which has its own discoverability problems, since I can find no way of following links on swift.org that lead to this document), but it has nothing to help with reference counting — I did check before responding to this thread. Nor is there anything about manual reference counting here where it should be:


https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html


The Managed type is documented in the Swift standard library document, if you already know what to look for, but discovering anything in that document is another exercise in frustration.


>> …or you folks are working way too hard


Now you're just being cruel. 😉 In my example code, I added 4 lines of code to handle some unmanaged memory. (I was going to actually use malloc, but I discovered that "allocate" method via an autocomplete accident.) How many steps does your solution have to manage memory? Um, 4, isn't it?

No, but what drives me crazy is that Unmanaged (and to some extent OpaquePointer) is basically undiscoverable.

Fair enough. I actually went looking for this technique in the Using Swift with Cocoa and Objective-C book before posting and couldn’t find it, which was a bit disappointing. I could’ve sworn I filed a bug requesting that it be added but I checked my records and can’t find it. Oh well, more quality time in Radar for me (r. 32918747).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"