From patchwork Wed Jun 29 07:04:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16407 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 B634FBE173 for ; Wed, 29 Jun 2022 07:04:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 961A665636; Wed, 29 Jun 2022 09:04:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486283; bh=kAL85UrCX4ZS6H5jh0S4WDQbWuTWODAQW0q+OmJ+1ng=; 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=s5oFWW9wJcV7M+Xa+WosaofnRNWibP+EmSxmnqP63YdSqNBu0zROTjkT8CJdUe+CN /hEvhaVIFi7UeeMWTVmrE5xRB5ppEkL8m1GRkcJtL7EHGrI6G/GWdjSx2n20dK70yw sSZMZa0g5kty9vPPjdWPGTqfDdZJNUzgxgcHE2kHc5KuIinSs/tnOFH6Sahr5xz0Og b6bSbvkOKrmsHXbUOOLMEP7nFzNw8QKSKHFuCwGdyHNTHjhnBz46cyXQkDzvbyf+MS zq+YBpj3oIZUcOcHeg/DSHRImFALF75UOhpt0j7yDy4w4kn2vxTd6SiQH7VTQh0IjT vgJqak6LA876g== 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 9A46F65631 for ; Wed, 29 Jun 2022 09:04:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Wq+zs6lX"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E2FDB6D1; Wed, 29 Jun 2022 09:04:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486280; bh=kAL85UrCX4ZS6H5jh0S4WDQbWuTWODAQW0q+OmJ+1ng=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Wq+zs6lXsW7fgOhcjWCKrn9opX6NDsUu/IFwlfGP3Ltf1tyEn6pw/xkYUJc3zR5sY 3I84rojbEiRd/m5Ai+enYq4yADbtinRzC8+8oayrWT86hEZe+YMM4YmZ89eK2er2Gi k1oRXA+hHZZY7c0jJWcMSwJuvt/9lbpe4QRFpbYM= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:03 +0300 Message-Id: <20220629070416.17550-2-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 01/14] py: cam.py: Fix multi camera capture without -C 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" -C flag is supposed to affect only the camera that was previously defined in the arguments. That's not the case, and, e.g.: cam.py -c2 -C -c3 causes camera 3 to start capturing, but it stops after the initial Requests have been completed. Fix the issue by filtering out camera contexts that do not have -C defined. Signed-off-by: Tomi Valkeinen Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- src/py/cam/cam.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 2ae89fa8..8a82e092 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -434,7 +434,10 @@ def main(): if args.info: ctx.do_cmd_info() - if args.capture: + # Filter out capture contexts which are not marked for capture + contexts = [ctx for ctx in contexts if ctx.opt_capture > 0] + + if contexts: state = CaptureState(cm, contexts) if args.renderer == 'null': From patchwork Wed Jun 29 07:04:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16408 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 01B1EBE173 for ; Wed, 29 Jun 2022 07:04:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D615365638; Wed, 29 Jun 2022 09:04:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486285; bh=GApko8Xk7fqa9bcs/6OBQN3OzWVj2WUfL+sa81qKAlw=; 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=JjP6a5mrM8rjl2ANwxFMYt+uAMmJBnyMCwj3v5y7oUUS8tLouUNCto3elFxusO592 YALk9SIjSEiibaf4iy/PTY2whCxilct4wpI8oReBywijyhG+olBMKzLLrrvvzI0FGw BGDLRG4Tgm7VTPdmSR39V5OmwMWM1Gc1wRTCcx3i6hq4BohOgpeZoSVoejrNHnIH5G H3s+FkLssgbOT4oZb7vt4nu5fNeStb9bxsR/A2A2TZ0+wKoG4nrPB9V664URrELONL mTf9IgCslxyQ/J6G5BZsYhyCsWnIREjOjHta7JYVJwAHMTebjMH8sWAOrdmflXp+b3 hAkRg2FqkVs8g== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EB07865635 for ; Wed, 29 Jun 2022 09:04:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DGwnBM4R"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6A5E76F0; Wed, 29 Jun 2022 09:04:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486280; bh=GApko8Xk7fqa9bcs/6OBQN3OzWVj2WUfL+sa81qKAlw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DGwnBM4RX0woFczNqiqwQwyClG+eC5DO5k4sGrRx/qEjqM1BPpGI2jEMXz51vytZA jdM7Szwhwjha97wMT0Yr4VoDd0ep+56Fx3oL70B4Kga0+/PzD2UOBNM+DjesrkN20u mekYO9igeYp1axVw7GY/0C8Hvv4gUr5InXAPL5ts= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:04 +0300 Message-Id: <20220629070416.17550-3-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 02/14] py: Add Python logging category 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 Python logging category, and use it in handleRequestCompleted(). Signed-off-by: Tomi Valkeinen Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- src/py/libcamera/py_main.cpp | 12 ++++++++++-- src/py/libcamera/py_main.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/py/libcamera/py_main.h diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 505cc3dc..4b698f77 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -5,6 +5,8 @@ * Python bindings */ +#include "py_main.h" + #include #include #include @@ -23,6 +25,12 @@ namespace py = pybind11; using namespace libcamera; +namespace libcamera { + +LOG_DEFINE_CATEGORY(Python) + +} + template static py::object valueOrTuple(const ControlValue &cv) { @@ -120,10 +128,10 @@ static void handleRequestCompleted(Request *req) size_t s = write(gEventfd, &v, 8); /* * We should never fail, and have no simple means to manage the error, - * so let's use LOG(Fatal). + * so let's log a fatal error. */ if (s != 8) - LOG(Fatal) << "Unable to write to eventfd"; + LOG(Python, Fatal) << "Unable to write to eventfd"; } void init_py_enums(py::module &m); diff --git a/src/py/libcamera/py_main.h b/src/py/libcamera/py_main.h new file mode 100644 index 00000000..5bb5f2d1 --- /dev/null +++ b/src/py/libcamera/py_main.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + */ + +#pragma once + +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Python) + +} From patchwork Wed Jun 29 07:04:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16409 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 20986BE173 for ; Wed, 29 Jun 2022 07:04:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 11ED065648; Wed, 29 Jun 2022 09:04:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486287; bh=Pa5/wJaKKF4mEpNi/1FHR/wV42PbNXVxJ/ZFRVOKfAQ=; 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=B9C9MiCTiQyPl4pfTqKBbShZ2BTH5pJf2UBxsGPo3F++oKO2M/vSqGVVq9yioa2bK N5qbnaVwJnziyp3H08vMFufqb87N3+oZEZ6kvlGzD97sj0i5HOZf9gozAyO2Umb1JN IA/rl6zfVZZAmPOYITwqBvy2OHkTwLq2WoQCuMGxODgUUJkrcIwz2TASdTzTizmDHu zlfZnZQdy2GWVt8FRBmT3XK3bnoNyFpj7HBxpSWKyz4FK67cIYA2N1xRQrT72UuLxh WshikOyI7ZQNEBB7mt2lfAiXWwwINXUKhY4AelvrzQ47rZGZ4TmCxqT8zsKNuxytPy fK2aRtraKWmFw== 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 740A065635 for ; Wed, 29 Jun 2022 09:04:41 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="t2j6apth"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E56BF4A8; Wed, 29 Jun 2022 09:04:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486281; bh=Pa5/wJaKKF4mEpNi/1FHR/wV42PbNXVxJ/ZFRVOKfAQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=t2j6apthdQv39A51Zri0YY0XRvtSeEJ6HR8qM5TqsN2UlimsVQfbRGrYT8LZi+g/Y z7CTNa0b3PlTlWdXuyUJcjgNY0KsidCbGopkbDLm+u6b6jskh0II4UZwRfJNm/jGvE r0xX1lWT3pNQSll77ZcN9lqkLNlubLSeOBWGf4gU= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:05 +0300 Message-Id: <20220629070416.17550-4-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 03/14] py: Move ControlValue helpers to py_helpers.cpp 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" Clean up the py_main.cpp a bit by moving the ControlValue helpers to a separate file. Signed-off-by: Tomi Valkeinen Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- src/py/libcamera/meson.build | 1 + src/py/libcamera/py_helpers.cpp | 97 +++++++++++++++++++++++++++++++++ src/py/libcamera/py_helpers.h | 13 +++++ src/py/libcamera/py_main.cpp | 83 +--------------------------- 4 files changed, 113 insertions(+), 81 deletions(-) create mode 100644 src/py/libcamera/py_helpers.cpp create mode 100644 src/py/libcamera/py_helpers.h diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index eb884538..04578bac 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -15,6 +15,7 @@ pybind11_dep = pybind11_proj.get_variable('pybind11_dep') pycamera_sources = files([ 'py_enums.cpp', 'py_geometry.cpp', + 'py_helpers.cpp', 'py_main.cpp', ]) diff --git a/src/py/libcamera/py_helpers.cpp b/src/py/libcamera/py_helpers.cpp new file mode 100644 index 00000000..45aecce9 --- /dev/null +++ b/src/py/libcamera/py_helpers.cpp @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + */ + +#include "py_helpers.h" + +#include + +#include +#include +#include + +namespace py = pybind11; + +using namespace libcamera; + +template +static py::object valueOrTuple(const ControlValue &cv) +{ + if (cv.isArray()) { + const T *v = reinterpret_cast(cv.data().data()); + auto t = py::tuple(cv.numElements()); + + for (size_t i = 0; i < cv.numElements(); ++i) + t[i] = v[i]; + + return std::move(t); + } + + return py::cast(cv.get()); +} + +py::object controlValueToPy(const ControlValue &cv) +{ + switch (cv.type()) { + case ControlTypeBool: + return valueOrTuple(cv); + case ControlTypeByte: + return valueOrTuple(cv); + case ControlTypeInteger32: + return valueOrTuple(cv); + case ControlTypeInteger64: + return valueOrTuple(cv); + case ControlTypeFloat: + return valueOrTuple(cv); + case ControlTypeString: + return py::cast(cv.get()); + case ControlTypeRectangle: { + const Rectangle *v = reinterpret_cast(cv.data().data()); + return py::cast(v); + } + case ControlTypeSize: { + const Size *v = reinterpret_cast(cv.data().data()); + return py::cast(v); + } + case ControlTypeNone: + default: + throw std::runtime_error("Unsupported ControlValue type"); + } +} + +template +static ControlValue controlValueMaybeArray(const py::object &ob) +{ + if (py::isinstance(ob) || py::isinstance(ob)) { + std::vector vec = ob.cast>(); + return ControlValue(Span(vec)); + } + + return ControlValue(ob.cast()); +} + +ControlValue pyToControlValue(const py::object &ob, ControlType type) +{ + switch (type) { + case ControlTypeBool: + return ControlValue(ob.cast()); + case ControlTypeByte: + return controlValueMaybeArray(ob); + case ControlTypeInteger32: + return controlValueMaybeArray(ob); + case ControlTypeInteger64: + return controlValueMaybeArray(ob); + case ControlTypeFloat: + return controlValueMaybeArray(ob); + case ControlTypeString: + return ControlValue(ob.cast()); + case ControlTypeRectangle: + return ControlValue(ob.cast()); + case ControlTypeSize: + return ControlValue(ob.cast()); + case ControlTypeNone: + default: + throw std::runtime_error("Control type not implemented"); + } +} diff --git a/src/py/libcamera/py_helpers.h b/src/py/libcamera/py_helpers.h new file mode 100644 index 00000000..cd31e2cc --- /dev/null +++ b/src/py/libcamera/py_helpers.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + */ + +#pragma once + +#include + +#include + +pybind11::object controlValueToPy(const libcamera::ControlValue &cv); +libcamera::ControlValue pyToControlValue(const pybind11::object &ob, libcamera::ControlType type); diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 4b698f77..e652837f 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -21,6 +21,8 @@ #include #include +#include "py_helpers.h" + namespace py = pybind11; using namespace libcamera; @@ -31,87 +33,6 @@ LOG_DEFINE_CATEGORY(Python) } -template -static py::object valueOrTuple(const ControlValue &cv) -{ - if (cv.isArray()) { - const T *v = reinterpret_cast(cv.data().data()); - auto t = py::tuple(cv.numElements()); - - for (size_t i = 0; i < cv.numElements(); ++i) - t[i] = v[i]; - - return std::move(t); - } - - return py::cast(cv.get()); -} - -static py::object controlValueToPy(const ControlValue &cv) -{ - switch (cv.type()) { - case ControlTypeBool: - return valueOrTuple(cv); - case ControlTypeByte: - return valueOrTuple(cv); - case ControlTypeInteger32: - return valueOrTuple(cv); - case ControlTypeInteger64: - return valueOrTuple(cv); - case ControlTypeFloat: - return valueOrTuple(cv); - case ControlTypeString: - return py::cast(cv.get()); - case ControlTypeRectangle: { - const Rectangle *v = reinterpret_cast(cv.data().data()); - return py::cast(v); - } - case ControlTypeSize: { - const Size *v = reinterpret_cast(cv.data().data()); - return py::cast(v); - } - case ControlTypeNone: - default: - throw std::runtime_error("Unsupported ControlValue type"); - } -} - -template -static ControlValue controlValueMaybeArray(const py::object &ob) -{ - if (py::isinstance(ob) || py::isinstance(ob)) { - std::vector vec = ob.cast>(); - return ControlValue(Span(vec)); - } - - return ControlValue(ob.cast()); -} - -static ControlValue pyToControlValue(const py::object &ob, ControlType type) -{ - switch (type) { - case ControlTypeBool: - return ControlValue(ob.cast()); - case ControlTypeByte: - return controlValueMaybeArray(ob); - case ControlTypeInteger32: - return controlValueMaybeArray(ob); - case ControlTypeInteger64: - return controlValueMaybeArray(ob); - case ControlTypeFloat: - return controlValueMaybeArray(ob); - case ControlTypeString: - return ControlValue(ob.cast()); - case ControlTypeRectangle: - return ControlValue(ob.cast()); - case ControlTypeSize: - return ControlValue(ob.cast()); - case ControlTypeNone: - default: - throw std::runtime_error("Control type not implemented"); - } -} - static std::weak_ptr gCameraManager; static int gEventfd; static std::mutex gReqlistMutex; From patchwork Wed Jun 29 07:04:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16410 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 02445BE173 for ; Wed, 29 Jun 2022 07:04:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4B8AE6564C; Wed, 29 Jun 2022 09:04:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486288; bh=fsW6BoODeR+MuGUOJjRgSvniGg9SQ+z5nws1Simatns=; 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=ygZjpgu5lS1I1jofA96CUijoQTl85VCBRk9X7v61289nTClLiiUi1ePbvyxTxT59j rDW0cgBmwx58gXiKDZ2Grd2MFyhSUVSkgSp+NcYrUaAJDAX1JlJ/PTVh8zSgLddo6O f22Sx7rs1lWLvaekeOKBINEZMaC3yu3yrh08mlJMlC+XYVO3Td/VNvVF2QeIMd1cqM 9pZfEhf56lzOupMFy9RnG0Lt1R+a6VjXKy0g2yNmgoXZTuf3ORXZ28l3UP3bxsRNP9 M1HU3DTnWMb8cNc9j9KnO3fHq9Di7f+RU/gktYKnzDAqHSQ2pIrbcmNOg3XN7pOBUw xMaSwuMyr7IWw== 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 202E665635 for ; Wed, 29 Jun 2022 09:04:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sYTLjJnd"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6D73E6D1; Wed, 29 Jun 2022 09:04:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486281; bh=fsW6BoODeR+MuGUOJjRgSvniGg9SQ+z5nws1Simatns=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sYTLjJndSFYYQpxVrJs4ya+gVTpbKoyCRI4u6hvSWVIJuyjQlv/viloDjGqtGlINa p81zF5ssvWlngzN7OFXDZfURzCmvATNR66fxR4ze2diMQmFX1EcSiP4AXhJ00yf/0I 9dHGqb7FOyEn1KqAkPHnUVWXOcFAflPfCixJm54c= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:06 +0300 Message-Id: <20220629070416.17550-5-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 04/14] py: cam.py: Remove todo comment 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" The comment is no longer valid. Signed-off-by: Tomi Valkeinen --- src/py/cam/cam.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 8a82e092..2701d937 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -3,9 +3,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2022, Tomi Valkeinen -# \todo Convert ctx and state dicts to proper classes, and move relevant -# functions to those classes. - from typing import Any import argparse import binascii From patchwork Wed Jun 29 07:04:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16411 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 D2BEBBE173 for ; Wed, 29 Jun 2022 07:04:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7E82D65646; Wed, 29 Jun 2022 09:04:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486290; bh=FDESrgPL0gxNa+Z4XsJ2JAUY5xvJcK9lZM+SU5xx8Xw=; 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=n50Fnvus6MEkQjF34IweDaAQ/W9Pdn29z3S7UnuwfMtGESY89w+Lu1+JBFXQCdy27 U73huNUeEIJX/OyE9CQrNGEQs1n0WlFxKA/0VMEQF/6VrByO1+/GwzFcWsRfTWsFSu BqRCN+SHfso4pH91/cAbVYKHhmI1Dt0ixNzzjPtci4/aR9kKikOfrwTknOa5PjAAw/ kCtWw0DQIoxk7pAvr8+rsCW1Q+xGThB3aKQVaU0y65asYv29DGX/4NQD1H5+x3MSAZ D3YkyhJVwxEftmXfjOOnP11we8UF0qGXdQuC+95Jn7zkEIQC36n0+8hXTcpMS6Zd3h a6B4yZnfHHJtA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 35AFF6563E for ; Wed, 29 Jun 2022 09:04:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Em5CR37v"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 50F7B4A8; Wed, 29 Jun 2022 09:04:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486282; bh=FDESrgPL0gxNa+Z4XsJ2JAUY5xvJcK9lZM+SU5xx8Xw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Em5CR37vyPKx5d9kUYf2PZQ/EesGPA129oDTdEMCrqSyC005gaMsiqwriTuM6vHo3 afOyeNmObERTKaUMsKArA5XQapxOrizkTpkXJ6s5T0zaN1U02vAO7xu6geG2YZS+bN ugId9CchyBLSLtmU8MH8PURsEZV0BB1XIgxU3qUY= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:07 +0300 Message-Id: <20220629070416.17550-6-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 05/14] py: Create PyCameraManager 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" Wrap the CameraManager with a PyCameraManager class and move the related code inside the new class. This helps understanding the life times of the used-to-be global variables, gets rid of static handleRequestCompleted function, and allows us to simplify the binding code as the more complex pieces are inside the class. There should be no user visible functional changes. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/meson.build | 1 + src/py/libcamera/py_camera_manager.cpp | 125 +++++++++++++++++++++++++ src/py/libcamera/py_camera_manager.h | 44 +++++++++ src/py/libcamera/py_main.cpp | 117 +++++------------------ 4 files changed, 193 insertions(+), 94 deletions(-) create mode 100644 src/py/libcamera/py_camera_manager.cpp create mode 100644 src/py/libcamera/py_camera_manager.h diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index 04578bac..ad2a6858 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -13,6 +13,7 @@ pybind11_proj = subproject('pybind11') pybind11_dep = pybind11_proj.get_variable('pybind11_dep') pycamera_sources = files([ + 'py_camera_manager.cpp', 'py_enums.cpp', 'py_geometry.cpp', 'py_helpers.cpp', diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp new file mode 100644 index 00000000..ad47271d --- /dev/null +++ b/src/py/libcamera/py_camera_manager.cpp @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + */ + +#include "py_camera_manager.h" + +#include +#include +#include +#include + +#include "py_main.h" + +namespace py = pybind11; + +using namespace libcamera; + +PyCameraManager::PyCameraManager() +{ + LOG(Python, Debug) << "PyCameraManager()"; + + cameraManager_ = std::make_unique(); + + int fd = eventfd(0, 0); + if (fd == -1) + throw std::system_error(errno, std::generic_category(), + "Failed to create eventfd"); + + eventFd_ = fd; + + int ret = cameraManager_->start(); + if (ret) { + close(fd); + eventFd_ = -1; + throw std::system_error(-ret, std::generic_category(), + "Failed to start CameraManager"); + } +} + +PyCameraManager::~PyCameraManager() +{ + LOG(Python, Debug) << "~PyCameraManager()"; + + if (eventFd_ != -1) { + close(eventFd_); + eventFd_ = -1; + } +} + +py::list PyCameraManager::cameras() +{ + /* + * Create a list of Cameras, where each camera has a keep-alive to + * CameraManager. + */ + py::list l; + + for (auto &camera : cameraManager_->cameras()) { + py::object py_cm = py::cast(this); + py::object py_cam = py::cast(camera); + py::detail::keep_alive_impl(py_cam, py_cm); + l.append(py_cam); + } + + return l; +} + +std::vector PyCameraManager::getReadyRequests() +{ + readFd(); + + std::vector ret; + + for (Request *request : getCompletedRequests()) { + py::object o = py::cast(request); + /* Decrease the ref increased in Camera.queue_request() */ + o.dec_ref(); + ret.push_back(o); + } + + return ret; +} + +/* Note: Called from another thread */ +void PyCameraManager::handleRequestCompleted(Request *req) +{ + pushRequest(req); + writeFd(); +} + +void PyCameraManager::writeFd() +{ + uint64_t v = 1; + + size_t s = write(eventFd_, &v, 8); + /* + * We should never fail, and have no simple means to manage the error, + * so let's log a fatal error. + */ + if (s != 8) + LOG(Python, Fatal) << "Unable to write to eventfd"; +} + +void PyCameraManager::readFd() +{ + uint8_t buf[8]; + + if (read(eventFd_, buf, 8) != 8) + throw std::system_error(errno, std::generic_category()); +} + +void PyCameraManager::pushRequest(Request *req) +{ + std::lock_guard guard(completedRequestsMutex_); + completedRequests_.push_back(req); +} + +std::vector PyCameraManager::getCompletedRequests() +{ + std::vector v; + std::lock_guard guard(completedRequestsMutex_); + swap(v, completedRequests_); + return v; +} diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h new file mode 100644 index 00000000..9c15f814 --- /dev/null +++ b/src/py/libcamera/py_camera_manager.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + */ + +#pragma once + +#include + +#include + +#include + +using namespace libcamera; + +class PyCameraManager +{ +public: + PyCameraManager(); + ~PyCameraManager(); + + pybind11::list cameras(); + std::shared_ptr get(const std::string &name) { return cameraManager_->get(name); } + + static const std::string &version() { return CameraManager::version(); } + + int eventFd() const { return eventFd_; } + + std::vector getReadyRequests(); + + void handleRequestCompleted(Request *req); + +private: + std::unique_ptr cameraManager_; + + int eventFd_ = -1; + std::mutex completedRequestsMutex_; + std::vector completedRequests_; + + void writeFd(); + void readFd(); + void pushRequest(Request *req); + std::vector getCompletedRequests(); +}; diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index e652837f..e7d078b5 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -7,10 +7,7 @@ #include "py_main.h" -#include #include -#include -#include #include @@ -21,6 +18,7 @@ #include #include +#include "py_camera_manager.h" #include "py_helpers.h" namespace py = pybind11; @@ -33,27 +31,11 @@ LOG_DEFINE_CATEGORY(Python) } -static std::weak_ptr gCameraManager; -static int gEventfd; -static std::mutex gReqlistMutex; -static std::vector gReqList; - -static void handleRequestCompleted(Request *req) -{ - { - std::lock_guard guard(gReqlistMutex); - gReqList.push_back(req); - } - - uint64_t v = 1; - size_t s = write(gEventfd, &v, 8); - /* - * We should never fail, and have no simple means to manage the error, - * so let's log a fatal error. - */ - if (s != 8) - LOG(Python, Fatal) << "Unable to write to eventfd"; -} +/* + * Note: global C++ destructors can be ran on this before the py module is + * destructed. + */ +static std::weak_ptr gCameraManager; void init_py_enums(py::module &m); void init_py_controls_generated(py::module &m); @@ -76,7 +58,7 @@ PYBIND11_MODULE(_libcamera, m) * https://pybind11.readthedocs.io/en/latest/advanced/misc.html#avoiding-c-types-in-docstrings */ - auto pyCameraManager = py::class_(m, "CameraManager"); + auto pyCameraManager = py::class_(m, "CameraManager"); auto pyCamera = py::class_(m, "Camera"); auto pyCameraConfiguration = py::class_(m, "CameraConfiguration"); auto pyCameraConfigurationStatus = py::enum_(pyCameraConfiguration, "Status"); @@ -110,78 +92,22 @@ PYBIND11_MODULE(_libcamera, m) /* Classes */ pyCameraManager .def_static("singleton", []() { - std::shared_ptr cm = gCameraManager.lock(); - if (cm) - return cm; - - int fd = eventfd(0, 0); - if (fd == -1) - throw std::system_error(errno, std::generic_category(), - "Failed to create eventfd"); - - cm = std::shared_ptr(new CameraManager, [](auto p) { - close(gEventfd); - gEventfd = -1; - delete p; - }); - - gEventfd = fd; - gCameraManager = cm; - - int ret = cm->start(); - if (ret) - throw std::system_error(-ret, std::generic_category(), - "Failed to start CameraManager"); - - return cm; - }) - - .def_property_readonly("version", &CameraManager::version) + std::shared_ptr cm = gCameraManager.lock(); - .def_property_readonly("event_fd", [](CameraManager &) { - return gEventfd; - }) - - .def("get_ready_requests", [](CameraManager &) { - uint8_t buf[8]; - - if (read(gEventfd, buf, 8) != 8) - throw std::system_error(errno, std::generic_category()); - - std::vector v; - - { - std::lock_guard guard(gReqlistMutex); - swap(v, gReqList); + if (!cm) { + cm = std::make_shared(); + gCameraManager = cm; } - std::vector ret; - - for (Request *req : v) { - py::object o = py::cast(req); - /* Decrease the ref increased in Camera.queue_request() */ - o.dec_ref(); - ret.push_back(o); - } - - return ret; + return cm; }) - .def("get", py::overload_cast(&CameraManager::get), py::keep_alive<0, 1>()) - - /* Create a list of Cameras, where each camera has a keep-alive to CameraManager */ - .def_property_readonly("cameras", [](CameraManager &self) { - py::list l; - - for (auto &c : self.cameras()) { - py::object py_cm = py::cast(self); - py::object py_cam = py::cast(c); - py::detail::keep_alive_impl(py_cam, py_cm); - l.append(py_cam); - } + .def_property_readonly("version", &PyCameraManager::version) + .def("get", &PyCameraManager::get, py::keep_alive<0, 1>()) + .def_property_readonly("cameras", &PyCameraManager::cameras) - return l; - }); + .def_property_readonly("event_fd", &PyCameraManager::eventFd) + .def("get_ready_requests", &PyCameraManager::getReadyRequests); pyCamera .def_property_readonly("id", &Camera::id) @@ -191,7 +117,10 @@ PYBIND11_MODULE(_libcamera, m) const std::unordered_map &controls) { /* \todo What happens if someone calls start() multiple times? */ - self.requestCompleted.connect(handleRequestCompleted); + auto cm = gCameraManager.lock(); + ASSERT(cm); + + self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); ControlList controlList(self.controls()); @@ -202,7 +131,7 @@ PYBIND11_MODULE(_libcamera, m) int ret = self.start(&controlList); if (ret) { - self.requestCompleted.disconnect(handleRequestCompleted); + self.requestCompleted.disconnect(); return ret; } @@ -214,7 +143,7 @@ PYBIND11_MODULE(_libcamera, m) if (ret) return ret; - self.requestCompleted.disconnect(handleRequestCompleted); + self.requestCompleted.disconnect(); return 0; }) From patchwork Wed Jun 29 07:04:08 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16412 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 1E6D9BE173 for ; Wed, 29 Jun 2022 07:04:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BDC2165653; Wed, 29 Jun 2022 09:04:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486292; bh=eCXWxXuGWl5r0HMuik1XEkAxHOfVyvYcsHvaM+5SdA8=; 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=ZqsHrosGcd1hByBFUH7N2WT0Ii8w2NvNn5y9Js5cNyfo/XW14gLue/3FQURkEg0JR bPLEEkgSNdVcVgZRBBCzCXxjY6JkW3E6BNuryRhejmUZ5z4GV1YIJYlf+Zjd3lV/n7 eJKMY5Hxis/ogMbJ9y25Iu9NmQVmqjcbo7+Y+Bf08TaV+6XI9lYazDr7drqVyKB65N QGOHdXNkOl5wRHbbXya4nnT/NDxytHXSEB7c7pDpm/PCfVGSS7Sq2zdi+hJMNUkfp9 a6682zV381nvJvNTxhK5QDXHGpvVv4hMxj3t+psZYnx/eycFTsy5hDK7kqP1JJp6Hd FVIO6khn9C5Qg== 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 B813F65637 for ; Wed, 29 Jun 2022 09:04:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bE4Erwcf"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 00A786D1; Wed, 29 Jun 2022 09:04:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486283; bh=eCXWxXuGWl5r0HMuik1XEkAxHOfVyvYcsHvaM+5SdA8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bE4ErwcfkE4emc2rUBusoqD4ImIZFxnLLDscJEapXntVV/aOUpinwRi+Izh2i+1AA xzaFKHOBAZtDo4s6BnsMAgO1Hy/MIYlYbdksJxp2hL8dhBMEg16gYey2wi129yzzhW jzdm3Z7Ot8/qGmfSG0bLyhQxRLxrVStI8v2CbHGA= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:08 +0300 Message-Id: <20220629070416.17550-7-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 06/14] py: Use UniqueFD 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" Use UniqueFD to automate the eventfd lifetime management. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 16 ++++------------ src/py/libcamera/py_camera_manager.h | 4 ++-- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index ad47271d..51a890c8 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -27,25 +27,17 @@ PyCameraManager::PyCameraManager() throw std::system_error(errno, std::generic_category(), "Failed to create eventfd"); - eventFd_ = fd; + eventFd_ = UniqueFD(fd); int ret = cameraManager_->start(); - if (ret) { - close(fd); - eventFd_ = -1; + if (ret) throw std::system_error(-ret, std::generic_category(), "Failed to start CameraManager"); - } } PyCameraManager::~PyCameraManager() { LOG(Python, Debug) << "~PyCameraManager()"; - - if (eventFd_ != -1) { - close(eventFd_); - eventFd_ = -1; - } } py::list PyCameraManager::cameras() @@ -93,7 +85,7 @@ void PyCameraManager::writeFd() { uint64_t v = 1; - size_t s = write(eventFd_, &v, 8); + size_t s = write(eventFd_.get(), &v, 8); /* * We should never fail, and have no simple means to manage the error, * so let's log a fatal error. @@ -106,7 +98,7 @@ void PyCameraManager::readFd() { uint8_t buf[8]; - if (read(eventFd_, buf, 8) != 8) + if (read(eventFd_.get(), buf, 8) != 8) throw std::system_error(errno, std::generic_category()); } diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 9c15f814..710163e8 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -24,7 +24,7 @@ public: static const std::string &version() { return CameraManager::version(); } - int eventFd() const { return eventFd_; } + int eventFd() const { return eventFd_.get(); } std::vector getReadyRequests(); @@ -33,7 +33,7 @@ public: private: std::unique_ptr cameraManager_; - int eventFd_ = -1; + UniqueFD eventFd_; std::mutex completedRequestsMutex_; std::vector completedRequests_; From patchwork Wed Jun 29 07:04:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16413 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 5EDCBBE173 for ; Wed, 29 Jun 2022 07:04:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F095565645; Wed, 29 Jun 2022 09:04:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486294; bh=4jssbuxUq5C1GVY59JXBlIIcwKau4KEUhfDWEvwAuHc=; 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=D0/hisNpM7dd3Bm8mvJKMX/QSM4pMjhRD9PjyVY/S/xWACufPERYZJP09tkETPV67 lNyaXutDMlwEmPt2jh51XveanvZ6mXiYa3Gv2lC5OMt2veOr0rYAd6aZvg4CcO31v9 ObzD3iY7IiAF1PihmwpFajUIpA7uGmwTd/KJOwBtMFTpDkOvoJYmDzNBlvEXBjxkZQ C6VVXr8yPbtPTVTamATFOH91r7eSiBOMJgKfkyjb9hezAKJanjoAGmmRZTukq8gunf jQuLDHC/yjh3fHSZKwQq9NkJY4ro7cdoQPYOMA2NQzfyANcetnLAVQBc/aLRslK5e8 rWfyyVPuMAkDQ== 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 1F95565641 for ; Wed, 29 Jun 2022 09:04:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dZ9Cgfws"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7D7AA4A8; Wed, 29 Jun 2022 09:04:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486283; bh=4jssbuxUq5C1GVY59JXBlIIcwKau4KEUhfDWEvwAuHc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dZ9CgfwsABD0N+ObVGsmZ5IhmcgBCoFb4SqnwM6jlcDePEm42Jz+BH+VQ3q6Y6Vou xT5ri3lGiScPLB5e5fHIQroEuL6zZ0/Mb7wLrxgmbHYJit2RSQcZnYTNZKGc1VOa+L EyQfJvW8WIwqT5laE9mb6XAIbW0mLEPaKddfUkFQ= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:09 +0300 Message-Id: <20220629070416.17550-8-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 07/14] py: Set EFD_CLOEXEC on eventfd to avoid fd leaking 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" Set EFD_CLOEXEC on eventfd to avoid fd leaking. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 51a890c8..3d422c9e 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -22,7 +22,7 @@ PyCameraManager::PyCameraManager() cameraManager_ = std::make_unique(); - int fd = eventfd(0, 0); + int fd = eventfd(0, EFD_CLOEXEC); if (fd == -1) throw std::system_error(errno, std::generic_category(), "Failed to create eventfd"); From patchwork Wed Jun 29 07:04:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16414 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 5B3C0BE173 for ; Wed, 29 Jun 2022 07:04:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DF0596564D; Wed, 29 Jun 2022 09:04:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486295; bh=P4csJrs78yKiXsTsTozO9b8oqqTGp+igSSWlBlKQZyo=; 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=UmTpezF5HVg5UTCl2jv9NVO1G2Dxuc/gYqs3tDxGtRoj36Fo0kLQnAXdJBdOZy2PC XGbTc69jHN51EEeS3JZN0VNi2EATDWBuBgGRvTDmtKLI8O3ZNSJu0HcWzVn6VVY/iG 55Hvv4c2uTRRGItkDHbEsU9EdKe9+FOqnL/Uv/zNfwYvVasuGrEjjrA04gk614TV9R wWF8Oa4WaN31S5T4+oGjo1iQHibA5vA66rzdCN0NIhO3A61sU6kco6BqRmPS/cEgXi bucDa1VvE687ZHHTWMOXBYcwZPWL4pwL1d0z7DgjNfFRH+mi1SsnXVFoIVTbtmET9a 1ybw2TbJ8gLhg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 935D965642 for ; Wed, 29 Jun 2022 09:04:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ogt2/5L8"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0C98C6D1; Wed, 29 Jun 2022 09:04:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486284; bh=P4csJrs78yKiXsTsTozO9b8oqqTGp+igSSWlBlKQZyo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ogt2/5L8WRctwbAsV66J/1pC3rh97iw32Sn1KfBlxjCQ6XHGF22D0qx/yLoe0IJl3 i9cdwWWlLCm2zuS4JXgUgE/QNdn528HljUyO2WpW3e9bXGR4Atqy28sDp5YZtykZ72 OODcyJvh1toIyFdYfThyQlKs1eKJoHJeKOxty0qI= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:10 +0300 Message-Id: <20220629070416.17550-9-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 08/14] py: Use libcamera's Mutex classes 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" Use libcamera's Mutex and MutexLocker instead of the std versions to get thread safety annotations. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 4 ++-- src/py/libcamera/py_camera_manager.h | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 3d422c9e..5600f661 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -104,14 +104,14 @@ void PyCameraManager::readFd() void PyCameraManager::pushRequest(Request *req) { - std::lock_guard guard(completedRequestsMutex_); + MutexLocker guard(completedRequestsMutex_); completedRequests_.push_back(req); } std::vector PyCameraManager::getCompletedRequests() { std::vector v; - std::lock_guard guard(completedRequestsMutex_); + MutexLocker guard(completedRequestsMutex_); swap(v, completedRequests_); return v; } diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 710163e8..56bea13d 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -5,7 +5,7 @@ #pragma once -#include +#include #include @@ -34,8 +34,9 @@ private: std::unique_ptr cameraManager_; UniqueFD eventFd_; - std::mutex completedRequestsMutex_; - std::vector completedRequests_; + libcamera::Mutex completedRequestsMutex_; + std::vector completedRequests_ + LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_); void writeFd(); void readFd(); From patchwork Wed Jun 29 07:04:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16415 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 2E85BBE173 for ; Wed, 29 Jun 2022 07:04:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CD82065651; Wed, 29 Jun 2022 09:04:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486295; bh=ZqYHwWRhUBynORUrnYf3eXOKW55fHL0ZwI7nz+RVte0=; 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=Zv63ghXbfBBy5cGy13GQE3yTKo4VyPiYQmb/5sVDpBQ2h4FJDbUBJIR3XJhEnmCLy mFDLnYHARhcIKcy9PAHUaBgiDl2VdD8SDw+nJPlSz78GRLoV4a0iV5r/3xHQoVqAAz 3Nz5wvOYDryf+eHnDIlHLjwd/PKSEV5g9yDaJgeJlXlDPYQKV+B76mmNvD2M6ExxtE fBF9eoDhNKc61mPy9+D632EojCc3xwdDvnWIOuIdaX5FF92JeAim5rowqzZ9jGACzb 7Vpf1ufZ9UxDNOpLVm/G/7TXPOzEoiGIB6lRAW4yexdY4vKpLpQwPWaxoVtfrwEwsI gjWuhIZND7EaQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8830A65644 for ; Wed, 29 Jun 2022 09:04:45 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Rzd6Pkwp"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A49006F0; Wed, 29 Jun 2022 09:04:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486285; bh=ZqYHwWRhUBynORUrnYf3eXOKW55fHL0ZwI7nz+RVte0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rzd6PkwpfWxATPzSxDOOZKNhhonkWypPo6EBunNXptZ4lw/WWhP1kVl4Qg2anubwn 7gZp125cLUgxRF+Yw3zscVKCDsy7yui1eyS7TEg5A22If6JIz7rk3Xg8RCoodBvMmD RHsP3oy3dx5skPxabC3R5RtYYGyRyhNKb5oAIMgc= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:11 +0300 Message-Id: <20220629070416.17550-10-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 09/14] 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, mimicing 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 --- 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 | 62 ++++++++++++---- test/py/unittests.py | 76 +++++++------------- 6 files changed, 93 insertions(+), 118 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 2701d937..6b6b678b 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 2b81bb65..e88a4737 100755 --- a/src/py/examples/simple-cam.py +++ b/src/py/examples/simple-cam.py @@ -258,12 +258,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') # -------------------------------------------------------------------- @@ -288,15 +283,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 a6a9b33e..07d12dae 100755 --- a/src/py/examples/simple-capture.py +++ b/src/py/examples/simple-capture.py @@ -42,8 +42,7 @@ def main(): # Acquire the camera for our use - ret = cam.acquire() - assert ret == 0 + cam.acquire() # Configure the camera @@ -59,8 +58,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}') @@ -82,15 +80,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 @@ -100,8 +96,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, @@ -147,13 +142,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 fe78a2dd..ef3f87d1 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 @@ -144,8 +139,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 @@ -176,8 +170,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 e7d078b5..1ef1384e 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -111,8 +111,19 @@ 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(); + /* \todo Should we ignore the error? */ + 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? */ @@ -132,20 +143,20 @@ 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; + /* \todo Should we just ignore the error? */ + if (ret) + throw std::system_error(-ret, std::generic_category(), + "Failed to start camera"); }) .def("__str__", [](Camera &self) { @@ -154,9 +165,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); @@ -169,10 +191,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) { @@ -250,7 +273,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 queue request"); + 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) { @@ -327,7 +356,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 9adc4337..b90b5fec 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() @@ -223,8 +207,7 @@ class SimpleCaptureMethods(CameraTesterBase): reqs = None gc.collect() - ret = cam.stop() - self.assertZero(ret) + cam.stop() def test_select(self): cm = self.cm @@ -238,14 +221,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)) @@ -255,19 +237,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() @@ -296,8 +275,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 Wed Jun 29 07:04:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16416 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 E72F8BE173 for ; Wed, 29 Jun 2022 07:04:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9867D60552; Wed, 29 Jun 2022 09:04:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486297; bh=/Yv54VY4nsytbaSDp75PRYRZjCs0hmsPbp/8nIPGblY=; 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=NXXiXZEp6YHjYyUPJ3cAhw25rcZYegrnpfGTur/RU0X42iGScv5vp5EBI1dlULRsn 9SxMcLHqnQBZvjz0+/7LhGtfH/1MoQqCOmQH9E7SbVyN83lQ8c04lj5p00V5rS2/9P H2NFMw2fbwMS4hbqzq8QlTZQLAf0UixbkoHGgWsKe/qhCLG4A/Dp21fPQOkyrZ3A+r upirUUQO17Ha53mMjUtrsmRwKGi4ksrZnW0ZLv+zcy1zWuu+X/I06bIp/agtWE4/un TQhbqOkf+QSrx3KyxdosQFfj8xDVonZctJHpWmzUwnhSS2XH+yG0txW7/UZDCs7mH7 mQF80LvtlL9dQ== 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 1B29D60552 for ; Wed, 29 Jun 2022 09:04:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="J/CEFeN0"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 790A84A8; Wed, 29 Jun 2022 09:04:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486285; bh=/Yv54VY4nsytbaSDp75PRYRZjCs0hmsPbp/8nIPGblY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J/CEFeN0OjR5rsJ5XTLiDpktls9pIWxJXWXHXZrpX6IJFNqnPNC5ZjSmE1qbGOOun pLmQ/fpcU4KIML47/k+tL+bUVaZQWoYU94Xt6+3TCIE/aPIf6SKoC2bZsryyn+l9U7 hx+W05/eeY/Pb/gsgJ0GaTEwfiD6D5xmrqchosZo= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:12 +0300 Message-Id: <20220629070416.17550-11-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 10/14] py: Switch to non-blocking eventfd 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" Blocking wait can be easily implemented on top in Python, so rather than supporting only blocking reads, or supporting both non-blocking and blocking reads, let's support only non-blocking reads. Signed-off-by: Tomi Valkeinen --- src/py/examples/simple-cam.py | 5 +++-- src/py/examples/simple-capture.py | 12 +++++++++-- src/py/examples/simple-continuous-capture.py | 5 +++-- src/py/libcamera/py_camera_manager.cpp | 22 +++++++++++++------- src/py/libcamera/py_camera_manager.h | 2 +- test/py/unittests.py | 7 +++++++ 6 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/py/examples/simple-cam.py b/src/py/examples/simple-cam.py index e88a4737..1cd1019d 100755 --- a/src/py/examples/simple-cam.py +++ b/src/py/examples/simple-cam.py @@ -19,8 +19,9 @@ TIMEOUT_SEC = 3 def handle_camera_event(cm): - # cm.get_ready_requests() will not block here, as we know there is an event - # to read. + # 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. reqs = cm.get_ready_requests() diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py index 07d12dae..4b85408f 100755 --- a/src/py/examples/simple-capture.py +++ b/src/py/examples/simple-capture.py @@ -14,6 +14,7 @@ import argparse import libcamera as libcam +import selectors import sys # Number of frames to capture @@ -102,11 +103,18 @@ def main(): # The main loop. Wait for the queued Requests to complete, process them, # and re-queue them again. + sel = selectors.DefaultSelector() + sel.register(cm.event_fd, selectors.EVENT_READ) + while frames_done < TOTAL_FRAMES: - # cm.get_ready_requests() blocks until there is an event and returns - # all the ready requests. Here we should almost always get a single + # 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. + events = sel.select() + if not events: + continue + reqs = cm.get_ready_requests() for req in reqs: diff --git a/src/py/examples/simple-continuous-capture.py b/src/py/examples/simple-continuous-capture.py index ef3f87d1..e1cb931e 100755 --- a/src/py/examples/simple-continuous-capture.py +++ b/src/py/examples/simple-continuous-capture.py @@ -83,8 +83,9 @@ class CaptureContext: camera_contexts: list[CameraCaptureContext] = [] def handle_camera_event(self): - # cm.get_ready_requests() will not block here, as we know there is an event - # to read. + # 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. reqs = self.cm.get_ready_requests() diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 5600f661..3dd8668e 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -22,7 +22,7 @@ PyCameraManager::PyCameraManager() cameraManager_ = std::make_unique(); - int fd = eventfd(0, EFD_CLOEXEC); + int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (fd == -1) throw std::system_error(errno, std::generic_category(), "Failed to create eventfd"); @@ -60,18 +60,24 @@ py::list PyCameraManager::cameras() std::vector PyCameraManager::getReadyRequests() { - readFd(); + int ret = readFd(); - std::vector ret; + if (ret == EAGAIN) + return std::vector(); + + if (ret != 0) + throw std::system_error(ret, std::generic_category()); + + 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(); - ret.push_back(o); + py_reqs.push_back(o); } - return ret; + return py_reqs; } /* Note: Called from another thread */ @@ -94,12 +100,14 @@ void PyCameraManager::writeFd() LOG(Python, Fatal) << "Unable to write to eventfd"; } -void PyCameraManager::readFd() +int PyCameraManager::readFd() { uint8_t buf[8]; if (read(eventFd_.get(), buf, 8) != 8) - throw std::system_error(errno, std::generic_category()); + return errno; + + return 0; } void PyCameraManager::pushRequest(Request *req) diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 56bea13d..3525057d 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -39,7 +39,7 @@ private: LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_); void writeFd(); - void readFd(); + int readFd(); void pushRequest(Request *req); std::vector getCompletedRequests(); }; diff --git a/test/py/unittests.py b/test/py/unittests.py index b90b5fec..794e46be 100755 --- a/test/py/unittests.py +++ b/test/py/unittests.py @@ -191,9 +191,16 @@ class SimpleCaptureMethods(CameraTesterBase): reqs = None gc.collect() + sel = selectors.DefaultSelector() + sel.register(cm.event_fd, selectors.EVENT_READ) + reqs = [] while True: + events = sel.select() + if not events: + continue + ready_reqs = cm.get_ready_requests() reqs += ready_reqs From patchwork Wed Jun 29 07:04:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16417 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 8F546BE173 for ; Wed, 29 Jun 2022 07:04:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 247B26565F; Wed, 29 Jun 2022 09:04:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486298; bh=Bq8k8rOb0O7mkL+i6snxoUc9XlhC/lVk2HbitiQO87o=; 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=KjQhtcvFrysNDoQOXGrdSx3/767GS5u8uEsP66T3mp+KCflKjDe1l4/sfgQCvDkk8 7TrWsGYJOraXGqQIiezfpEG8QR9V4EPpJLe5uY6/u2EWGuaJXBuW4hiwkgjMub83Gf LRZPLvAdipTbYdA/7bn+EpHS7RX/AG4i/uPpAXPGyz+otEaNaqSJLfgQ25/G5y61TP aMZNk/x5CjBLJZm7cxSkvRXZtR69vFJSLC55yDNdgXv2yaDre/q1W1iu7O/I1UmLUw c8Xam5xMhEr9/zX48s3FoWypK8L0/idEViGNKM3pKwZUZbNcBjEmIhX/wjTo8vazYK QR2hx++HNkbQA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B60E96563A for ; Wed, 29 Jun 2022 09:04:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Tt6OapQP"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0BAE76D1; Wed, 29 Jun 2022 09:04:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486286; bh=Bq8k8rOb0O7mkL+i6snxoUc9XlhC/lVk2HbitiQO87o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Tt6OapQP0crZ850xpZkMpjqBpwqVb1YOKHdQNM+U7qz0NQWPFtFyOkCvcLDZphm+u otUNiqJ/LGj0LB9Wk1FV7+G2GNddk4IdWUOzCm6sUy6dZsFO1sCWeJXT2TUxMy1NHg AzMUAMYlaEMzUfWq/hFr3vNe6B++vt8qKvGhGY/8= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:13 +0300 Message-Id: <20220629070416.17550-12-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 11/14] 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 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 as follows: The user sets callbacks to the CameraManager or Cameras (e.g. Camera.buffer_completed). When the event_fd informs of an event, the user must call CameraManager.dispatch_events() which gets the queued events and calls the relevant callbacks for each queued event. Additionally there is CameraManager.discard_events() if the user does not want to process the events but wants to clear the event queue (e.g. after stopping the cameras or when exiting the app). I'm not very happy with this patch. It works fine, but there's a lot of repetition of almost-the-same code. Perhaps some template magics might reduce the repetition, but I also fear that it can easily make the code more difficult to read. TODO: Documentation. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 250 +++++++++++++++++++++++-- src/py/libcamera/py_camera_manager.h | 64 ++++++- src/py/libcamera/py_main.cpp | 89 ++++++++- 3 files changed, 381 insertions(+), 22 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 3dd8668e..599a9f7e 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -58,6 +58,7 @@ py::list PyCameraManager::cameras() return l; } +/* DEPRECATED */ std::vector PyCameraManager::getReadyRequests() { int ret = readFd(); @@ -70,21 +71,234 @@ 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()) { + switch (ev.type_) { + case CameraEvent::Type::RequestCompleted: { + py::object o = py::cast(ev.request_); + /* Decrease the ref increased in Camera.queue_request() */ + o.dec_ref(); + py_reqs.push_back(o); + } + default: + /* ignore */ + break; + } } return py_reqs; } /* Note: Called from another thread */ -void PyCameraManager::handleRequestCompleted(Request *req) +void PyCameraManager::handleBufferCompleted(std::shared_ptr cam, Request *req, FrameBuffer *fb) { - pushRequest(req); - writeFd(); + CameraEvent ev(CameraEvent::Type::BufferCompleted, cam); + ev.request_ = req; + ev.fb_ = fb; + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleRequestCompleted(std::shared_ptr cam, Request *req) +{ + CameraEvent ev(CameraEvent::Type::RequestCompleted, cam); + ev.request_ = req; + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleDisconnected(std::shared_ptr cam) +{ + CameraEvent ev(CameraEvent::Type::Disconnect, cam); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleCameraAdded(std::shared_ptr cam) +{ + CameraEvent ev(CameraEvent::Type::CameraAdded, cam); + + pushEvent(ev); +} + +/* Note: Called from another thread */ +void PyCameraManager::handleCameraRemoved(std::shared_ptr cam) +{ + CameraEvent ev(CameraEvent::Type::CameraRemoved, cam); + + pushEvent(ev); +} + +void PyCameraManager::dispatchEvents() +{ + int ret = readFd(); + + if (ret == EAGAIN) { + LOG(Python, Debug) << "No events to dispatch"; + return; + } + + if (ret != 0) + throw std::system_error(ret, std::generic_category()); + + std::vector events = getEvents(); + + LOG(Python, Debug) << "Dispatch " << events.size() << " events"; + + for (const auto &event : events) { + std::shared_ptr camera = event.camera_; + + switch (event.type_) { + case CameraEvent::Type::CameraAdded: { + if (cameraAddedHandler_) + cameraAddedHandler_(camera); + + break; + } + case CameraEvent::Type::CameraRemoved: { + if (cameraRemovedHandler_) + cameraRemovedHandler_(camera); + + break; + } + case CameraEvent::Type::BufferCompleted: { + auto cb = getBufferCompleted(camera.get()); + if (cb) + cb(camera, event.request_, event.fb_); + + break; + } + case CameraEvent::Type::RequestCompleted: { + auto cb = getRequestCompleted(camera.get()); + if (cb) + cb(camera, event.request_); + + /* Decrease the ref increased in Camera.queue_request() */ + py::object o = py::cast(event.request_); + o.dec_ref(); + + break; + } + case CameraEvent::Type::Disconnect: { + auto cb = getDisconnected(camera.get()); + if (cb) + cb(camera); + + break; + } + default: + ASSERT(false); + } + } +} + +void PyCameraManager::discardEvents() +{ + int ret = readFd(); + + if (ret == EAGAIN) + return; + + if (ret != 0) + throw std::system_error(ret, std::generic_category()); + + std::vector v = getEvents(); + + LOG(Python, Debug) << "Discard " << v.size() << " events"; + + for (const auto &ev : v) { + if (ev.type_ != CameraEvent::Type::RequestCompleted) + continue; + + /* Decrease the ref increased in Camera.queue_request() */ + py::object o = py::cast(ev.request_); + o.dec_ref(); + } +} + +std::function)> PyCameraManager::getCameraAdded() const +{ + return cameraAddedHandler_; +} + +void PyCameraManager::setCameraAdded(std::function)> func) +{ + if (cameraAddedHandler_) + cameraManager_->cameraAdded.disconnect(); + + cameraAddedHandler_ = func; + + if (func) + cameraManager_->cameraAdded.connect(this, &PyCameraManager::handleCameraAdded); +} + +std::function)> PyCameraManager::getCameraRemoved() const +{ + return cameraRemovedHandler_; +} + +void PyCameraManager::setCameraRemoved(std::function)> func) +{ + if (cameraRemovedHandler_) + cameraManager_->cameraRemoved.disconnect(); + + cameraRemovedHandler_ = func; + + if (func) + cameraManager_->cameraRemoved.connect(this, &PyCameraManager::handleCameraRemoved); +} + +std::function, Request *)> PyCameraManager::getRequestCompleted(Camera *cam) +{ + if (auto it = cameraRequestCompletedHandlers_.find(cam); + it != cameraRequestCompletedHandlers_.end()) + return it->second; + + return nullptr; +} + +void PyCameraManager::setRequestCompleted(Camera *cam, std::function, Request *)> func) +{ + if (func) + cameraRequestCompletedHandlers_[cam] = func; + else + cameraRequestCompletedHandlers_.erase(cam); +} + +std::function, Request *, FrameBuffer *)> PyCameraManager::getBufferCompleted(Camera *cam) +{ + if (auto it = cameraBufferCompletedHandlers_.find(cam); + it != cameraBufferCompletedHandlers_.end()) + return it->second; + + return nullptr; +} + +void PyCameraManager::setBufferCompleted(Camera *cam, std::function, Request *, FrameBuffer *)> func) +{ + if (func) + cameraBufferCompletedHandlers_[cam] = func; + else + cameraBufferCompletedHandlers_.erase(cam); +} + +std::function)> PyCameraManager::getDisconnected(Camera *cam) +{ + if (auto it = cameraDisconnectHandlers_.find(cam); + it != cameraDisconnectHandlers_.end()) + return it->second; + + return nullptr; +} + +void PyCameraManager::setDisconnected(Camera *cam, std::function)> func) +{ + if (func) + cameraDisconnectHandlers_[cam] = func; + else + cameraDisconnectHandlers_.erase(cam); } void PyCameraManager::writeFd() @@ -110,16 +324,22 @@ int PyCameraManager::readFd() return 0; } -void PyCameraManager::pushRequest(Request *req) +void PyCameraManager::pushEvent(const CameraEvent &ev) { - MutexLocker guard(completedRequestsMutex_); - completedRequests_.push_back(req); + MutexLocker guard(cameraEventsMutex_); + cameraEvents_.push_back(ev); + + writeFd(); + + LOG(Python, Debug) << "Queued events: " << cameraEvents_.size(); } -std::vector PyCameraManager::getCompletedRequests() +std::vector PyCameraManager::getEvents() { - std::vector v; - MutexLocker guard(completedRequestsMutex_); - swap(v, completedRequests_); + std::vector v; + + MutexLocker guard(cameraEventsMutex_); + swap(v, cameraEvents_); + return v; } diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 3525057d..aa51a6bc 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -30,16 +30,70 @@ public: void handleRequestCompleted(Request *req); + 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 dispatchEvents(); + void discardEvents(); + + std::function)> getCameraAdded() const; + void setCameraAdded(std::function)> func); + + std::function)> getCameraRemoved() const; + void setCameraRemoved(std::function)> func); + + std::function, Request *)> getRequestCompleted(Camera *cam); + void setRequestCompleted(Camera *cam, std::function, Request *)> func); + + std::function, Request *, FrameBuffer *)> getBufferCompleted(Camera *cam); + void setBufferCompleted(Camera *cam, std::function, Request *, FrameBuffer *)> func); + + std::function)> getDisconnected(Camera *cam); + void setDisconnected(Camera *cam, std::function)> func); + private: + struct CameraEvent { + enum class Type { + Undefined = 0, + CameraAdded, + CameraRemoved, + Disconnect, + RequestCompleted, + BufferCompleted, + }; + + CameraEvent(Type type, std::shared_ptr camera) + : type_(type), camera_(camera) + { + } + + Type type_; + + std::shared_ptr camera_; + + Request *request_ = nullptr; + FrameBuffer *fb_ = nullptr; + }; + std::unique_ptr cameraManager_; UniqueFD eventFd_; - libcamera::Mutex completedRequestsMutex_; - std::vector completedRequests_ - LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_); + libcamera::Mutex cameraEventsMutex_; + std::vector cameraEvents_ + LIBCAMERA_TSA_GUARDED_BY(cameraEvents_); + + std::function)> cameraAddedHandler_; + std::function)> cameraRemovedHandler_; + + std::map, Request *, FrameBuffer *)>> cameraBufferCompletedHandlers_; + std::map, Request *)>> cameraRequestCompletedHandlers_; + std::map)>> cameraDisconnectHandlers_; void writeFd(); int readFd(); - void pushRequest(Request *req); - std::vector getCompletedRequests(); + void pushEvent(const CameraEvent &ev); + std::vector getEvents(); }; diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 1ef1384e..a07f06c4 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -107,7 +107,20 @@ 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("dispatch_events", &PyCameraManager::dispatchEvents) + .def("discard_events", &PyCameraManager::discardEvents) + + .def_property("camera_added", + &PyCameraManager::getCameraAdded, + &PyCameraManager::setCameraAdded) + + .def_property("camera_removed", + &PyCameraManager::getCameraRemoved, + &PyCameraManager::setCameraRemoved); pyCamera .def_property_readonly("id", &Camera::id) @@ -131,7 +144,14 @@ PYBIND11_MODULE(_libcamera, m) auto cm = gCameraManager.lock(); ASSERT(cm); - self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted); + /* + * Note: We always subscribe requestComplete, as the bindings + * use requestComplete event to decrement the Request refcount- + */ + + self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) { + cm->handleRequestCompleted(camera, req); + }); ControlList controlList(self.controls()); @@ -159,6 +179,71 @@ PYBIND11_MODULE(_libcamera, m) "Failed to start camera"); }) + .def_property("request_completed", + [](Camera &self) { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + return cm->getRequestCompleted(&self); + }, + [](Camera &self, std::function, Request *)> f) { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + cm->setRequestCompleted(&self, f); + + /* + * Note: We do not subscribe requestComplete here, as we + * do that in the start() method. + */ + }) + + .def_property("buffer_completed", + [](Camera &self) -> std::function, Request *, FrameBuffer *)> { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + return cm->getBufferCompleted(&self); + }, + [](Camera &self, std::function, Request *, FrameBuffer *)> f) { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + cm->setBufferCompleted(&self, f); + + self.bufferCompleted.disconnect(); + + if (!f) + return; + + self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) { + cm->handleBufferCompleted(camera, req, fb); + }); + }) + + .def_property("disconnected", + [](Camera &self) -> std::function)> { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + return cm->getDisconnected(&self); + }, + [](Camera &self, std::function)> f) { + auto cm = gCameraManager.lock(); + ASSERT(cm); + + cm->setDisconnected(&self, f); + + self.disconnected.disconnect(); + + if (!f) + return; + + self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() { + cm->handleDisconnected(camera); + }); + }) + .def("__str__", [](Camera &self) { return ""; }) From patchwork Wed Jun 29 07:04:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16418 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 8182EBE173 for ; Wed, 29 Jun 2022 07:04:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 26A4765656; Wed, 29 Jun 2022 09:04:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486299; bh=cDIG+D1ixMFp/dsesXD5Bu5CWYwPNvTWd8LiZXLOx7A=; 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=pV0RG5FKGmfzqdodBM/SmZWxRsm6xdq1XD5wpAmtXm5ncr3S7I04mJkD6HjWFa41v m1hUE0UrSIj8iDhrLIgCJyzzxKLxANrof9IKY6ZYRssMovifGGkwS3Mpj4j7sRO01k X15QE6Bkzx4JTX1nn978kpcB0zN+cxZuIb2CEKe8CPlnuKqg4GAmunax7AmgwimkyK RL9CQzm+GcI6Wrj41hFOYc/GlKAkBCDx5Fuasbc1OHpjjA4/LqdNUWPbF2BSTQkfug 09WDVTM2juUeqQFWU8fSc3ne8su2zJYhgvCAKYDGQEj4+jYH1Ht5uXBlK6jkGtT9Lz JF+h/yGRApyyA== 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 45FAA65649 for ; Wed, 29 Jun 2022 09:04:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uNLlaRcf"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ADBEE4A8; Wed, 29 Jun 2022 09:04:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486287; bh=cDIG+D1ixMFp/dsesXD5Bu5CWYwPNvTWd8LiZXLOx7A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uNLlaRcfuDqbWZj3mTokkSMMDXY5fgS1GTpy5Qa1S5HOezGsNfLtnqeOHSqpeWUbg /6mlK7WCD19ct8HdeqbmBGwSsEaQ2S4Y9qXBvvwmWcm1dEdwhomzOzfIhZcJNpwIOi UEda54zrbyvuoulrJ+onsLGGYAt6YsOQtWFA+CSU= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:14 +0300 Message-Id: <20220629070416.17550-13-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 12/14] 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: Laurent Pinchart --- src/py/cam/cam.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 6b6b678b..17a4268b 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -230,11 +230,7 @@ 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) + self.cm.dispatch_events() running = any(ctx.reqs_completed < ctx.opt_capture for ctx in self.contexts) return running @@ -242,7 +238,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)) @@ -297,6 +295,9 @@ class CaptureState: ctx.camera.queue_request(req) ctx.reqs_queued += 1 + def __disconnect_handler(self, cam): + print(f'Camera {cam} disconnected') + def __capture_init(self): for ctx in self.contexts: ctx.acquire() @@ -310,6 +311,15 @@ class CaptureState: for ctx in self.contexts: ctx.create_requests() + for ctx in self.contexts: + # These cause circular dependencies: + # The callback in the camera points to CaptureState. + # The CaptureState points to the camera. + # This can be solved by setting the callbacks to None after stopping + # or using lambdas here and weakref.WeakMethod. + ctx.camera.request_completed = self.__request_handler + ctx.camera.disconnected = self.__disconnect_handler + def __capture_start(self): for ctx in self.contexts: ctx.start() @@ -321,6 +331,10 @@ class CaptureState: for ctx in self.contexts: ctx.stop() + for ctx in self.contexts: + ctx.camera.request_completed = None + ctx.camera.disconnected = None + for ctx in self.contexts: ctx.release() @@ -389,6 +403,9 @@ def main(): cm = libcam.CameraManager.singleton() + cm.camera_added = lambda c: print('Camera added:', c) + cm.camera_removed = lambda c: print('Camera removed:', c) + if args.list: do_cmd_list(cm) @@ -447,6 +464,10 @@ def main(): state.do_cmd_capture() + # This is not strictly needed, but it helps to do a proper cleanup and + # so makes it easier to use memory leak detectors. + cm.discard_events() + return 0 From patchwork Wed Jun 29 07:04:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16419 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 64F0EBE173 for ; Wed, 29 Jun 2022 07:05:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 04BC665659; Wed, 29 Jun 2022 09:04:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486300; bh=jMNfoKH54gCKMC1c4+vxSz2aDSApVNlcC2eDc42ZuDA=; 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=kt+osuUNlpo2pR6JjZzxtuECzAZHs5bnWNtAxu/BrRQItO10bMYPOVVZTojzI6E8f JXwioi0v5hyuXeJlovwfvry4g/hJPIGUypHuhS2Is0lYkEjABX+BdTQMncNEgKe9e8 zlRtMjHFLdWBSgmBUN0fA9fP9+U7u3fSKDI01aDWiiJqxHLtKq0NNDxEUNgI2VeejQ GBWEub+1GjPQnEIFd8NbQoUaiQoBC0HL9WKiaLecGkG2lMKZzSSKcV2KZUaZ9tNpA4 FKYZN4UaZF2czhqUWKxEFmN5d1C/JaA8M4LbMhwRVBqQUc1vVqZuwPtryPFdgNgzsp S13Qi9TIL7rVw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C977D65647 for ; Wed, 29 Jun 2022 09:04:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PslNNwxa"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 395356D1; Wed, 29 Jun 2022 09:04:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486287; bh=jMNfoKH54gCKMC1c4+vxSz2aDSApVNlcC2eDc42ZuDA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PslNNwxad2l/DDnCOyggrk0sDAxwJ8RYZuYaLw90UtDFkqbxQmUyGET9gDRbr+dkN CvwMatdL28XoqDCwFkKlyAr7B14ypnLRVn0YvbChgOElHmUs9fWv0yLYFCfgoy3E4S NTorJ9od4IHlQCmZVPkpiVzgSyoHICR1LKCKlClo= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:15 +0300 Message-Id: <20220629070416.17550-14-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 13/14] py: Discard/Dispatch Request events on camera.stop() 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" To prevent old Request related events from being left in the event queue, messing up the next camera.start(), force the events to be either discarded or dispatched when camera.stop() is called. camera.stop() will discard all Request events (RequestCompleted and BufferCompleted) for that camera. camera.stop(dispatch_events=True) will instead dispatch all events, also for other cameras. The dispatch version dispatches all events instead of just the Request events for one camera so that the event ordering stays the same. Signed-off-by: Tomi Valkeinen --- src/py/libcamera/py_camera_manager.cpp | 30 ++++++++++++++++++++++++++ src/py/libcamera/py_camera_manager.h | 1 + src/py/libcamera/py_main.cpp | 12 +++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp index 599a9f7e..dfd43a09 100644 --- a/src/py/libcamera/py_camera_manager.cpp +++ b/src/py/libcamera/py_camera_manager.cpp @@ -218,6 +218,36 @@ void PyCameraManager::discardEvents() } } +void PyCameraManager::discardRequests(std::shared_ptr camera) +{ + MutexLocker guard(cameraEventsMutex_); + + size_t oldSize = cameraEvents_.size(); + + for (const auto &ev : cameraEvents_) { + if (ev.type_ != CameraEvent::Type::RequestCompleted) + continue; + + if (ev.camera_ != camera) + continue; + + /* Decrease the ref increased in Camera.queue_request() */ + py::object o = py::cast(ev.request_); + o.dec_ref(); + } + + cameraEvents_.erase(std::remove_if(cameraEvents_.begin(), cameraEvents_.end(), + [&camera](const CameraEvent &ev) { + return ev.camera_ == camera && + (ev.type_ == CameraEvent::Type::RequestCompleted || + ev.type_ == CameraEvent::Type::BufferCompleted); + }), + cameraEvents_.end()); + + LOG(Python, Debug) << "Discarded " << oldSize - cameraEvents_.size() + << " request events"; +} + std::function)> PyCameraManager::getCameraAdded() const { return cameraAddedHandler_; diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index aa51a6bc..9c8d6ca8 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -38,6 +38,7 @@ public: void dispatchEvents(); void discardEvents(); + void discardRequests(std::shared_ptr camera); std::function)> getCameraAdded() const; void setCameraAdded(std::function)> func); diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index a07f06c4..c4755bea 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -168,16 +168,24 @@ PYBIND11_MODULE(_libcamera, m) } }, py::arg("controls") = std::unordered_map()) - .def("stop", [](Camera &self) { + .def("stop", [](Camera &self, bool dispatchEvents) { int ret = self.stop(); self.requestCompleted.disconnect(); + auto cm = gCameraManager.lock(); + ASSERT(cm); + + if (dispatchEvents) + cm->dispatchEvents(); + else + cm->discardRequests(self.shared_from_this()); + /* \todo Should we just ignore the error? */ if (ret) throw std::system_error(-ret, std::generic_category(), "Failed to start camera"); - }) + }, py::arg("dispatch_events") = false) .def_property("request_completed", [](Camera &self) { From patchwork Wed Jun 29 07:04:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16420 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 48961BE173 for ; Wed, 29 Jun 2022 07:05:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E203C6565D; Wed, 29 Jun 2022 09:05:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1656486301; bh=9IF/8cQaAqh4IDA/idC7EzTVcSpnho5mCs7AV/Irp0k=; 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=xb+TSo/chmDot28pYiQ1w/6jz/Tbd9j4YOVjydHc/CrJvYW+AGE62skDslmSWIC2i uhKk6IlldYzlDOWyl5zdY+uKKcrMYj3I/DZ6FslcZljI2vspX+xTI3Vn0d0IHsD3sU qZJWgVnEWp9TXlfsUK0vKfFTaqcB1Jd/7QC9A9821Kocrvj5/oCcJVBncm+SQQOr5Y lhhYv2OjPjatxRpNBjmlzJ9U6BFVarn9/rxKD9UyVuWG6+hYjIyQrzFGkjbkcQXjIA 7+3jau2uqmDO7uhNWEP/IBnbDrP98aPSNSLPm8P+kIpP4SNHaeAXGTWiIkatfxh6hw yHKAJ9IEXCG4A== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5662E6564D for ; Wed, 29 Jun 2022 09:04:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uwi0HuD7"; dkim-atps=neutral Received: from deskari.lan (91-158-154-79.elisa-laajakaista.fi [91.158.154.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B690C4A8; Wed, 29 Jun 2022 09:04:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1656486288; bh=9IF/8cQaAqh4IDA/idC7EzTVcSpnho5mCs7AV/Irp0k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uwi0HuD7b66EO0YxRi1jofo97HHNO2YIVJjAa028FY4DzLiGKmz+qpeIbgf7wv2/V 1M8odTc3n2pahu4GX5c6y30LFMqZ0Ly/wRLNZC1uMtL/kquN3yf4oBxLtiBoKDhhxb ApfnbW+lAeea3a8zb1DzfiaGPhTA20LaPVz2GbV4= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Wed, 29 Jun 2022 10:04:16 +0300 Message-Id: <20220629070416.17550-15-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> References: <20220629070416.17550-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 14/14] 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 --- src/py/examples/hotplug-monitor.py | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 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..787aa45a --- /dev/null +++ b/src/py/examples/hotplug-monitor.py @@ -0,0 +1,38 @@ +#!/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() + + cm.camera_added = lambda c: print('Camera added:', c) + cm.camera_removed = lambda c: print('Camera removed:', c) + + 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 + + cm.dispatch_events() + + cm.discard_events() + + return 0 + + +if __name__ == '__main__': + sys.exit(main())