pyobjc icon indicating copy to clipboard operation
pyobjc copied to clipboard

metadata issue with AVCaptureStillImageOutput.captureStillImageAsynchronouslyFromConnection:completionHandler:

Open g0t4 opened this issue 3 years ago • 7 comments

Describe the bug

When I use captureStillImageAsynchronouslyFromConnection:completionHandler on an instance of AVCaptureStillImageOutput I get the following error:

TypeError: Argument 3 is a block, but no signature available

Platform information

  • Python version 3.9.13
  • How was python installed (python.org, anaconda, homebrew, ...) homebrew
  • macOS version 13.0 Beta (22A5342f)

To Reproduce

still_output = AVFoundation.AVCaptureStillImageOutput.new()

# ...

def on_complete(sample_buffer, err):
  print("test")

still_output.captureStillImageAsynchronouslyFromConnection_completionHandler_(first_conn, on_complete)

Expected behavior callback to be executed with sample buffer

Additional context I looked into metadata a bit and did find something that might be useful. The following don't match whereas I have found that they match for other selectors:

# get metadata from class selector ("unbound")
print(AVFoundation.AVCaptureStillImageOutput.captureStillImageAsynchronouslyFromConnection_completionHandler_.__metadata__())
# get metadata from instance's selector
print(still_output.captureStillImageAsynchronouslyFromConnection_completionHandler_.__metadata__())

Here's the output for the above two prints:


# class selector:
{'retval': {'_template': True, 'type': b'v'}, 'arguments': ({'_template': True, 'type': b'@'}, {'_template': True, 'type': b':'}, {'_template': True, 'type': b'@'}, {'_template': True, 'type': b'@?', 'callable': {'retval': {'type': b'v'}, 'arguments': ({'type': b'^v', 'null_accepted': True}, {'type': b'^{opaqueCMSampleBuffer=}', 'null_accepted': True}, {'type': b'@'})}, 'callable_retained': True}), 'classmethod': False, 'hidden': False}

# instance selector:
{'retval': {'_template': True, 'type': b'v'}, 'arguments': ({'_template': True, 'type': b'@'}, {'_template': True, 'type': b':'}, {'_template': True, 'type': b'@'}, {'_template': True, 'type': b'@?'}), 'classmethod': False, 'hidden': False}

I also found overrides for this selector's metadata in the pyobjc AVFoundation module: https://github.com/ronaldoussoren/pyobjc/blob/5bc7a29ce8e31fd858c1f1b2796f051a0470d24c/pyobjc-framework-AVFoundation/Lib/AVFoundation/_metadata.py#L3356-L3375

I wonder if objc.registerMetaDataForSelector isn't updating the metadata everywhere that's needed? It seems to alter the class selector's metadata but not the instance selector's which I assume is what's used. IIUC register performs some sort of merge logic.. is there a way I can formulate a call to change the metadata that's used to see if that fixes the error I get?

I am not versed in how to go about patching the metadata for this to attempt to fix it... it just seems like somehow the callback's type definition is missing and needs added. But, it only appears missing on the instance selector (the class selector appears correct though I don't know if that's even used).

Thanks in advance!

g0t4 avatar Sep 12 '22 00:09 g0t4

Ok so I did some digging and it seems that instances of AVCaptureStillImageOutput were instances of AVCaptureStillImageOutput_Tundra at runtime so I used the metadata "fix" to that _Tundra class too. Hence the divergence in metadata between class selector and instance selector...

https://github.com/ronaldoussoren/pyobjc/blob/5bc7a29ce8e31fd858c1f1b2796f051a0470d24c/pyobjc-framework-AVFoundation/Lib/AVFoundation/_metadata.py#L3356-L3374

change b"AVCaptureStillImageOutput", to b"AVCaptureStillImageOutput_Tundra", I added this metadata fix to my own codebase (not pyobjc repo) NOTE: r =objc.registerMetaDataForSelector

And, now things work! My instance's selector is fixed. And, my callback is invoked with a CMSampleBuffer.

Not sure what the _Tundra classes are in AVFoundation nor what to do to fix this officially, nor if this is a real fix... just wanted to leave a note that I accomplished what I wanted to do (fix metadata).

g0t4 avatar Sep 12 '22 09:09 g0t4

TLDR, fix AVCaptureStillImageOutput_Tundra metadata:

objc.registerMetaDataForSelector(
    b"AVCaptureStillImageOutput_Tundra",
    b"captureStillImageAsynchronouslyFromConnection:completionHandler:",
    {
        "arguments": {
            3: {
                "callable": {
                    "retval": {"type": b"v"},
                    "arguments": {
                        0: {"type": b"^v"},
                        1: {"type": b"^{opaqueCMSampleBuffer=}"},
                        2: {"type": b"@"},
                    },
                },
                "type": "@?",
            }
        }
    },
)

Verify the change applies successfully by inspecting metadata before and after calling registerMetaDataForSelector:

print(AVCaptureStillImageOutput_Tundra.captureStillImageAsynchronouslyFromConnection_completionHandler_.metadata())

I assume metadata fixes would need applied for all AVFoundation types that have _Tundra types at runtime... I can help make that change. Before that change it would be prudent to know why these _Tundra types exist. And it probably would make sense to conditionally apply metadata fixes (only when the types exist).

g0t4 avatar Sep 12 '22 22:09 g0t4

Interesting...

The ObjC metadata system assumes that the documented classes are the base classes that introduce particular methods, and that's not true here. Worse yet, the various "Tundra" classes aren't even subclasses of the documented classes. That breaks the API contract of those documented classes, but I'm afraid filing an issue about his in Feedback Assistant would be a wast of time for this.

I'll update the metadata for this by tweaking some of the scripts I use for generating the _metadata modules.

ronaldoussoren avatar Sep 13 '22 09:09 ronaldoussoren

Thanks for the insight and help.

g0t4 avatar Sep 13 '22 23:09 g0t4

The changeset I just pushed will fix the issue in the 9.0 release (which will be released just before macOS 13 is released).

There will be a separate changeset for 8.x later today (if all goes to plan).

ronaldoussoren avatar Sep 14 '22 09:09 ronaldoussoren

Just tested the repo changes, works great! Thanks

Just curious, I see classclones used.. where is the metadata compiler code for that? Is that private?

g0t4 avatar Sep 15 '22 16:09 g0t4

Just tested the repo changes, works great! Thanks

Just curious, I see classclones used.. where is the metadata compiler code for that? Is that private?

That's private at this point. I started a major cleanup in the public repository, but that's making less progress than I'd like. I'm therefore still using a private copy of that code base.

BTW. Current plan is to push out a 8.5.x release over the weekend with a fix for this (and some other changes). The fix in that branch is slightly different and updates the generated metadata file (which should be fine because the 8.5 branch won't get updated metadata anyway).

ronaldoussoren avatar Sep 15 '22 21:09 ronaldoussoren