2

I'm currently trying to use VideoToolbox to encode video data from an AVCaptureVideoDataOutput, but I'm having an issue referencing self from within the VTCompressionOutputCallback.

My code is as follows:

...

var sessionRef: VTCompressionSession?

let outputCallback: VTCompressionOutputCallback = { _, _, status, _, sampleBuffer in
    guard status == noErr, let sampleBuffer = sampleBuffer else {
        return
    }

    debugPrint("[INFO]: outputCallback: sampleBuffer: \(sampleBuffer)")
}

let sessionErr = VTCompressionSessionCreate(allocator: nil,
                                            width: width,
                                            height: height,
                                            codecType: kCMVideoCodecType_H264,
                                            encoderSpecification: nil,
                                            imageBufferAttributes: nil,
                                            compressedDataAllocator: nil,
                                            outputCallback: outputCallback,
                                            refcon: nil,
                                            compressionSessionOut: UnsafeMutablePointer(&sessionRef))

...

That works fine, and the print outputs as expected, but as soon as I try and add a reference to self within the VTCompressionOutputCallback, it get a compiler error stating

A C function pointer cannot be formed from a closure that captures context

How can I use self from within the callback?

Thanks in advance for the help.

RPK
  • 1,830
  • 14
  • 30

1 Answers1

3

I figured out a solution.

The VTCompressionSessionCreate call has a parameter for outputCallbackRefCon which gets passed along to the VTCompressionOutputCallback.

By wrapping self in an UnsafeMutableRawPointer like so

let unmanagedSelf = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

I was able to pass that value into the VTCompressionSessionCreate under the refcon parameter. Inside of the callback I was then able to do pull that value back out using

let scopedSelf = Unmanaged<ViewController>.fromOpaque(unmanagedSelf).takeUnretainedValue()
RPK
  • 1,830
  • 14
  • 30
  • 1
    Shouldn't you pass `self` in using `passRetained()`, and consume it with `takeRetainedValue()`, so that `VTCompressionSessionCreate` has a strong reference keeping `self` alive? By passing unretained, it is possible that in a case with no strong references remaining to keep `self` alive, it can get deallocated before your callback is called. You would get a dangling pointer. – Alexander Oct 15 '20 at 18:43