[libcamera-devel] [PATCH v5 04/13] py: cam.py: Use new events support

Tomi Valkeinen tomi.valkeinen at ideasonboard.com
Mon Jun 5 11:37:26 CEST 2023


On 05/06/2023 12:10, Tomi Valkeinen wrote:
> 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...

Yes, that's the issue. It can be solved by passing a 
weak_ptr<PyCameraManager> instead. And we also need to disconnect all 
the signals in the PyCameraManager destructor, as otherwise libcamera 
complains about "Removing media device /dev/media0 while still in use".

  Tomi



More information about the libcamera-devel mailing list