0

I have been trying to make a compression callback which sends the compressed data to a different part of the process, but when I put the line to send the data I get back this error

Instance member 'ptManager' cannot be used on type 'SampleHandler'

Here is the code for the callback:

let vtCallback : @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, OSStatus, VTEncodeInfoFlags, CMSampleBuffer?) -> Swift.Void =
    {
        (outputCallbackRefCon, sourceFrameRefCon, status, infoFlags, sampleBuffer) -> Swift.Void in
        
        let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer!)
        
        
        CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
        let height = CVPixelBufferGetHeight(imageBuffer!)
        let src_buff = CVPixelBufferGetBaseAddress(imageBuffer!)
        let data = NSData(bytes: src_buff, length: bytesPerRow * height)
        CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        
        NSLog("Size: " + String((data as Data).count))
        ptManager.sendObject(object: data, type: 102)
    }

I have looked at other solutions to this but have found nothing that works because if I set ptManager to static it causes many more problems than it solves and the same with removing the =.

All help with this would be appreciated!

Edit

Here is some of the earlier code that I tried that might have worked but never got called when I assigned it to the callback:

func compressionOutputCallback(
    outputCallbackRefCon:UnsafeMutableRawPointer?,
    sourceFrameRefCon:UnsafeMutableRawPointer?,
    status:OSStatus,
    infoFlags:VTEncodeInfoFlags,
    sampleBuffer:CMSampleBuffer) {
     let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
     let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
     let height = CVPixelBufferGetHeight(imageBuffer!)
     let src_buff = CVPixelBufferGetBaseAddress(imageBuffer!)
     let data = NSData(bytes: src_buff, length: bytesPerRow * height)
    CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    NSLog("Size: " + String((data as Data).count))
    ptManager.sendObject(object: data, type: 102)
     if status != noErr{
      NSLog("SBC: Error encoding video", status)
      print("SBC: Error encoding video", status)
       return
    }
    print("SBC: compressionOutputCallback dataBuffer", status)
  }
//Does not get called at all

Edit 2:

Here is where the callback is being used:

 VTCompressionSessionCreate(allocator: nil, width: 1080, height: 1920, codecType: kCMVideoCodecType_H264,encoderSpecification: nil, imageBufferAttributes: nil, compressedDataAllocator: nil, outputCallback: vtCallback as? VTCompressionOutputCallback, refcon: nil, compressionSessionOut: sessionOut)
Asra
  • 151
  • 1
  • 11
  • 1
    This is kind of a rough spot. You're interfacing with a C API, which accepts a C function pointer (hence why your function or closure needs to use `@convention(c)`. C functions are global in a sense. They're not like closures, which are allocated in their own memory and can capture variables. Thus, C functions can't access non-global (or non-static) values like `ptManager`, because there's nowhere for that enclosed reference to be stored. – Alexander Oct 15 '20 at 02:19
  • The exact solution for this varies depending on the specific context, but usually, APIs that accept function pointers also take something like a `void *userInfo`. That pointer is something you can pass into the function call, and the API will pass it into your callback function pointer as one of the args. You can then down cast that `void *userInfo` into the specific type you set it to. This `userInfo` lets you capture and pass through a context, similar to how closures automatically enclose over variables and give you access to them. It doesn't appear that this callback has such a param, tho. – Alexander Oct 15 '20 at 02:21
  • I think the problem with making the variable static, which I may be able to work around is that I have to declare that `ptManager = self` later, which causes problems. I have also put up some of the code that I tried earlier but that didn't get called at all in case that helps. – Asra Oct 15 '20 at 02:34
  • "Static" is just one way to get a global variable. It's namespaced, but it's still one per program, globally accessible. I wouldn't recommend that. (For one, what happens when you want multiple instances, each with their own `ptManager`? You should show the context of where `vtCallback` is actually being used – Alexander Oct 15 '20 at 12:38
  • In another edit, I added the code where `vtCallback` is used. – Asra Oct 15 '20 at 18:37
  • @Z3RO The `void *outputCallbackRefCon` parameter of `VTCompressionSessionCreate` is precisely the `userInfo` mechanism I was talking about. `VTCompressionSessionCreate` will take that value, and pass it into your callback as `void *sourceFrameRefCon`. You're free to use that to smuggle in what ever context you want, such as `self`, or `self.ptManager`. Let me know if the duplicated question doesn't fully answer your questions. – Alexander Oct 15 '20 at 18:45
  • I tried that but `Unmanaged.passUnretained(self).toOpaque()` is giving me an error: `Cannot invoke 'passUnretained' with an agrument list of type '((SampleHandler) -> () -> (SampleHandler))'`. – Asra Oct 15 '20 at 19:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/223114/discussion-between-alexander-reinstate-monica-and-z3r0). – Alexander Oct 15 '20 at 19:50

0 Answers0