From patchwork Thu Mar 9 14:25:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18362 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 4A073BDE17 for ; Thu, 9 Mar 2023 14:26:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 76A25626DC; Thu, 9 Mar 2023 15:26:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371978; bh=LEDkDy2IYdbTCOYWF7yJFbM6VLO8CFdiLyqTUN0n5cE=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=MzOqeKTKnIX+Q4B+Ye9rTo/EFMnQnYC5UVgvS8DD+WH8mbvpoqxjV3DJHt+5fRkBW 3jC7TAUdI4xr6ZCYn32uaopMX2PWYIqN4jGSHcj4OTwfIYpm0So6joe5FB2J6mqkDm wyWwtx/39qdS7C1JHJH24D0axmpDWdHctmBXDz7PlR+8t9O2+h3xu8b/sSynOMj0O2 8AdHEmXkEFqSmAQ6fIb9jIgZo8RiDCcOqGzD5AIeuuYQnGrYHYqsUULcpzRsu77WUA QQcdhmoGpg4640mdYp3eZ3N2ptOwJhfzakxYCExX5odwETeqTjXIAja4VC3LfVhdqi wREeNKo0IwCIw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B6DA561ED7 for ; Thu, 9 Mar 2023 15:26:16 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="C1c7dEiR"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2BB9F886; Thu, 9 Mar 2023 15:26:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371976; bh=LEDkDy2IYdbTCOYWF7yJFbM6VLO8CFdiLyqTUN0n5cE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=C1c7dEiRy3KUZdpv4YUxNTglD047bWDD+erLwqwUmcGdxV0CzEXull2QzL4QqCFuG ml95FxKFUKlLpt1GP6P8IQJJsnzTTtH/1TgDjF9+Cggzn19cexDa2y1wfveMkBI0MD GkuV8ZhN0aE1wTejbt2b9N3f8zKJXZuzkKf7WA0E= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:47 +0200 Message-Id: <20230309142601.70556-2-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 01/15] py: Use exceptions instead of returning error codes X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" We have multiple methods which return an error code, mimicking the C++ API. Using exceptions is more natural in the Python API, so change all those methods to raise an Exception instead. Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/py/cam/cam.py | 16 +---- src/py/examples/simple-cam.py | 15 +--- src/py/examples/simple-capture.py | 21 ++---- src/py/examples/simple-continuous-capture.py | 21 ++---- src/py/libcamera/py_main.cpp | 60 ++++++++++++---- test/py/unittests.py | 76 +++++++------------- 6 files changed, 91 insertions(+), 118 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 967a72f5..a2a115c1 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -158,9 +158,7 @@ class CameraContext: print('Camera configuration adjusted') - r = self.camera.configure(camconfig) - if r != 0: - raise Exception('Configure failed') + self.camera.configure(camconfig) self.stream_names = {} self.streams = [] @@ -175,12 +173,7 @@ class CameraContext: allocator = libcam.FrameBufferAllocator(self.camera) for stream in self.streams: - ret = allocator.allocate(stream) - if ret < 0: - print('Cannot allocate buffers') - exit(-1) - - allocated = len(allocator.buffers(stream)) + allocated = allocator.allocate(stream) print('{}-{}: Allocated {} buffers'.format(self.id, self.stream_names[stream], allocated)) @@ -205,10 +198,7 @@ class CameraContext: buffers = self.allocator.buffers(stream) buffer = buffers[buf_num] - ret = request.add_buffer(stream, buffer) - if ret < 0: - print('Can not set buffer for request') - exit(-1) + request.add_buffer(stream, buffer) requests.append(request) diff --git a/src/py/examples/simple-cam.py b/src/py/examples/simple-cam.py index b3e97ca7..1cd1019d 100755 --- a/src/py/examples/simple-cam.py +++ b/src/py/examples/simple-cam.py @@ -259,12 +259,7 @@ def main(): allocator = libcam.FrameBufferAllocator(camera) for cfg in config: - ret = allocator.allocate(cfg.stream) - if ret < 0: - print('Can\'t allocate buffers') - return -1 - - allocated = len(allocator.buffers(cfg.stream)) + allocated = allocator.allocate(cfg.stream) print(f'Allocated {allocated} buffers for stream') # -------------------------------------------------------------------- @@ -289,15 +284,9 @@ def main(): requests = [] for i in range(len(buffers)): request = camera.create_request() - if not request: - print('Can\'t create request') - return -1 buffer = buffers[i] - ret = request.add_buffer(stream, buffer) - if ret < 0: - print('Can\'t set buffer for request') - return -1 + request.add_buffer(stream, buffer) # Controls can be added to a request on a per frame basis. request.set_control(libcam.controls.Brightness, 0.5) diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py index 5f93574f..4b85408f 100755 --- a/src/py/examples/simple-capture.py +++ b/src/py/examples/simple-capture.py @@ -43,8 +43,7 @@ def main(): # Acquire the camera for our use - ret = cam.acquire() - assert ret == 0 + cam.acquire() # Configure the camera @@ -60,8 +59,7 @@ def main(): w, h = [int(v) for v in args.size.split('x')] stream_config.size = libcam.Size(w, h) - ret = cam.configure(cam_config) - assert ret == 0 + cam.configure(cam_config) print(f'Capturing {TOTAL_FRAMES} frames with {stream_config}') @@ -83,15 +81,13 @@ def main(): req = cam.create_request(i) buffer = allocator.buffers(stream)[i] - ret = req.add_buffer(stream, buffer) - assert ret == 0 + req.add_buffer(stream, buffer) reqs.append(req) # Start the camera - ret = cam.start() - assert ret == 0 + cam.start() # frames_queued and frames_done track the number of frames queued and done @@ -101,8 +97,7 @@ def main(): # Queue the requests to the camera for req in reqs: - ret = cam.queue_request(req) - assert ret == 0 + cam.queue_request(req) frames_queued += 1 # The main loop. Wait for the queued Requests to complete, process them, @@ -155,13 +150,11 @@ def main(): # Stop the camera - ret = cam.stop() - assert ret == 0 + cam.stop() # Release the camera - ret = cam.release() - assert ret == 0 + cam.release() return 0 diff --git a/src/py/examples/simple-continuous-capture.py b/src/py/examples/simple-continuous-capture.py index 26a8060b..e1cb931e 100755 --- a/src/py/examples/simple-continuous-capture.py +++ b/src/py/examples/simple-continuous-capture.py @@ -28,8 +28,7 @@ class CameraCaptureContext: # Acquire the camera for our use - ret = cam.acquire() - assert ret == 0 + cam.acquire() # Configure the camera @@ -37,8 +36,7 @@ class CameraCaptureContext: stream_config = cam_config.at(0) - ret = cam.configure(cam_config) - assert ret == 0 + cam.configure(cam_config) stream = stream_config.stream @@ -62,8 +60,7 @@ class CameraCaptureContext: req = cam.create_request(idx) buffer = allocator.buffers(stream)[i] - ret = req.add_buffer(stream, buffer) - assert ret == 0 + req.add_buffer(stream, buffer) self.reqs.append(req) @@ -73,13 +70,11 @@ class CameraCaptureContext: def uninit_camera(self): # Stop the camera - ret = self.cam.stop() - assert ret == 0 + self.cam.stop() # Release the camera - ret = self.cam.release() - assert ret == 0 + self.cam.release() # A container class for our state @@ -145,8 +140,7 @@ class CaptureContext: for cam_ctx in self.camera_contexts: for req in cam_ctx.reqs: - ret = cam_ctx.cam.queue_request(req) - assert ret == 0 + cam_ctx.cam.queue_request(req) # Use Selector to wait for events from the camera and from the keyboard @@ -177,8 +171,7 @@ def main(): # Start the cameras for cam_ctx in ctx.camera_contexts: - ret = cam_ctx.cam.start() - assert ret == 0 + cam_ctx.cam.start() ctx.capture() diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index d14e18e2..1d4c23b3 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -114,8 +114,18 @@ PYBIND11_MODULE(_libcamera, m) pyCamera .def_property_readonly("id", &Camera::id) - .def("acquire", &Camera::acquire) - .def("release", &Camera::release) + .def("acquire", [](Camera &self) { + int ret = self.acquire(); + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to acquire camera"); + }) + .def("release", [](Camera &self) { + int ret = self.release(); + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to release camera"); + }) .def("start", [](Camera &self, const std::unordered_map &controls) { /* \todo What happens if someone calls start() multiple times? */ @@ -135,20 +145,19 @@ PYBIND11_MODULE(_libcamera, m) int ret = self.start(&controlList); if (ret) { self.requestCompleted.disconnect(); - return ret; + throw std::system_error(-ret, std::generic_category(), + "Failed to start camera"); } - - return 0; }, py::arg("controls") = std::unordered_map()) .def("stop", [](Camera &self) { int ret = self.stop(); - if (ret) - return ret; self.requestCompleted.disconnect(); - return 0; + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to stop camera"); }) .def("__str__", [](Camera &self) { @@ -157,9 +166,20 @@ PYBIND11_MODULE(_libcamera, m) /* Keep the camera alive, as StreamConfiguration contains a Stream* */ .def("generate_configuration", &Camera::generateConfiguration, py::keep_alive<0, 1>()) - .def("configure", &Camera::configure) + .def("configure", [](Camera &self, CameraConfiguration *config) { + int ret = self.configure(config); + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to configure camera"); + }) - .def("create_request", &Camera::createRequest, py::arg("cookie") = 0) + .def("create_request", [](Camera &self, uint64_t cookie) { + std::unique_ptr req = self.createRequest(cookie); + if (!req) + throw std::system_error(ENOMEM, std::generic_category(), + "Failed to create request"); + return req; + }, py::arg("cookie") = 0) .def("queue_request", [](Camera &self, Request *req) { py::object py_req = py::cast(req); @@ -172,10 +192,11 @@ PYBIND11_MODULE(_libcamera, m) py_req.inc_ref(); int ret = self.queueRequest(req); - if (ret) + if (ret) { py_req.dec_ref(); - - return ret; + throw std::system_error(-ret, std::generic_category(), + "Failed to queue request"); + } }) .def_property_readonly("streams", [](Camera &self) { @@ -253,7 +274,13 @@ PYBIND11_MODULE(_libcamera, m) pyFrameBufferAllocator .def(py::init>(), py::keep_alive<1, 2>()) - .def("allocate", &FrameBufferAllocator::allocate) + .def("allocate", [](FrameBufferAllocator &self, Stream *stream) { + int ret = self.allocate(stream); + if (ret < 0) + throw std::system_error(-ret, std::generic_category(), + "Failed to allocate buffers"); + return ret; + }) .def_property_readonly("allocated", &FrameBufferAllocator::allocated) /* Create a list of FrameBuffers, where each FrameBuffer has a keep-alive to FrameBufferAllocator */ .def("buffers", [](FrameBufferAllocator &self, Stream *stream) { @@ -330,7 +357,10 @@ PYBIND11_MODULE(_libcamera, m) pyRequest /* \todo Fence is not supported, so we cannot expose addBuffer() directly */ .def("add_buffer", [](Request &self, const Stream *stream, FrameBuffer *buffer) { - return self.addBuffer(stream, buffer); + int ret = self.addBuffer(stream, buffer); + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to add buffer"); }, py::keep_alive<1, 3>()) /* Request keeps Framebuffer alive */ .def_property_readonly("status", &Request::status) .def_property_readonly("buffers", &Request::buffers) diff --git a/test/py/unittests.py b/test/py/unittests.py index 6dea80fc..794e46be 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -42,31 +42,26 @@ class SimpleTestMethods(BaseTestCase): cam = cm.get('platform/vimc.0 Sensor B') self.assertIsNotNone(cam) - ret = cam.acquire() - self.assertZero(ret) + cam.acquire() - ret = cam.release() - self.assertZero(ret) + cam.release() def test_double_acquire(self): cm = libcam.CameraManager.singleton() cam = cm.get('platform/vimc.0 Sensor B') self.assertIsNotNone(cam) - ret = cam.acquire() - self.assertZero(ret) + cam.acquire() libcam.log_set_level('Camera', 'FATAL') - ret = cam.acquire() - self.assertEqual(ret, -errno.EBUSY) + with self.assertRaises(RuntimeError): + cam.acquire() libcam.log_set_level('Camera', 'ERROR') - ret = cam.release() - self.assertZero(ret) + cam.release() - ret = cam.release() - # I expected EBUSY, but looks like double release works fine - self.assertZero(ret) + # I expected exception here, but looks like double release works fine + cam.release() class CameraTesterBase(BaseTestCase): @@ -80,11 +75,7 @@ class CameraTesterBase(BaseTestCase): self.cm = None self.skipTest('No vimc found') - ret = self.cam.acquire() - if ret != 0: - self.cam = None - self.cm = None - raise Exception('Failed to acquire camera') + self.cam.acquire() self.wr_cam = weakref.ref(self.cam) self.wr_cm = weakref.ref(self.cm) @@ -93,9 +84,7 @@ class CameraTesterBase(BaseTestCase): # If a test fails, the camera may be in running state. So always stop. self.cam.stop() - ret = self.cam.release() - if ret != 0: - raise Exception('Failed to release camera') + self.cam.release() self.cam = None self.cm = None @@ -115,8 +104,7 @@ class AllocatorTestMethods(CameraTesterBase): streamconfig = camconfig.at(0) wr_streamconfig = weakref.ref(streamconfig) - ret = cam.configure(camconfig) - self.assertZero(ret) + cam.configure(camconfig) stream = streamconfig.stream wr_stream = weakref.ref(stream) @@ -129,8 +117,8 @@ class AllocatorTestMethods(CameraTesterBase): self.assertIsNotNone(wr_streamconfig()) allocator = libcam.FrameBufferAllocator(cam) - ret = allocator.allocate(stream) - self.assertTrue(ret > 0) + num_bufs = allocator.allocate(stream) + self.assertTrue(num_bufs > 0) wr_allocator = weakref.ref(allocator) buffers = allocator.buffers(stream) @@ -173,14 +161,13 @@ class SimpleCaptureMethods(CameraTesterBase): self.assertIsNotNone(fmts) fmts = None - ret = cam.configure(camconfig) - self.assertZero(ret) + cam.configure(camconfig) stream = streamconfig.stream allocator = libcam.FrameBufferAllocator(cam) - ret = allocator.allocate(stream) - self.assertTrue(ret > 0) + num_bufs = allocator.allocate(stream) + self.assertTrue(num_bufs > 0) num_bufs = len(allocator.buffers(stream)) @@ -190,19 +177,16 @@ class SimpleCaptureMethods(CameraTesterBase): self.assertIsNotNone(req) buffer = allocator.buffers(stream)[i] - ret = req.add_buffer(stream, buffer) - self.assertZero(ret) + req.add_buffer(stream, buffer) reqs.append(req) buffer = None - ret = cam.start() - self.assertZero(ret) + cam.start() for req in reqs: - ret = cam.queue_request(req) - self.assertZero(ret) + cam.queue_request(req) reqs = None gc.collect() @@ -230,8 +214,7 @@ class SimpleCaptureMethods(CameraTesterBase): reqs = None gc.collect() - ret = cam.stop() - self.assertZero(ret) + cam.stop() def test_select(self): cm = self.cm @@ -245,14 +228,13 @@ class SimpleCaptureMethods(CameraTesterBase): self.assertIsNotNone(fmts) fmts = None - ret = cam.configure(camconfig) - self.assertZero(ret) + cam.configure(camconfig) stream = streamconfig.stream allocator = libcam.FrameBufferAllocator(cam) - ret = allocator.allocate(stream) - self.assertTrue(ret > 0) + num_bufs = allocator.allocate(stream) + self.assertTrue(num_bufs > 0) num_bufs = len(allocator.buffers(stream)) @@ -262,19 +244,16 @@ class SimpleCaptureMethods(CameraTesterBase): self.assertIsNotNone(req) buffer = allocator.buffers(stream)[i] - ret = req.add_buffer(stream, buffer) - self.assertZero(ret) + req.add_buffer(stream, buffer) reqs.append(req) buffer = None - ret = cam.start() - self.assertZero(ret) + cam.start() for req in reqs: - ret = cam.queue_request(req) - self.assertZero(ret) + cam.queue_request(req) reqs = None gc.collect() @@ -303,8 +282,7 @@ class SimpleCaptureMethods(CameraTesterBase): reqs = None gc.collect() - ret = cam.stop() - self.assertZero(ret) + cam.stop() # Recursively expand slist's objects into olist, using seen to track already From patchwork Thu Mar 9 14:25:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18363 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2EF1AC3262 for ; Thu, 9 Mar 2023 14:26:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E0851626E3; Thu, 9 Mar 2023 15:26:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371978; bh=YKfZjDmtlRzCbiYHT8kiSClzauOYlnIxVzkNSZoTeJo=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=iGdzelFH9dVeQouBmmZlgYmDzkCRj9me1WRm/qrqMccPrjX1GLpE1KM5iAKAH/bSu 85bbusTcLUGmzlZsnuVJ0RfhwcsPUe8GAdNsoHok+/NMNRbWHb3hvi9YomtBc5f71U vCsXr+VI4ykmiK279kAjYKtVG6PLjWQGHuZWsh6ftc04dO7gczodKfE7pfHR3KMb2v d5ba3bDox2HUnRTGcPm6rIpcKIb/lMrzU91/BeQcLoK8I+tn6G5Qv36w5pHkUnyJGw p4iynd8rETLn77XPmYMQflVyJ8TMFlOhAhHOBA0wQutt9SyWRbyoBxmcyaDG2LReYC q1NKvWbt1lcgQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1305A626D5 for ; Thu, 9 Mar 2023 15:26:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="o3rB1vhu"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 97ECF589; Thu, 9 Mar 2023 15:26:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371976; bh=YKfZjDmtlRzCbiYHT8kiSClzauOYlnIxVzkNSZoTeJo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o3rB1vhuk1UCD3j1NGJcLcZSXKouYFZco8FdBZd4xi5K0Olw2tXCvCbUnM+z2z00b 6FxtRzyjYSYZVIoAaWS4Ci/Ujkr8/USqlMtIvk9VIcP9GPsPjiAWP0Lsc+f+bha1qe zyvm16dzMl/UTBorWNLopI1l4tI3cr5+O/sRqCF0= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:48 +0200 Message-Id: <20230309142601.70556-3-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 02/15] py: New event handling X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" At the moment the Python bindings only handle the requestCompleted events. But we have others, bufferCompleted and disconnect from the Camera, and cameraAdded and cameraRemoved from the CameraManager. This patch makes all those events available to the users. The get_ready_requests() method is now deprecated, but available to keep backward compatibility. The new event handling works like get_ready_requests(), but instead of returning only Requests from requestCompleted events, it returns all event types using a new Event class. Additionally camera.stop() has been changed to return events for that camera. This serves two purposes: first, it removes the requestCompleted events from the event queue, thus preventing the old events being returned when the camera is started again, and second, it allows the user to process those events if it so wants. All other event types are always collected and returned, except bufferCompleted which needs to be enabled by setting the CameraManager.buffer_completed_active to True. This is to reduce the overhead a bit. It is not clear if this is a necessary optimization or not. TODO: Documentation. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 195 +++++++++++++++++++++++-- src/py/libcamera/py_camera_manager.h | 66 ++++++++- src/py/libcamera/py_main.cpp | 44 +++++- 3 files changed, 281 insertions(+), 24 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 9ccb7aad..7d6dded4 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -5,6 +5,7 @@ #include "py_camera_manager.h" +#include #include #include #include @@ -35,11 +36,17 @@ PyCameraManager::PyCameraManager() if (ret) throw std::system_error(-ret, std::generic_category(), "Failed to start CameraManager"); + + cameraManager_->cameraAdded.connect(this, &PyCameraManager::handleCameraAdded); + cameraManager_->cameraRemoved.connect(this, &PyCameraManager::handleCameraRemoved); } PyCameraManager::~PyCameraManager() { LOG(Python, Debug) << "~PyCameraManager()"; + + cameraManager_->cameraAdded.disconnect(); + cameraManager_->cameraRemoved.disconnect(); } py::list PyCameraManager::cameras() @@ -60,6 +67,43 @@ py::list PyCameraManager::cameras() return l; } +PyCameraEvent PyCameraManager::convertEvent(const CameraEvent &event) +{ + /* + * We need to set a keep-alive here so that the camera keeps the + * camera manager alive. + */ + py::object py_cm = py::cast(this); + py::object py_cam = py::cast(event.camera_); + py::detail::keep_alive_impl(py_cam, py_cm); + + PyCameraEvent pyevent(event.type_, py_cam); + + switch (event.type_) { + case CameraEventType::CameraAdded: + case CameraEventType::CameraRemoved: + case CameraEventType::Disconnect: + /* No additional parameters to add */ + break; + + case CameraEventType::BufferCompleted: + pyevent.request_ = py::cast(event.request_); + pyevent.fb_ = py::cast(event.fb_); + break; + + case CameraEventType::RequestCompleted: + pyevent.request_ = py::cast(event.request_); + + /* Decrease the ref increased in Camera.queue_request() */ + pyevent.request_.dec_ref(); + + break; + } + + return pyevent; +} + +/* DEPRECATED */ std::vector PyCameraManager::getReadyRequests() { int ret = readFd(); @@ -72,21 +116,134 @@ std::vector PyCameraManager::getReadyRequests() std::vector py_reqs; - for (Request *request : getCompletedRequests()) { - py::object o = py::cast(request); - /* Decrease the ref increased in Camera.queue_request() */ - o.dec_ref(); - py_reqs.push_back(o); + for (const auto &ev : getEvents()) { + if (ev.type_ != CameraEventType::RequestCompleted) + continue; + + PyCameraEvent pyev = convertEvent(ev); + py_reqs.push_back(pyev.request_); } return py_reqs; } +std::vector PyCameraManager::getPyEvents() +{ + int ret = readFd(); + + if (ret == EAGAIN) { + LOG(Python, Debug) << "No events"; + return {}; + } + + if (ret != 0) + throw std::system_error(ret, std::generic_category()); + + std::vector events = getEvents(); + + LOG(Python, Debug) << "Got " << events.size() << " events"; + + std::vector pyevents; + pyevents.reserve(events.size()); + + std::transform(events.begin(), events.end(), std::back_inserter(pyevents), + [this](const CameraEvent &ev) { + return convertEvent(ev); + }); + + return pyevents; +} + +static bool isCameraSpecificEvent(const CameraEvent &event, std::shared_ptr &camera) +{ + return event.camera_ == camera && + (event.type_ == CameraEventType::RequestCompleted || + event.type_ == CameraEventType::BufferCompleted || + event.type_ == CameraEventType::Disconnect); +} + +std::vector PyCameraManager::getPyCameraEvents(std::shared_ptr camera) +{ + std::vector events; + size_t unhandled_size; + + { + MutexLocker guard(eventsMutex_); + + /* + * Collect events related to the given camera and remove them + * from the events_ vector. + */ + + auto it = events_.begin(); + while (it != events_.end()) { + if (isCameraSpecificEvent(*it, camera)) { + events.push_back(*it); + it = events_.erase(it); + } else { + it++; + } + } + + unhandled_size = events_.size(); + } + + /* Convert events to Python events */ + + std::vector pyevents; + + for (const auto &event : events) { + PyCameraEvent pyev = convertEvent(event); + pyevents.push_back(pyev); + } + + LOG(Python, Debug) << "Got " << pyevents.size() << " camera events, " + << unhandled_size << " unhandled events left"; + + return pyevents; +} + /* Note: Called from another thread */ -void PyCameraManager::handleRequestCompleted(Request *req) +void PyCameraManager::handleBufferCompleted(std::shared_ptr cam, Request *req, FrameBuffer *fb) { - pushRequest(req); - writeFd(); + if (!bufferCompletedEventActive_) + return; + + CameraEvent ev(CameraEventType::BufferCompleted, cam, req, fb); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleRequestCompleted(std::shared_ptr cam, Request *req) +{ + CameraEvent ev(CameraEventType::RequestCompleted, cam, req); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleDisconnected(std::shared_ptr cam) +{ + CameraEvent ev(CameraEventType::Disconnect, cam); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleCameraAdded(std::shared_ptr cam) +{ + CameraEvent ev(CameraEventType::CameraAdded, cam); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleCameraRemoved(std::shared_ptr cam) +{ + CameraEvent ev(CameraEventType::CameraRemoved, cam); + + pushEvent(ev); } void PyCameraManager::writeFd() @@ -116,16 +273,24 @@ int PyCameraManager::readFd() return -EIO; } -void PyCameraManager::pushRequest(Request *req) +void PyCameraManager::pushEvent(const CameraEvent &ev) { - MutexLocker guard(completedRequestsMutex_); - completedRequests_.push_back(req); + { + MutexLocker guard(eventsMutex_); + events_.push_back(ev); + } + + writeFd(); + + LOG(Python, Debug) << "Queued events: " << events_.size(); } -std::vector PyCameraManager::getCompletedRequests() +std::vector PyCameraManager::getEvents() { - std::vector v; - MutexLocker guard(completedRequestsMutex_); - swap(v, completedRequests_); + std::vector v; + + MutexLocker guard(eventsMutex_); + swap(v, events_); + return v; } diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 3525057d..757f6d8e 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -13,6 +13,48 @@ using namespace libcamera; +enum class CameraEventType { + CameraAdded, + CameraRemoved, + Disconnect, + RequestCompleted, + BufferCompleted, +}; + +/* + * This event struct is used internally to queue the events we receive from + * other threads. + */ +struct CameraEvent { + CameraEvent(CameraEventType type, std::shared_ptr camera, + Request *request = nullptr, FrameBuffer *fb = nullptr) + : type_(type), camera_(camera), request_(request), fb_(fb) + { + } + + CameraEventType type_; + std::shared_ptr camera_; + Request *request_; + FrameBuffer *fb_; +}; + +/* + * This event struct is passed to Python. We need to use pybind11::object here + * instead of a C++ pointer so that we keep a ref to the Request, and a + * keep-alive from the camera to the camera manager. + */ +struct PyCameraEvent { + PyCameraEvent(CameraEventType type, pybind11::object camera) + : type_(type), camera_(camera) + { + } + + CameraEventType type_; + pybind11::object camera_; + pybind11::object request_; + pybind11::object fb_; +}; + class PyCameraManager { public: @@ -26,20 +68,30 @@ public: int eventFd() const { return eventFd_.get(); } - std::vector getReadyRequests(); + std::vector getReadyRequests(); /* DEPRECATED */ + std::vector getPyEvents(); + std::vector getPyCameraEvents(std::shared_ptr camera); + + void handleBufferCompleted(std::shared_ptr cam, Request *req, FrameBuffer *fb); + void handleRequestCompleted(std::shared_ptr cam, Request *req); + void handleDisconnected(std::shared_ptr cam); + void handleCameraAdded(std::shared_ptr cam); + void handleCameraRemoved(std::shared_ptr cam); - void handleRequestCompleted(Request *req); + bool bufferCompletedEventActive_ = false; private: std::unique_ptr cameraManager_; UniqueFD eventFd_; - libcamera::Mutex completedRequestsMutex_; - std::vector completedRequests_ - LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_); + libcamera::Mutex eventsMutex_; + std::vector events_ + LIBCAMERA_TSA_GUARDED_BY(eventsMutex_); void writeFd(); int readFd(); - void pushRequest(Request *req); - std::vector getCompletedRequests(); + void pushEvent(const CameraEvent &ev); + std::vector getEvents(); + + PyCameraEvent convertEvent(const CameraEvent &event); }; diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 1d4c23b3..0fffc030 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -61,6 +61,7 @@ PYBIND11_MODULE(_libcamera, m) * https://pybind11.readthedocs.io/en/latest/advanced/misc.html#avoiding-c-types-in-docstrings */ + auto pyEvent = py::class_(m, "Event"); auto pyCameraManager = py::class_(m, "CameraManager"); auto pyCamera = py::class_(m, "Camera"); auto pyCameraConfiguration = py::class_(m, "CameraConfiguration"); @@ -93,6 +94,20 @@ PYBIND11_MODULE(_libcamera, m) m.def("log_set_level", &logSetLevel); /* Classes */ + + py::enum_(pyEvent, "Type") + .value("CameraAdded", CameraEventType::CameraAdded) + .value("CameraRemoved", CameraEventType::CameraRemoved) + .value("Disconnect", CameraEventType::Disconnect) + .value("RequestCompleted", CameraEventType::RequestCompleted) + .value("BufferCompleted", CameraEventType::BufferCompleted); + + pyEvent + .def_readonly("type", &PyCameraEvent::type_) + .def_readonly("camera", &PyCameraEvent::camera_) + .def_readonly("request", &PyCameraEvent::request_) + .def_readonly("fb", &PyCameraEvent::fb_); + pyCameraManager .def_static("singleton", []() { std::shared_ptr cm = gCameraManager.lock(); @@ -110,7 +125,13 @@ PYBIND11_MODULE(_libcamera, m) .def_property_readonly("cameras", &PyCameraManager::cameras) .def_property_readonly("event_fd", &PyCameraManager::eventFd) - .def("get_ready_requests", &PyCameraManager::getReadyRequests); + + /* DEPRECATED */ + .def("get_ready_requests", &PyCameraManager::getReadyRequests) + + .def("get_events", &PyCameraManager::getPyEvents) + + .def_readwrite("buffer_completed_active", &PyCameraManager::bufferCompletedEventActive_); pyCamera .def_property_readonly("id", &Camera::id) @@ -133,7 +154,17 @@ PYBIND11_MODULE(_libcamera, m) auto cm = gCameraManager.lock(); ASSERT(cm); - self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); + self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) { + cm->handleRequestCompleted(camera, req); + }); + + self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) { + cm->handleBufferCompleted(camera, req, fb); + }); + + self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() { + cm->handleDisconnected(camera); + }); ControlList controlList(self.controls()); @@ -154,10 +185,19 @@ PYBIND11_MODULE(_libcamera, m) int ret = self.stop(); self.requestCompleted.disconnect(); + self.bufferCompleted.disconnect(); + self.disconnected.disconnect(); + + auto cm = gCameraManager.lock(); + ASSERT(cm); + + auto events = cm->getPyCameraEvents(self.shared_from_this()); if (ret) throw std::system_error(-ret, std::generic_category(), "Failed to stop camera"); + + return events; }) .def("__str__", [](Camera &self) { From patchwork Thu Mar 9 14:25:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18364 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 18952BDE17 for ; Thu, 9 Mar 2023 14:26:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 87F00626E1; Thu, 9 Mar 2023 15:26:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371979; bh=FUnyVT+SxtKHp4nPrOfxAgg6Mxeo3KgPtH5RKskTD0E=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Pu3C0GFkKSlYi3nQdPZ2I6lQmINzjTwSa3wy3sxZnnlEt4S1TBoLxnqxwyWdi6TBs +emM1ONGv/gZsoFC+rjHCJundpxfegcQF+Kdjo87hUcycE+N45A3fzGuHX5XbavQdv bYahprzivATB1d9f09E5oCJyrZdSmevSBlbzWz8Q3fpnC8ADsef4IXDHTFCwjJTzoi d9iOKta37pvEe72Z0ucGs7SEfc8lnZ7oPB01LCFvXkLssxvaqiP5CK6YTrvTgcYV1L oNFb2J4bTxvnr7aAY+t7yl1ilJnXIaZLdtia8wFKL1l3EXPxu0AcnQs8BilsvQsdYu ZxxacsjDWH0wA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 756AA626D5 for ; Thu, 9 Mar 2023 15:26:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ih7Me3KS"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id F1EBF886; Thu, 9 Mar 2023 15:26:16 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371977; bh=FUnyVT+SxtKHp4nPrOfxAgg6Mxeo3KgPtH5RKskTD0E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ih7Me3KSsJQDEWzckjzW04xtAVRIrMpiXNIpZkPyofy819AB1+mmkV+MMTs/3WB/S +c2AZU2U06b/cZ70juAQmNl0ob7wGFAqNaedBp1n8ytR1qAzy0j4d5ek6LIIaRUBWH BwoXRjovp2Sg1tWocbpmotvA+tLh+6ZjF28XyW1I= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:49 +0200 Message-Id: <20230309142601.70556-4-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 03/15] py: cam.py: Use new events support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/py/cam/cam.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index a2a115c1..0b464256 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -230,11 +230,20 @@ 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) + evs = self.cm.get_events() + for ev in evs: + type = ev.type + + if type == libcam.Event.Type.CameraAdded: + print('Camera added:', ev.camera) + elif type == libcam.Event.Type.CameraRemoved: + print('Camera removed:', ev.camera) + 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") running = any(ctx.reqs_completed < ctx.opt_capture for ctx in self.contexts) return running @@ -242,7 +251,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 +458,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() + return 0 From patchwork Thu Mar 9 14:25:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18365 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 08ABAC329C for ; Thu, 9 Mar 2023 14:26:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 311B8626F0; Thu, 9 Mar 2023 15:26:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371981; bh=dISrbspAYCcz8jDXGpTgbn0J+SDAdO6eEhIAvVRnSUc=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=IDeh9dgf/RNT12Oty7jKUEFB22wUBA/oSzSQKDUGOThSirEmIkyCEBLfH0os+b7Du 54PMMNDTOBVgbPcClh+inSt5HLfT6r00C4N6vmCdAjcvmX5ja9SqfZjxBbu8ACtRXC 8j269X3jpTqNQW5XHNfqdieNFdZGJMuMaE4OlkFYm7s9Saqpg2d/n7yhnsVoatMRdv W+QwATofbt5Brha48o652bE35XvwvcgxCz/rC8pC2ABLF2r5TgAqmn1kgc0fT5FVvj b+PedGIPyWOYI4MN7iBJSbARMTK1ho3Zx6Fuf5+3o/T3sjR1Y2PshSNjzF+OyaFdhG 9jdqw4Vgip+nw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 16BCE626D5 for ; Thu, 9 Mar 2023 15:26:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TA4w02Fm"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 673B9589; Thu, 9 Mar 2023 15:26:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371977; bh=dISrbspAYCcz8jDXGpTgbn0J+SDAdO6eEhIAvVRnSUc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TA4w02Fm3gzu0IhQzbW0fG1GZ8KqfIPjOI3W1ATv3ql7FcnBXKIWGOulBGSgk8gdL QlVbAn64lBmHSoRlcaO3enJNoTQiKkVXk76Efb8cDco9u7B8aVyaBD+OMfaPJ375T0 6BJwM0v90HJfqTIrNOVOGCaXmgqZwGnF2ue8oFao= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:50 +0200 Message-Id: <20230309142601.70556-5-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 04/15] py: unittests.py: Use new events support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Update the Python unittests to use the new event model. The unittests expect a controlled environment to be run on, and the tests will fail if we get any other event but RequestCompleted (e.g. camera connected). Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- test/py/unittests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/py/unittests.py b/test/py/unittests.py index 794e46be..fab791d1 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -201,9 +201,9 @@ class SimpleCaptureMethods(CameraTesterBase): if not events: continue - ready_reqs = cm.get_ready_requests() - - reqs += ready_reqs + for ev in cm.get_events(): + self.assertEqual(ev.type, libcam.Event.Type.RequestCompleted) + reqs.append(ev.request) if len(reqs) == num_bufs: break @@ -267,9 +267,9 @@ class SimpleCaptureMethods(CameraTesterBase): while running: events = sel.select() for key, _ in events: - ready_reqs = cm.get_ready_requests() - - reqs += ready_reqs + for ev in cm.get_events(): + self.assertEqual(ev.type, libcam.Event.Type.RequestCompleted) + reqs.append(ev.request) if len(reqs) == num_bufs: running = False From patchwork Thu Mar 9 14:25:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18366 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A3224C329D for ; Thu, 9 Mar 2023 14:26:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BB4EE626F9; Thu, 9 Mar 2023 15:26:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371983; bh=edz4xFWTKtABKI4EC7dWnAxOKJeMyqJxs/BmtaJae4g=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=HOrnXeCXjn53XoVw37sYuHFpuDjYzueU2XubKyD7XMhLGjY6Jen2jpJCLxcvluq9g WGTr+o7U6Jkfk2byesE2nreN7yk9PeLqCPge1hzOnQAzyyKIlwokPXoPETVzJhk7wj 4DShB6ZzFCt/qqJKPRJzv9So9ctlnZludXN2QIMMhIlxnBKVs9NOG+/+ifz0uSorvk 0zJojbGcWlq75BXABndoYhj1olNtRyc/NXO63L1aqlHmxwveCUL+LNVwrG/o6DrJMd 3sKbcuABlf/0mwgZfTqcjaZzNLfO//NfxXQtSOu27o/aJ+L/D5gr7ElB+TQ1KBdgEO akX0AYVjTSUIQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 57F0B626DB for ; Thu, 9 Mar 2023 15:26:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ChpWjtCE"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CEDDC886; Thu, 9 Mar 2023 15:26:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371978; bh=edz4xFWTKtABKI4EC7dWnAxOKJeMyqJxs/BmtaJae4g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ChpWjtCEUSAn99Eq7tKNHQtSClKDXdgmsDTSZCRgWBaFYEECfjOuuNINiQVz6hcjU IEOEwVrOZm1P/JkBDdF+TbncfXFMQWQhQSzIE+I8H20JDJw0ngvS8Gu5BP40WqmeJ5 0+BE6MBHxQw5OpoCLNc+Zm30Uyxs7chZvrjJFxfc= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:51 +0200 Message-Id: <20230309142601.70556-6-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 05/15] py: simple-capture.py: Use new events support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Update simple-capture.py to the new event model. Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/py/examples/simple-capture.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py index 4b85408f..4fa36d6f 100755 --- a/src/py/examples/simple-capture.py +++ b/src/py/examples/simple-capture.py @@ -107,17 +107,22 @@ def main(): sel.register(cm.event_fd, selectors.EVENT_READ) while frames_done < TOTAL_FRAMES: - # cm.get_ready_requests() does not block, so we use a Selector to wait - # for a camera event. Here we should almost always get a single - # Request, but in some cases there could be multiple or none. + # cm.get_events() does not block, so we use a Selector to wait for a + # camera event. Here we should almost always get a single request + # completion event, but in some cases there could be multiple ones, + # other events, or no events at all. events = sel.select() if not events: continue - reqs = cm.get_ready_requests() + for ev in cm.get_events(): + # We are only interested in RequestCompleted events + if ev.type != libcam.Event.Type.RequestCompleted: + continue + + req = ev.request - for req in reqs: frames_done += 1 buffers = req.buffers From patchwork Thu Mar 9 14:25:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18367 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2E2F2C329E for ; Thu, 9 Mar 2023 14:26:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CB5E3626E2; Thu, 9 Mar 2023 15:26:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371984; bh=ivMAMfY7LAj8Ihu/ud9j0ozVaB9QF6fEIVB/e9R5Ukw=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=XI9qZYP91nD7+pd+9a0+hGLt5QLitQHhESmdapajS8N8vh0wq9fc8z3nTy4vfmoRH b+KluvQpiwnQKx3E5/cSop/e302FAkX/+VxDvnmcc71p2VzF5MqhUeAJzVG/t1d6Dl xwax5psiAZoR6VpFWsY3EnxE6RU5RLOWAxWmYrdoR8kDBpvHDJku/F61nbzxNmfwwj x4Bem8satuz5yFUVMjfKDX/rN5eZAd2MmjMDXrmeU5PvpJc7IW7MB2Y2FMoso6TZJU RRcBGwm8K49DHUfJXXl2OBg7mnqwjdPQ2P0WoF2aDf6Aclomvz3+Mzr0H5VmFQ+x/N fHl8O2jb7SJpw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CD3B061ED7 for ; Thu, 9 Mar 2023 15:26:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JBc+z+mT"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 49E53589; Thu, 9 Mar 2023 15:26:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371978; bh=ivMAMfY7LAj8Ihu/ud9j0ozVaB9QF6fEIVB/e9R5Ukw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JBc+z+mTKQFlCZhG0571ggM3xCkOaunCsOSk9iUxFLc48i3/izl5mG80fblcPJFMk H8j6V9XqSXcJTKXMxRh0YLF4+6qx+3bCl1cm+8OO1JoduPA00bh5AO3n3N2TxnoKWw ptoFnKDE4Y/KovCgycsJQMSzk3HrAmgjkRDBHTGA= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:52 +0200 Message-Id: <20230309142601.70556-7-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 06/15] py: simple-continuous-capture.py: Use new events support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Update simple-continuous-capture.py to the new event model. Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/py/examples/simple-continuous-capture.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/py/examples/simple-continuous-capture.py b/src/py/examples/simple-continuous-capture.py index e1cb931e..37ca623c 100755 --- a/src/py/examples/simple-continuous-capture.py +++ b/src/py/examples/simple-continuous-capture.py @@ -83,16 +83,17 @@ class CaptureContext: camera_contexts: list[CameraCaptureContext] = [] def handle_camera_event(self): - # cm.get_ready_requests() returns the ready requests, which in our case - # should almost always return a single Request, but in some cases there - # could be multiple or none. + # cm.get_events() returns the ready events, which in our case should + # almost always be a single RequestCompleted event, but in some cases + # there could be multiple ones, other events, or no events at all. - reqs = self.cm.get_ready_requests() + for ev in self.cm.get_events(): + # We are only interested in RequestCompleted events + if ev.type != libcam.Event.Type.RequestCompleted: + continue - # Process the captured frames - - for req in reqs: - self.handle_request(req) + # Process the captured frames + self.handle_request(ev.request) return True From patchwork Thu Mar 9 14:25:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18368 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 8354CC329F for ; Thu, 9 Mar 2023 14:26:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BFE5162702; Thu, 9 Mar 2023 15:26:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371985; bh=iaRylJ5l1sFUWopCwUojy8cIUA0Y2r4I2IXZHJH+WuA=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Q+xrVVB+f+mJBQsCXg15oIwozh7qPMgrtfvx+AmrFrmmfTTUxWMq0Xl5TuWFto1qc PwGBgf521obcrKK1rcBYM9C+IJQXuVfWxBrh082YUqPxnORmEXCOYiS/EUYZ1vbKax M1KEv5CkJL4n+PorWTP2teqBnhzqm6tuDxoTRdTkxiPcZpTOt3QM2I4w2EGrOMv24+ 5BsQB9InlGgav0xT6XWsFnynFRvgBNuAkALuq+RK3HF0fMhCHwxrUAaRYOHGnntrrY F2QrNuVirXqHcp/vGOc2KqC7FiwhmJyQd16y/L2cQC3tyIFfP27eT+Ff3MD3CouLCa rSyiSVAwEUhUw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4D24D626E6 for ; Thu, 9 Mar 2023 15:26:19 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="cRcvIR9w"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BEC66886; Thu, 9 Mar 2023 15:26:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371979; bh=iaRylJ5l1sFUWopCwUojy8cIUA0Y2r4I2IXZHJH+WuA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cRcvIR9wgXxifvOH+cisvmqvf37JwsNTjSZp5ySpudmjsxX2A6TYjOCeQsWEmdgVs DVwMieZLUYnn64QMSIkQZ8ErpzukFycou1NVyXDLELZhTJIRniLdKmMmA7WoOnFEcp 31yie2niS0tC4YcD6jruQyQdzy4aHsms+WYh6lVo= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:53 +0200 Message-Id: <20230309142601.70556-8-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 07/15] py: simple-cam.py: Use new events support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Update simple-cam.py to the new event model. Signed-off-by: Tomi Valkeinen Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/py/examples/simple-cam.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/py/examples/simple-cam.py b/src/py/examples/simple-cam.py index 1cd1019d..2d359cb7 100755 --- a/src/py/examples/simple-cam.py +++ b/src/py/examples/simple-cam.py @@ -19,16 +19,17 @@ TIMEOUT_SEC = 3 def handle_camera_event(cm): - # cm.get_ready_requests() returns the ready requests, which in our case - # should almost always return a single Request, but in some cases there - # could be multiple or none. + # cm.get_events() returns the ready events, which in our case should + # almost always be a single RequestCompleted event, but in some cases + # there could be multiple ones, other events, or no events at all. - reqs = cm.get_ready_requests() + for ev in cm.get_events(): + # We are only interested in RequestCompleted events + if ev.type != libcam.Event.Type.RequestCompleted: + continue - # Process the captured frames - - for req in reqs: - process_request(req) + # Process the captured frames + process_request(ev.request) def process_request(request): @@ -304,7 +305,7 @@ def main(): # CameraManager and an event will be raised using eventfd. # # The list of completed Requests can be retrieved with - # CameraManager.get_ready_requests(), which will also clear the list in the + # CameraManager.get_events(), which will also clear the list in the # CameraManager. # # The eventfd can be retrieved from CameraManager.event_fd, and the fd can From patchwork Thu Mar 9 14:25:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18369 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id EFB26C32A0 for ; Thu, 9 Mar 2023 14:26:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 88282626F7; Thu, 9 Mar 2023 15:26:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371986; bh=/JddElKh6X9ITcfC6NJ0RVE/Z/A8GC7PA2q4w37zG88=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=LBNhgORObVRrBpPMQ/tEQ2oFM/Jw+uaQCVreybZWJD/ZrCXQXOOvalAL8tuZU4V4Y dzAhU6tX8xRllP/O1tvkWlDnS2UJ+ZTpywGzFDAEmJIk0K2r5+iYpEjsgp/Wi0Opmm ZTATgM8fAcjzIcZP+qkhpyMynhIcrVmbiL4MdeQnxQJwzaxGxe6sRaYPzcAMB2E6ye qSf78lNBh1G8mDG3LVDrg8ABaOxLOX4DvLbRHEGj4S17gpyi5g5rn4pkXqX5DExR6K P/sqdbmuA0YqL4WmQsbW1PB2C1AQ64KD08SdiwyYpziaJtymds+E986g8wvgACq0Ny kK2qOq4qNnjvA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B3C3A626E7 for ; Thu, 9 Mar 2023 15:26:19 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wiky06JM"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4B6668B1; Thu, 9 Mar 2023 15:26:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371979; bh=/JddElKh6X9ITcfC6NJ0RVE/Z/A8GC7PA2q4w37zG88=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wiky06JM/2502k+Cw53z7FqMHxPLTdnFOHqpcR64P5etxzXzK3FI4zcYmS05oxqyM QA424XVZe0BYWWSfSFQqABy2Ja8xCYxxCXEdMt9B4kQQqeJQowFTdxUDinwBi3cMEH tWDaTBAMkGnlWPxIi+BSe+ztgYj/hQN0BbJWEIxg= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:54 +0200 Message-Id: <20230309142601.70556-9-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 08/15] py: Drop get_ready_requests() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" get_ready_requests() is deprecated, and the Python code in libcamera has been converted to the new event system, so we can drop get_ready_requests(). Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- src/py/libcamera/py_camera_manager.cpp | 24 ------------------------ src/py/libcamera/py_camera_manager.h | 1 - src/py/libcamera/py_main.cpp | 3 --- 3 files changed, 28 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 7d6dded4..c3b9646f 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -103,30 +103,6 @@ PyCameraEvent PyCameraManager::convertEvent(const CameraEvent &event) return pyevent; } -/* DEPRECATED */ -std::vector PyCameraManager::getReadyRequests() -{ - int ret = readFd(); - - if (ret == -EAGAIN) - return std::vector(); - - if (ret != 0) - throw std::system_error(-ret, std::generic_category()); - - std::vector py_reqs; - - for (const auto &ev : getEvents()) { - if (ev.type_ != CameraEventType::RequestCompleted) - continue; - - PyCameraEvent pyev = convertEvent(ev); - py_reqs.push_back(pyev.request_); - } - - return py_reqs; -} - std::vector PyCameraManager::getPyEvents() { int ret = readFd(); diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 757f6d8e..648d78af 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -68,7 +68,6 @@ public: int eventFd() const { return eventFd_.get(); } - std::vector getReadyRequests(); /* DEPRECATED */ std::vector getPyEvents(); std::vector getPyCameraEvents(std::shared_ptr camera); diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 0fffc030..1069fd9c 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -126,9 +126,6 @@ PYBIND11_MODULE(_libcamera, m) .def_property_readonly("event_fd", &PyCameraManager::eventFd) - /* DEPRECATED */ - .def("get_ready_requests", &PyCameraManager::getReadyRequests) - .def("get_events", &PyCameraManager::getPyEvents) .def_readwrite("buffer_completed_active", &PyCameraManager::bufferCompletedEventActive_); From patchwork Thu Mar 9 14:25:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18370 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 12841C3262 for ; Thu, 9 Mar 2023 14:26:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B974D626F4; Thu, 9 Mar 2023 15:26:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371987; bh=bggb8XXvFlEgt5vvMs4RTDr+kH9sme6KyV0CMYByloU=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=y2WXLlNfZgxlli+YJbj4yXzu6lRep6ma9lwz3LgXFSw6GZN1Ogqrv3Z8bwfGQEZ8s Wa7S7YR2Kn/+MYXLBV4sefKsB7SKPg5AMymd85P8EAErx5n1fuJ//sWSkIe2SUm0Ek U2lZpRQLYQtzvfTyUQjVAF8yuKjLLMWAlyyhseiBGm5+MNAiPenKS9/PRcI9Gt4X6D lRqbEFsK8jOzBC+C0fCsXanZUi7qKEnUeFOW3Q9cqWgONiDn0/pn4haYGPTlJkibmY aMG7Tk9giwpKgUX5DJAEiLBrgZnfWhnIAnKXjkfh9GQXZvoOOzb5IlLDFo5x3fqQcL KlsNvusNUcGlw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 24578626E2 for ; Thu, 9 Mar 2023 15:26:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SkfGjKZ3"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A34BC886; Thu, 9 Mar 2023 15:26:19 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371979; bh=bggb8XXvFlEgt5vvMs4RTDr+kH9sme6KyV0CMYByloU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SkfGjKZ3qE+0p3/BPAHWalisS1nEMqjrGo2OtlAfW/pNCLPVcW+yLhAwGyC12M3Si cODsuAJGzPT9dX49xVdtRW0l2d4Z3M4gMaDkUwHcqW+Z2iAopz/Zk7LVCv9ktYy0mj NhFl3KdTLT+G4SYT2BZRPnvfJjYOD3L1DTraYpIo= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:55 +0200 Message-Id: <20230309142601.70556-10-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 09/15] py: Add hotplug-monitor.py X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a simple example script which waits for camera hotplug events. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- src/py/examples/hotplug-monitor.py | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/py/examples/hotplug-monitor.py diff --git a/src/py/examples/hotplug-monitor.py b/src/py/examples/hotplug-monitor.py new file mode 100644 index 00000000..5f42970c --- /dev/null +++ b/src/py/examples/hotplug-monitor.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2022, Tomi Valkeinen + +import libcamera as libcam +import selectors +import sys + + +def main(): + cm = libcam.CameraManager.singleton() + + sel = selectors.DefaultSelector() + sel.register(cm.event_fd, selectors.EVENT_READ) + + print('Waiting for camera hotplug events... (CTRL-C to exit)') + + while True: + try: + events = sel.select() + if not events: + continue + except KeyboardInterrupt: + break + + events = cm.get_events() + + for ev in events: + if ev.type == libcam.Event.Type.CameraAdded: + print('Camera added:', ev.camera) + elif ev.type == libcam.Event.Type.CameraRemoved: + print('Camera removed:', ev.camera) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) From patchwork Thu Mar 9 14:25:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18371 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 82885C32A1 for ; Thu, 9 Mar 2023 14:26:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4340362701; Thu, 9 Mar 2023 15:26:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371988; bh=3wTUx+H1Obw0NPayo4vNEyDPTT+A2Yvuao3QMWAHhyc=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=OxKZrk6q1BT0OovsoxzMT0KzC0ljGuPVK0NfyaFJPnbMIsUzon4/UWBKHWTxPhfrF JrmbcAwfQbJZt2YjNbUJh1LS6sP3wZIjEE0Iw+aT3qUw417857HRIA4JJelT7oH8kz jUtmC7WHZ97i2swOLlayj5zVobuqYGYk8hCiUSFW2HZGw0pyAltepYmN3DjWoRc5II jHl5F/NwztUoI/JUk4r0rxBU0tc/YgFM/z6ih21zZM7dh4PONcgpxh2XlYLxINa5+j TNM/4x33GvgDjQ8LzcmM5dXhzVk73pr+Idr0iT+8fkogZzGCfvoOsgenZYDa4oMClq +H/qzmLeEYwSg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 84517626EA for ; Thu, 9 Mar 2023 15:26:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aOBTipCH"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 163658B1; Thu, 9 Mar 2023 15:26:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371980; bh=3wTUx+H1Obw0NPayo4vNEyDPTT+A2Yvuao3QMWAHhyc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aOBTipCHf0vohCFEK8GiGU3Sabp6oaCc8qWoc79XHFKlBqFj4OgxoCrY+66OThB5Y 5bx74k8Ks+h0zFzsg6Ed8+EMScKy0Y7/yZizuHQnrLr7dGE/0AIsHAiWozcy/aenPl pqBgmiEUOf2U3QKgvDuQe/kCMkKvfSRxf/ny/Z9I= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:56 +0200 Message-Id: <20230309142601.70556-11-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 10/15] py: unittests.py: Add weakref helpers and use del X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add helpers to check if a weakref or a list of weakrefs is alive or dead. Also use 'del' for local variables instead of setting the variable to None. This makes debugging the test easier as the locals will be gone from locals() dict. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- test/py/unittests.py | 60 ++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/test/py/unittests.py b/test/py/unittests.py index fab791d1..3bd2d7fd 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -18,6 +18,18 @@ class BaseTestCase(unittest.TestCase): def assertZero(self, a, msg=None): self.assertEqual(a, 0, msg) + def assertIsAlive(self, wr, msg='object not alive'): + self.assertIsNotNone(wr(), msg) + + def assertIsDead(self, wr, msg='object not dead'): + self.assertIsNone(wr(), msg) + + def assertIsAllAlive(self, wr_list, msg='object not alive'): + self.assertTrue(all([wr() for wr in wr_list]), msg) + + def assertIsAllDead(self, wr_list, msg='object not dead'): + self.assertTrue(all([not wr() for wr in wr_list]), msg) + class SimpleTestMethods(BaseTestCase): def test_get_ref(self): @@ -28,14 +40,14 @@ class SimpleTestMethods(BaseTestCase): self.assertIsNotNone(cam) wr_cam = weakref.ref(cam) - cm = None + del cm gc.collect() - self.assertIsNotNone(wr_cm()) + self.assertIsAlive(wr_cm) - cam = None + del cam gc.collect() - self.assertIsNone(wr_cm()) - self.assertIsNone(wr_cam()) + self.assertIsDead(wr_cm) + self.assertIsDead(wr_cam) def test_acquire_release(self): cm = libcam.CameraManager.singleton() @@ -89,8 +101,8 @@ class CameraTesterBase(BaseTestCase): self.cam = None self.cm = None - self.assertIsNone(self.wr_cm()) - self.assertIsNone(self.wr_cam()) + self.assertIsDead(self.wr_cm) + self.assertIsDead(self.wr_cam) class AllocatorTestMethods(CameraTesterBase): @@ -110,11 +122,11 @@ class AllocatorTestMethods(CameraTesterBase): wr_stream = weakref.ref(stream) # stream should keep streamconfig and camconfig alive - streamconfig = None - camconfig = None + del streamconfig + del camconfig gc.collect() - self.assertIsNotNone(wr_camconfig()) - self.assertIsNotNone(wr_streamconfig()) + self.assertIsAlive(wr_camconfig) + self.assertIsAlive(wr_streamconfig) allocator = libcam.FrameBufferAllocator(cam) num_bufs = allocator.allocate(stream) @@ -123,29 +135,29 @@ class AllocatorTestMethods(CameraTesterBase): buffers = allocator.buffers(stream) self.assertIsNotNone(buffers) - buffers = None + del buffers buffer = allocator.buffers(stream)[0] self.assertIsNotNone(buffer) wr_buffer = weakref.ref(buffer) - allocator = None + del allocator gc.collect() - self.assertIsNotNone(wr_buffer()) - self.assertIsNotNone(wr_allocator()) - self.assertIsNotNone(wr_stream()) + self.assertIsAlive(wr_buffer) + self.assertIsAlive(wr_allocator) + self.assertIsAlive(wr_stream) - buffer = None + del buffer gc.collect() - self.assertIsNone(wr_buffer()) - self.assertIsNone(wr_allocator()) - self.assertIsNotNone(wr_stream()) + self.assertIsDead(wr_buffer) + self.assertIsDead(wr_allocator) + self.assertIsAlive(wr_stream) - stream = None + del stream gc.collect() - self.assertIsNone(wr_stream()) - self.assertIsNone(wr_camconfig()) - self.assertIsNone(wr_streamconfig()) + self.assertIsDead(wr_stream) + self.assertIsDead(wr_camconfig) + self.assertIsDead(wr_streamconfig) class SimpleCaptureMethods(CameraTesterBase): From patchwork Thu Mar 9 14:25:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18372 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 30706C32A2 for ; Thu, 9 Mar 2023 14:26:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CF92F6270F; Thu, 9 Mar 2023 15:26:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371988; bh=H74Qncs4GZTGGWW/AD1IEKuXkea5UP5UCTCdfElM1nI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=vdhHGwztlY7QNq5F6WoaPYi39vwsEclJQzRr5bVzhxqLbHjzUWNnVHD7HAdsaCST5 Nmktk8W0SzUL/qAom+tY6jr/Le0Sr6hEZxJd5+X1nCZ4858cWDRe5ej2ST6ELyp8Vw IhoD5wQhw0ibmWusb/unXlyLktK1UkIase2NlBD/uf6xBdUcD8JUjf6SnvYTDa0ykA x5d92yKZaIyVJgfdpjhWNrkVqavcuTNo8VwJ0VJp4Rnk/1y4LIqila7w48dYozWOav 0VmQLionZKWkCrlbGdMzKcafP0PYWBVTnrbWxlULyCZBGcgHKvpmbzrQ6N39rJv3gw x0Is/ehs0iAdA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DEE85626D5 for ; Thu, 9 Mar 2023 15:26:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="A0zHtfOI"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 71FB9886; Thu, 9 Mar 2023 15:26:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371980; bh=H74Qncs4GZTGGWW/AD1IEKuXkea5UP5UCTCdfElM1nI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=A0zHtfOIbVr27/qlatv/ncxLp/P6wuDWccm19W+IfHkR52BRHVduOzJOWkiV09d7E ko7AoA8zxwq7/SLTYTUAAWyidB9AzvF0DJ2KWhWXaSIzDIF/3qGNvkMkFkJJwA/VVR pAVLXmfi/NDmxESdUtYospt3UV5FoATzbISG9KpI= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:57 +0200 Message-Id: <20230309142601.70556-12-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 11/15] py: unittests.py: Add test for refs & keep-alives X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a test for references and keep-alives by doing a capture, reusing the Requests once and testing that the objects are freed as soon as all the refs and keep-alives are gone. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- test/py/unittests.py | 128 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/test/py/unittests.py b/test/py/unittests.py index 3bd2d7fd..b52b3391 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -31,6 +31,134 @@ class BaseTestCase(unittest.TestCase): self.assertTrue(all([not wr() for wr in wr_list]), msg) +# Test references and keep-alives by doing a capture, reusing the Requests once +# and testing that the objects are freed as soon as all the refs and keep-alives +# are gone. +class CaptureRefTestMethods(BaseTestCase): + def test_ref(self): + cm = libcam.CameraManager.singleton() + wr_cm = weakref.ref(cm) + + cam = cm.get('platform/vimc.0 Sensor B') + self.assertIsNotNone(cam) + wr_cam = weakref.ref(cam) + + cam.acquire() + + camconfig = cam.generate_configuration([libcam.StreamRole.StillCapture]) + self.assertTrue(camconfig.size == 1) + wr_camconfig = weakref.ref(camconfig) + + streamconfig = camconfig.at(0) + wr_streamconfig = weakref.ref(streamconfig) + + cam.configure(camconfig) + + stream = streamconfig.stream + wr_stream = weakref.ref(stream) + + # stream keeps streamconfig and camconfig alive + del streamconfig + del camconfig + gc.collect() + self.assertIsAlive(wr_camconfig) + self.assertIsAlive(wr_streamconfig) + + allocator = libcam.FrameBufferAllocator(cam) + num_bufs = allocator.allocate(stream) + self.assertTrue(num_bufs > 0) + wr_allocator = weakref.ref(allocator) + + buffers = allocator.buffers(stream) + self.assertIsNotNone(buffers) + + wr_buffers = [weakref.ref(b) for b in buffers] + + del allocator + self.assertIsAlive(wr_allocator) + + reqs = [] + wr_reqs = [] + for i in range(num_bufs): + req = cam.create_request(i) + self.assertIsNotNone(req) + + wr_reqs.append(weakref.ref(req)) + + req.add_buffer(stream, buffers[i]) + + reqs.append(req) + + del buffers + del stream + + self.assertIsDead(wr_stream) + + cam.start() + + reqs_target = num_bufs * 2 + reqs_queued = 0 + reqs_captured = 0 + + for req in reqs: + cam.queue_request(req) + reqs_queued += 1 + + del req + del reqs + + # All buffers and reqs should be alive + self.assertIsAllAlive(wr_buffers) + self.assertIsAllAlive(wr_reqs) + + sel = selectors.DefaultSelector() + sel.register(cm.event_fd, selectors.EVENT_READ) + + while True: + events = sel.select() + if not events: + continue + del events + + for ev in cm.get_events(): + self.assertEqual(ev.type, libcam.Event.Type.RequestCompleted) + + reqs_captured += 1 + self.assertLessEqual(reqs_captured, reqs_target) + + if reqs_queued < reqs_target: + req: libcam.Request = typing.cast(libcam.Request, ev.request) + req.reuse() + cam.queue_request(req) + reqs_queued += 1 + del req + + del ev + + if reqs_captured == reqs_target: + break + + del sel + + # The allocator and all buffers and reqs should be dead + self.assertIsAllDead(wr_buffers) + self.assertIsAllDead(wr_reqs) + self.assertIsDead(wr_allocator) + + events = cam.stop() + self.assertZero(len(events)) + del events + cam.release() + + del cm + self.assertIsAlive(wr_cm) + self.assertIsAlive(wr_cam) + + del cam + self.assertIsDead(wr_cam) + self.assertIsDead(wr_cm) + + class SimpleTestMethods(BaseTestCase): def test_get_ref(self): cm = libcam.CameraManager.singleton() From patchwork Thu Mar 9 14:25:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18373 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2D338C32A3 for ; Thu, 9 Mar 2023 14:26:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A8D8062707; Thu, 9 Mar 2023 15:26:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371989; bh=4MqxyjxY/iP++ZltTTNrTBKjC7IhTnuZczK+Y7Nzd5A=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=bS1/oxAvQZYSDN2V1nHE3A/YFNW6tJDMZFRctax6JJsLPM+2sUo/Gb2V+T54lD698 iEs4kKxEgGifsNACEoUxGIRMj4SSaTChRpQZnwCfpgk9ffT8VpkeY9j2biybrZFXZo eInKEe1LPu7lTIvUJczJFn4w+c6e2A/T2MbyNQzHNnOSmcvkfiQjn2gsE5i8ibkWaF r4dWKfLmS11JoTNMHxPmURm6XkJFYsE0s1pQ4aNOZxC/RGXIASy5JyItG4/0Z2xlih SW7mC7Bp9551uOazylM1J/rLM9ksZdqfl4RhII2vMmcRlvPuI6lNsC0tHRMTAxsjhY ZpAPG6oYKzrWg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 43233626F1 for ; Thu, 9 Mar 2023 15:26:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="qG1eKF/3"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CB01B589; Thu, 9 Mar 2023 15:26:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371981; bh=4MqxyjxY/iP++ZltTTNrTBKjC7IhTnuZczK+Y7Nzd5A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qG1eKF/3zMULIX7VnTHvPcsudQxbPp6ZuL//Wl0obsSNvA79wPYSNA/KRLABQ3gEF udGuyV7MIDozrtd/Tx32W25Y4sP/angLYRBWEUEzcTrm5dUOpO05iuAgSAUbxNtL/Z 4JYZyr4lqV/EIk9/0WCJ/cUUX6JOx+oY6OaJUhTA= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:58 +0200 Message-Id: <20230309142601.70556-13-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 12/15] py: unittests.py: Fix type checker warnings X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- test/py/unittests.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/py/unittests.py b/test/py/unittests.py index b52b3391..9fd8730e 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -4,11 +4,9 @@ # Copyright (C) 2022, Tomi Valkeinen from collections import defaultdict -import errno import gc import libcamera as libcam import selectors -import time import typing import unittest import weakref @@ -406,7 +404,7 @@ class SimpleCaptureMethods(CameraTesterBase): running = True while running: events = sel.select() - for key, _ in events: + for _ in events: for ev in cm.get_events(): self.assertEqual(ev.type, libcam.Event.Type.RequestCompleted) reqs.append(ev.request) From patchwork Thu Mar 9 14:25:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18374 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B3AA9C32A4 for ; Thu, 9 Mar 2023 14:26:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4709062712; Thu, 9 Mar 2023 15:26:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371990; bh=a834tRZgWj+HQJbehTbn0uHl1GWwsCL3m61QJJX++a4=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=MvASTM+DzR+Da5PuAPaRv9DDRU0snJ2nBb/s5+BoRVwEP5oOm4skDHMvDYSEteyRR eaxia4339yjjfbxpxQyYvfWrpYr0QwgEq2RZYRnLKG7uORYjYSBd7ymXPzp7VKStZu qV/GaOim8MCylZTqyCBvCfJ+CxFJ0+cm6Qz4SwSUozgEsSElZwjGetmnDxnRYqPSaY nADueC0SB3DYLnMyhgtj+Pa8XhRhPRuR81nvdzW8MJBttf8gWaSWjI91iuUCoIKO+2 JO+6PsutFl9shcg/T5NNgwZ0tv8Zub/ZP73h7Az1CHV8xZtj3jML9tDipMkEY+Q8E4 OpLH0tf+yLe5A== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A104C626F4 for ; Thu, 9 Mar 2023 15:26:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sYGHLIDD"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 33D2B886; Thu, 9 Mar 2023 15:26:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371981; bh=a834tRZgWj+HQJbehTbn0uHl1GWwsCL3m61QJJX++a4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sYGHLIDDK+fOjCLEDeFEXLrj6+3xsGqOG47c+GXOyRm0NJkwefJrlMws1cj9EzMxJ 46cq7MEAwrfp/FeZ05l5Ty2EHHZsgoKDVAMbXgPNOj/Gv61uSQ4aHAjUABqlU1jnoY LQeLvdMhdFaGcFzDkYRMckgxDS/tKhcsrMh0+ulA= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:25:59 +0200 Message-Id: <20230309142601.70556-14-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 13/15] py: Move Color Space and Transform classes to separate files X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Move Color Space and Transform classes to separate files from the main py_main.cpp, for clarity. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- src/py/libcamera/meson.build | 2 + src/py/libcamera/py_color_space.cpp | 71 +++++++++++++++++ src/py/libcamera/py_main.cpp | 115 ++-------------------------- src/py/libcamera/py_transform.cpp | 81 ++++++++++++++++++++ 4 files changed, 159 insertions(+), 110 deletions(-) create mode 100644 src/py/libcamera/py_color_space.cpp create mode 100644 src/py/libcamera/py_transform.cpp diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index af19ffdd..f87b1b4d 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -14,10 +14,12 @@ pybind11_dep = pybind11_proj.get_variable('pybind11_dep') pycamera_sources = files([ 'py_camera_manager.cpp', + 'py_color_space.cpp', 'py_enums.cpp', 'py_geometry.cpp', 'py_helpers.cpp', 'py_main.cpp', + 'py_transform.cpp', ]) # Generate controls diff --git a/src/py/libcamera/py_color_space.cpp b/src/py/libcamera/py_color_space.cpp new file mode 100644 index 00000000..4bd86320 --- /dev/null +++ b/src/py/libcamera/py_color_space.cpp @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + * + * Python bindings - Color Space classes + */ + +#include +#include + +#include +#include +#include + +namespace py = pybind11; + +using namespace libcamera; + +void init_py_color_space(py::module &m) +{ + auto pyColorSpace = py::class_(m, "ColorSpace"); + auto pyColorSpacePrimaries = py::enum_(pyColorSpace, "Primaries"); + auto pyColorSpaceTransferFunction = py::enum_(pyColorSpace, "TransferFunction"); + auto pyColorSpaceYcbcrEncoding = py::enum_(pyColorSpace, "YcbcrEncoding"); + auto pyColorSpaceRange = py::enum_(pyColorSpace, "Range"); + + pyColorSpace + .def(py::init([](ColorSpace::Primaries primaries, + ColorSpace::TransferFunction transferFunction, + ColorSpace::YcbcrEncoding ycbcrEncoding, + ColorSpace::Range range) { + return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); + }), py::arg("primaries"), py::arg("transferFunction"), + py::arg("ycbcrEncoding"), py::arg("range")) + .def(py::init([](ColorSpace &other) { return other; })) + .def("__str__", [](ColorSpace &self) { + return ""; + }) + .def_readwrite("primaries", &ColorSpace::primaries) + .def_readwrite("transferFunction", &ColorSpace::transferFunction) + .def_readwrite("ycbcrEncoding", &ColorSpace::ycbcrEncoding) + .def_readwrite("range", &ColorSpace::range) + .def_static("Raw", []() { return ColorSpace::Raw; }) + .def_static("Srgb", []() { return ColorSpace::Srgb; }) + .def_static("Sycc", []() { return ColorSpace::Sycc; }) + .def_static("Smpte170m", []() { return ColorSpace::Smpte170m; }) + .def_static("Rec709", []() { return ColorSpace::Rec709; }) + .def_static("Rec2020", []() { return ColorSpace::Rec2020; }); + + pyColorSpacePrimaries + .value("Raw", ColorSpace::Primaries::Raw) + .value("Smpte170m", ColorSpace::Primaries::Smpte170m) + .value("Rec709", ColorSpace::Primaries::Rec709) + .value("Rec2020", ColorSpace::Primaries::Rec2020); + + pyColorSpaceTransferFunction + .value("Linear", ColorSpace::TransferFunction::Linear) + .value("Srgb", ColorSpace::TransferFunction::Srgb) + .value("Rec709", ColorSpace::TransferFunction::Rec709); + + pyColorSpaceYcbcrEncoding + .value("Null", ColorSpace::YcbcrEncoding::None) + .value("Rec601", ColorSpace::YcbcrEncoding::Rec601) + .value("Rec709", ColorSpace::YcbcrEncoding::Rec709) + .value("Rec2020", ColorSpace::YcbcrEncoding::Rec2020); + + pyColorSpaceRange + .value("Full", ColorSpace::Range::Full) + .value("Limited", ColorSpace::Range::Limited); + +} diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 1069fd9c..1585b14a 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -40,11 +40,13 @@ LOG_DEFINE_CATEGORY(Python) */ static std::weak_ptr gCameraManager; -void init_py_enums(py::module &m); +void init_py_color_space(py::module &m); void init_py_controls_generated(py::module &m); +void init_py_enums(py::module &m); void init_py_formats_generated(py::module &m); void init_py_geometry(py::module &m); void init_py_properties_generated(py::module &m); +void init_py_transform(py::module &m); PYBIND11_MODULE(_libcamera, m) { @@ -52,6 +54,8 @@ PYBIND11_MODULE(_libcamera, m) init_py_controls_generated(m); init_py_geometry(m); init_py_properties_generated(m); + init_py_color_space(m); + init_py_transform(m); /* Forward declarations */ @@ -80,12 +84,6 @@ PYBIND11_MODULE(_libcamera, m) auto pyFrameMetadata = py::class_(m, "FrameMetadata"); auto pyFrameMetadataStatus = py::enum_(pyFrameMetadata, "Status"); auto pyFrameMetadataPlane = py::class_(pyFrameMetadata, "Plane"); - auto pyTransform = py::class_(m, "Transform"); - auto pyColorSpace = py::class_(m, "ColorSpace"); - auto pyColorSpacePrimaries = py::enum_(pyColorSpace, "Primaries"); - auto pyColorSpaceTransferFunction = py::enum_(pyColorSpace, "TransferFunction"); - auto pyColorSpaceYcbcrEncoding = py::enum_(pyColorSpace, "YcbcrEncoding"); - auto pyColorSpaceRange = py::enum_(pyColorSpace, "Range"); auto pyPixelFormat = py::class_(m, "PixelFormat"); init_py_formats_generated(m); @@ -455,109 +453,6 @@ PYBIND11_MODULE(_libcamera, m) pyFrameMetadataPlane .def_readwrite("bytes_used", &FrameMetadata::Plane::bytesused); - pyTransform - .def(py::init([](int rotation, bool hflip, bool vflip, bool transpose) { - bool ok; - - Transform t = transformFromRotation(rotation, &ok); - if (!ok) - throw std::invalid_argument("Invalid rotation"); - - if (hflip) - t ^= Transform::HFlip; - if (vflip) - t ^= Transform::VFlip; - if (transpose) - t ^= Transform::Transpose; - return t; - }), py::arg("rotation") = 0, py::arg("hflip") = false, - py::arg("vflip") = false, py::arg("transpose") = false) - .def(py::init([](Transform &other) { return other; })) - .def("__str__", [](Transform &self) { - return ""; - }) - .def_property("hflip", - [](Transform &self) { - return !!(self & Transform::HFlip); - }, - [](Transform &self, bool hflip) { - if (hflip) - self |= Transform::HFlip; - else - self &= ~Transform::HFlip; - }) - .def_property("vflip", - [](Transform &self) { - return !!(self & Transform::VFlip); - }, - [](Transform &self, bool vflip) { - if (vflip) - self |= Transform::VFlip; - else - self &= ~Transform::VFlip; - }) - .def_property("transpose", - [](Transform &self) { - return !!(self & Transform::Transpose); - }, - [](Transform &self, bool transpose) { - if (transpose) - self |= Transform::Transpose; - else - self &= ~Transform::Transpose; - }) - .def("inverse", [](Transform &self) { return -self; }) - .def("invert", [](Transform &self) { - self = -self; - }) - .def("compose", [](Transform &self, Transform &other) { - self = self * other; - }); - - pyColorSpace - .def(py::init([](ColorSpace::Primaries primaries, - ColorSpace::TransferFunction transferFunction, - ColorSpace::YcbcrEncoding ycbcrEncoding, - ColorSpace::Range range) { - return ColorSpace(primaries, transferFunction, ycbcrEncoding, range); - }), py::arg("primaries"), py::arg("transferFunction"), - py::arg("ycbcrEncoding"), py::arg("range")) - .def(py::init([](ColorSpace &other) { return other; })) - .def("__str__", [](ColorSpace &self) { - return ""; - }) - .def_readwrite("primaries", &ColorSpace::primaries) - .def_readwrite("transferFunction", &ColorSpace::transferFunction) - .def_readwrite("ycbcrEncoding", &ColorSpace::ycbcrEncoding) - .def_readwrite("range", &ColorSpace::range) - .def_static("Raw", []() { return ColorSpace::Raw; }) - .def_static("Srgb", []() { return ColorSpace::Srgb; }) - .def_static("Sycc", []() { return ColorSpace::Sycc; }) - .def_static("Smpte170m", []() { return ColorSpace::Smpte170m; }) - .def_static("Rec709", []() { return ColorSpace::Rec709; }) - .def_static("Rec2020", []() { return ColorSpace::Rec2020; }); - - pyColorSpacePrimaries - .value("Raw", ColorSpace::Primaries::Raw) - .value("Smpte170m", ColorSpace::Primaries::Smpte170m) - .value("Rec709", ColorSpace::Primaries::Rec709) - .value("Rec2020", ColorSpace::Primaries::Rec2020); - - pyColorSpaceTransferFunction - .value("Linear", ColorSpace::TransferFunction::Linear) - .value("Srgb", ColorSpace::TransferFunction::Srgb) - .value("Rec709", ColorSpace::TransferFunction::Rec709); - - pyColorSpaceYcbcrEncoding - .value("Null", ColorSpace::YcbcrEncoding::None) - .value("Rec601", ColorSpace::YcbcrEncoding::Rec601) - .value("Rec709", ColorSpace::YcbcrEncoding::Rec709) - .value("Rec2020", ColorSpace::YcbcrEncoding::Rec2020); - - pyColorSpaceRange - .value("Full", ColorSpace::Range::Full) - .value("Limited", ColorSpace::Range::Limited); - pyPixelFormat .def(py::init<>()) .def(py::init()) diff --git a/src/py/libcamera/py_transform.cpp b/src/py/libcamera/py_transform.cpp new file mode 100644 index 00000000..08783e29 --- /dev/null +++ b/src/py/libcamera/py_transform.cpp @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + * + * Python bindings - Transform class + */ + +#include +#include + +#include +#include +#include + +namespace py = pybind11; + +using namespace libcamera; + +void init_py_transform(py::module &m) +{ + auto pyTransform = py::class_(m, "Transform"); + + pyTransform + .def(py::init([](int rotation, bool hflip, bool vflip, bool transpose) { + bool ok; + + Transform t = transformFromRotation(rotation, &ok); + if (!ok) + throw std::invalid_argument("Invalid rotation"); + + if (hflip) + t ^= Transform::HFlip; + if (vflip) + t ^= Transform::VFlip; + if (transpose) + t ^= Transform::Transpose; + return t; + }), py::arg("rotation") = 0, py::arg("hflip") = false, + py::arg("vflip") = false, py::arg("transpose") = false) + .def(py::init([](Transform &other) { return other; })) + .def("__str__", [](Transform &self) { + return ""; + }) + .def_property("hflip", + [](Transform &self) { + return !!(self & Transform::HFlip); + }, + [](Transform &self, bool hflip) { + if (hflip) + self |= Transform::HFlip; + else + self &= ~Transform::HFlip; + }) + .def_property("vflip", + [](Transform &self) { + return !!(self & Transform::VFlip); + }, + [](Transform &self, bool vflip) { + if (vflip) + self |= Transform::VFlip; + else + self &= ~Transform::VFlip; + }) + .def_property("transpose", + [](Transform &self) { + return !!(self & Transform::Transpose); + }, + [](Transform &self, bool transpose) { + if (transpose) + self |= Transform::Transpose; + else + self &= ~Transform::Transpose; + }) + .def("inverse", [](Transform &self) { return -self; }) + .def("invert", [](Transform &self) { + self = -self; + }) + .def("compose", [](Transform &self, Transform &other) { + self = self * other; + }); +} From patchwork Thu Mar 9 14:26:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18375 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3A1DFC32A5 for ; Thu, 9 Mar 2023 14:26:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C431762715; Thu, 9 Mar 2023 15:26:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371990; bh=5FwgOSzFtXbcHkPOsawNfIsu3wCs2lDtN23afzfYdJg=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=CWOpzUrgCBi43wsp2GI943e9o3jMptiOtT0E29TfYtPbw/gOJW6X5KoKmf/W/gRXN HHw9NFBeCyBEmJnws7QhRWlf0441U4Ko8qTiSBZ1YdnHZz/FelCb6VR28FLr9pWOqc W0tMvwDqowFbo+PCEu7bQ6CHqRzhdmIsWBpKKRtunb0UetB6fzQOueYewmtSqOZ+7L eVQzimXE0Pwr7JBDJMiwVq4Kz+HgTpmgjhBAlQZbyY8jsDzGbOn97i7den8hMmY1Uc /gzMKMGhPOpWn3dcuCXxAHc+OGeWoMJZ7LxgCPAjRsDQKf1m9EkJVylPaG3ajdP6II T0fcvpDZaKaHA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F1949626F5 for ; Thu, 9 Mar 2023 15:26:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DH7UYVkU"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8C577589; Thu, 9 Mar 2023 15:26:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371981; bh=5FwgOSzFtXbcHkPOsawNfIsu3wCs2lDtN23afzfYdJg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DH7UYVkUD427sYhVd0n37rhXgXGVEe5n870PsdaUccwHfeAjwqrCqOtbe6mYr+vDq 3UsLscvgPf1XAlvRF541WoYBB9O+RCzBnDX0tgFvAMj/dfJ155kbb7A5tyGo16hLkR GHk7eQGciGJAI52B6NSBaPUlyEeWrM2j+hYv/x9o= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:26:00 +0200 Message-Id: <20230309142601.70556-15-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 14/15] py: Improve stub type generation for PyCameraEvent X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" We store the camera, request and fb fields in PyCameraEvent as py::objects, so that we keep the refs and keep-alives. When we return py::objects to the Python side, they, obviously, show up as "objects" when the stub generation is looking at the fields. Fix this by manually casting the py::objects to the correct C++ counterparts, which causes pybind11 to assign the correct types for the properties, helpind stubgen to generate correct types. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- src/py/libcamera/py_main.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 1585b14a..cb7088b1 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -100,11 +100,19 @@ PYBIND11_MODULE(_libcamera, m) .value("RequestCompleted", CameraEventType::RequestCompleted) .value("BufferCompleted", CameraEventType::BufferCompleted); + /* + * For camera, request and fb fields, manually typecast to the C++ type, + * so that pybind11 assigns the actual type for the return value. This + * makes the stubs generated by pybind11-stubgen contain the correct type, + * instead of "object". + * + * Maybe there's a better way to do this... + */ pyEvent .def_readonly("type", &PyCameraEvent::type_) - .def_readonly("camera", &PyCameraEvent::camera_) - .def_readonly("request", &PyCameraEvent::request_) - .def_readonly("fb", &PyCameraEvent::fb_); + .def_property_readonly("camera", [](PyCameraEvent &self) { return self.camera_.cast(); }) + .def_property_readonly("request", [](PyCameraEvent &self) { return self.request_.cast(); }) + .def_property_readonly("fb", [](PyCameraEvent &self) { return self.fb_.cast(); }); pyCameraManager .def_static("singleton", []() { From patchwork Thu Mar 9 14:26:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 18376 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CD191C32A6 for ; Thu, 9 Mar 2023 14:26:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6EDB062718; Thu, 9 Mar 2023 15:26:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1678371991; bh=H9Z7KLUXihKcr8UogM/YAwzpdvBQoIgZwip8xyxiUKE=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Von8tyngRaZVFLcngtTJdmZ0M2Hbu17JNoatDm70T8uK2BQUtDdt/0l1OQX+GLPf9 Rjc9joTVONTAbx3NXxPvAmzMqAGEtyAb1+1F652cARWzPGJWzC3DTBh3QLQIqfp7k8 0kg8+HUA2AMFS/6UbgpA2R0T9oPIAlXrKV+K3u/OfxtmAdeVKYUoK30FzoWYPKECi6 V4PXmeSI0c5Ny2ZTJOYqTUDvnKaMdddUqzX9vUFpoIflBAULEqTVabCsKUl/VLrqrF lz2okTvComplWE5GA1lwaEnRcA/myHD981l3FNY1xI3RwZOg0P5NCRJ/uM7a/a2Ae0 Sky/+l3E+ihAw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 590A6626D8 for ; Thu, 9 Mar 2023 15:26:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="AAx2kdGr"; dkim-atps=neutral Received: from desky.lan (91-154-32-225.elisa-laajakaista.fi [91.154.32.225]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E537E886; Thu, 9 Mar 2023 15:26:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1678371982; bh=H9Z7KLUXihKcr8UogM/YAwzpdvBQoIgZwip8xyxiUKE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AAx2kdGru3mqqccaOtY9QEJFeJfp13kOxUYv5gX7lxTpw81IdqQfiFuOl6eWNeWJn F8m9sJFHLrYp+SDlJTKrD71FjzOyAPVH1QCj68vGk6yjHvJLkSR149VqEextn0xOU1 IxH6nm/75SkW3LQf10OsJ+Mq8CmdZwyj5PqpDGcA= To: libcamera-devel@lists.libcamera.org Date: Thu, 9 Mar 2023 16:26:01 +0200 Message-Id: <20230309142601.70556-16-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> References: <20230309142601.70556-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 15/15] py: Hack for different event enable mechanism X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Instead of connecting all the event signals automatically (except bufferCompletedEvent), add properties to Camera object to enable/disable the events. E.g. cam.requestCompletedEnabled = True would enable the delivery of request completed events. Also, as (I think) almost everyone wants request completed events, subscribe to them automatically for all cameras when creating the PyCameraManager singleton. If someone doesn't want request completed events, they can set the cam.requestCompletedEnabled to False. This is just a quick hack, a proper implementation would need a bit more abstraction functionality to the PyCameraManager. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 3 - src/py/libcamera/py_camera_manager.h | 5 +- src/py/libcamera/py_main.cpp | 113 ++++++++++++++++++++----- 3 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index c3b9646f..ca0f6f7a 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -182,9 +182,6 @@ std::vector PyCameraManager::getPyCameraEvents(std::shared_ptr cam, Request *req, FrameBuffer *fb) { - if (!bufferCompletedEventActive_) - return; - CameraEvent ev(CameraEventType::BufferCompleted, cam, req, fb); pushEvent(ev); diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 648d78af..41105de7 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -77,10 +77,11 @@ public: void handleCameraAdded(std::shared_ptr cam); void handleCameraRemoved(std::shared_ptr cam); - bool bufferCompletedEventActive_ = false; + uint32_t event_mask_; + std::map, uint32_t> camera_event_masks_; + std::unique_ptr cameraManager_; private: - std::unique_ptr cameraManager_; UniqueFD eventFd_; libcamera::Mutex eventsMutex_; diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index cb7088b1..54b58366 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -48,6 +48,57 @@ void init_py_geometry(py::module &m); void init_py_properties_generated(py::module &m); void init_py_transform(py::module &m); +static bool py_camera_get_event_flag(std::shared_ptr camera, CameraEventType event_type) +{ + const uint32_t evbit = 1 << (uint32_t)event_type; + + auto cm = gCameraManager.lock(); + ASSERT(cm); + + auto it = cm->camera_event_masks_.find(camera); + + uint32_t mask = 0; + + if (it != cm->camera_event_masks_.end()) + mask = it->second; + + return !!(mask & evbit); +} + +static void py_camera_set_event_flag(std::shared_ptr camera, CameraEventType event_type, bool value) +{ + const uint32_t evbit = 1 << (uint32_t)event_type; + + auto cm = gCameraManager.lock(); + ASSERT(cm); + + uint32_t mask = 0; + + auto it = cm->camera_event_masks_.find(camera); + if (it != cm->camera_event_masks_.end()) + mask = it->second; + + bool old_val = !!(mask & evbit); + + if (old_val == value) + return; + + if (value) + mask |= evbit; + else + mask &= ~evbit; + + cm->camera_event_masks_[camera] = mask; + + if (value) { + camera->requestCompleted.connect(camera.get(), [cm, camera](Request *req) { + cm->handleRequestCompleted(camera, req); + }); + } else { + camera->requestCompleted.disconnect(); + } +} + PYBIND11_MODULE(_libcamera, m) { init_py_enums(m); @@ -121,6 +172,10 @@ PYBIND11_MODULE(_libcamera, m) if (!cm) { cm = std::make_shared(); gCameraManager = cm; + + /* Always enable RequestCompleted for all cameras */ + for (auto cam : cm->cameraManager_->cameras()) + py_camera_set_event_flag(cam, CameraEventType::RequestCompleted, true); } return cm; @@ -132,12 +187,47 @@ PYBIND11_MODULE(_libcamera, m) .def_property_readonly("event_fd", &PyCameraManager::eventFd) - .def("get_events", &PyCameraManager::getPyEvents) - - .def_readwrite("buffer_completed_active", &PyCameraManager::bufferCompletedEventActive_); + .def("get_events", &PyCameraManager::getPyEvents); pyCamera .def_property_readonly("id", &Camera::id) + + .def_property( + "requestCompletedEnabled", + [](Camera &self) { + return py_camera_get_event_flag(self.shared_from_this(), + CameraEventType::RequestCompleted); + }, + [](Camera &self, bool val) { + py_camera_set_event_flag(self.shared_from_this(), + CameraEventType::RequestCompleted, + val); + }) + + .def_property( + "bufferCompletedEnabled", + [](Camera &self) { + return py_camera_get_event_flag(self.shared_from_this(), + CameraEventType::BufferCompleted); + }, + [](Camera &self, bool val) { + py_camera_set_event_flag(self.shared_from_this(), + CameraEventType::BufferCompleted, + val); + }) + + .def_property( + "disconnectEnabled", + [](Camera &self) { + return py_camera_get_event_flag(self.shared_from_this(), + CameraEventType::Disconnect); + }, + [](Camera &self, bool val) { + py_camera_set_event_flag(self.shared_from_this(), + CameraEventType::Disconnect, + val); + }) + .def("acquire", [](Camera &self) { int ret = self.acquire(); if (ret) @@ -157,18 +247,6 @@ PYBIND11_MODULE(_libcamera, m) auto cm = gCameraManager.lock(); ASSERT(cm); - self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) { - cm->handleRequestCompleted(camera, req); - }); - - self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) { - cm->handleBufferCompleted(camera, req, fb); - }); - - self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() { - cm->handleDisconnected(camera); - }); - ControlList controlList(self.controls()); for (const auto& [id, obj]: controls) { @@ -178,7 +256,6 @@ PYBIND11_MODULE(_libcamera, m) int ret = self.start(&controlList); if (ret) { - self.requestCompleted.disconnect(); throw std::system_error(-ret, std::generic_category(), "Failed to start camera"); } @@ -187,10 +264,6 @@ PYBIND11_MODULE(_libcamera, m) .def("stop", [](Camera &self) { int ret = self.stop(); - self.requestCompleted.disconnect(); - self.bufferCompleted.disconnect(); - self.disconnected.disconnect(); - auto cm = gCameraManager.lock(); ASSERT(cm);