[libcamera-devel] [PATCH v5 04/13] py: cam.py: Use new events support
Tomi Valkeinen
tomi.valkeinen at ideasonboard.com
Mon Jun 5 11:10:40 CEST 2023
On 05/06/2023 08:10, Laurent Pinchart wrote:
> Hi Tomi,
>
> Thank you for the patch.
>
> On Sat, Jun 03, 2023 at 10:56:06AM +0300, Tomi Valkeinen wrote:
>> Convert cam.py to use the new event dispatching. In addition to handling
>> the request-completed event, handle also disconnect, camera-added and
>> camera-removed events (which only do a simple print).
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
>> Reviewed-by: Jacopo Mondi <jacopo at jmondi.org>
>> Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>> ---
>> src/py/cam/cam.py | 27 +++++++++++++++++++++------
>> 1 file changed, 21 insertions(+), 6 deletions(-)
>>
>> diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py
>> index a2a115c1..1e2d1f69 100755
>> --- a/src/py/cam/cam.py
>> +++ b/src/py/cam/cam.py
>> @@ -230,11 +230,19 @@ class CaptureState:
>> # Called from renderer when there is a libcamera event
>> def event_handler(self):
>> try:
>> - reqs = self.cm.get_ready_requests()
>> -
>> - for req in reqs:
>> - ctx = next(ctx for ctx in self.contexts if ctx.idx == req.cookie)
>> - self.__request_handler(ctx, req)
>> + for ev in self.cm.get_events():
>> + type = ev.type
>> +
>> + if type == libcam.Event.Type.CameraAdded:
>> + print(f'Camera {ev.camera} added')
>> + elif type == libcam.Event.Type.CameraRemoved:
>> + print(f'Camera {ev.camera} removed')
>> + elif type == libcam.Event.Type.Disconnect:
>> + print(f'Camera {ev.camera} disconnected')
>> + elif type == libcam.Event.Type.RequestCompleted:
>> + self.__request_handler(ev.camera, ev.request)
>> + else:
>> + raise RuntimeError("Bad event type")
>
> This will cause issues if we later add new event types. Wouldn't it be
> better to ignore unknown event types, or possibly print a (one-time)
> warning message ?
Right, this error should never happen. Maybe this again shows that we
have some unclear behavior in the bindings. If we make it so that all
the events (including CameraManager's events) must be explicitly
enabled, we can only handle the ones we have enabled, and raise an error
for anything else (or assert). But if we do implicitly enable some
events, we have to ignore any unhandled ones.
I'm leaning towards the explicit behavior, as these are somewhat low
level bindings.
>>
>> running = any(ctx.reqs_completed < ctx.opt_capture for ctx in self.contexts)
>> return running
>> @@ -242,7 +250,9 @@ class CaptureState:
>> traceback.print_exc()
>> return False
>>
>> - def __request_handler(self, ctx, req):
>> + def __request_handler(self, cam, req):
>> + ctx = next(ctx for ctx in self.contexts if ctx.camera == cam)
>> +
>> if req.status != libcam.Request.Status.Complete:
>> raise Exception('{}: Request failed: {}'.format(ctx.id, req.status))
>>
>> @@ -447,6 +457,11 @@ def main():
>>
>> state.do_cmd_capture()
>>
>> + # This is not strictly needed, but it helps to do a proper cleanup as we
>> + # drop any unhandled events, and so makes it easier to use memory leak
>> + # detectors.
>> + cm.get_events()
>
> Somehow this feels like a hack, outlining a design issue. Is there any
> way we could do better ? How about clearing events in the camera manager
> destructor ?
If we have events in the queue, the events keep their respective cameras
alive, and the cameras keep the camera manager alive, so there will
never be a destructor call. That said, python gc should detect circular
references, but I'm not sure if it applies here.
But now that I test it, I don't see PyCameraManager destructor being
called at all... So something has gotten broken. Hmm it's probably the
new signal subscription. We keep the requestCompleted subscribed, and we
have passed a shared_ptr<CameraManager> to it...
This is odd, I'm pretty sure I tested this. And the unittests think that
CameraManager does get cleaned up. Sigh...
Tomi
More information about the libcamera-devel
mailing list