From patchwork Fri Sep 18 07:59:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28313 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 11750BE175 for ; Fri, 18 Sep 2026 08:07:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 49A7268715; Fri, 18 Sep 2026 10:07:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="JJvnS8PL"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0EA91686C6 for ; Fri, 18 Sep 2026 10:07:39 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843af75de5so45602f8f.3 for ; Fri, 18 Sep 2026 01:07:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718858; x=1790323658; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pWte4PF2E/tGRz7BJM4oPrDJmjYKQaUPe9tX1qsF+8U=; b=JJvnS8PL+Qr8SHU0Eu0rqkTwfYidkZ0a1PMveg5rfeUUK1Q59hCQnA54NkH7fIlJWb /2WnK5qmWOYhKJuBQfzyXFmkaizWmzJmzreH4NWVVamSLWEerWqAzIhl26NTpaj+ZKij J0TKTVokP+IswGHmjcMlCKBRr1o81HkJNrA2V08UfAn4vuE+/0Jgk/mLu9fje7nf88b/ fZuXf0LE+0qWbnv7iAcFzRDTkuqO5YO2jeQhNMkfe6XY5Qc0o9HUPkya7+ySKRiVuY84 yuJv/mSPNrjUOwX9loP85eFWQei0cpbxq4IBZ3ZtqDtB987p4lDCpD1E4PE825RxlBGm vdrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718858; x=1790323658; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=pWte4PF2E/tGRz7BJM4oPrDJmjYKQaUPe9tX1qsF+8U=; b=dZBPV0W3FWJ4RF+16StvehVFmAeRkRmOk8wayQEHIXmLxKlh60FKyFGXmVMFZPfxgh TVOPEdBQpFlpkBG/sS9nvtVn5q0abwjvH93GPfv2PmjggNLfk/qNYQF5qAjfBwonY+GA QXj69/6tcN+2Pp11hZu9UwIqJbGRlEPO0thuwB+DtL63TKlWWgPw7Dg8RZ4zaUAcjGA+ A/64Epms5YdRqn2pVMwuC/W2PwPhA0GGrvWYQBbagsxS54umB9yJCXLxj2m7Qw4wC+uB LGMtzpa6RE+NR5rdfSSSXwELnWMm9PAgr982BDRorrsxWh8i1BhgaiyCAWYQU/gf96pA Oxyw== X-Gm-Message-State: AFuF++nKDIOIpBTx4jD2AJHUNXo+DFdTAxefRMJv3yJeMDD8S6515g6z SShMT9al1vNmAAX8jVV36/jM1ZCrB2tdyra9Mt4/AwGyK6eN71cX/vrJpFN+nTIckdoKolOey2v WS7yJJo0= X-Gm-Gg: AYBFou2ZRV0RjuWhM9Qt3NguVnOzanDQIBHcJUoHvgvl6wHbfy/5f8jxDkwyl/o7v8J /kbOGemfHgzYm6Fo8iREPgfrLFVAIF2UrEe8HDyXwrbhZ/KIr/8lOnhKXjmpeYz6RLrjXJk5Fi+ /EO73KVscxM2nrEftH6n1YPOFIhhjyYyU4YRWjOJ/FILQ/jZjI90r3fUf0DxyzRWOO8lvs2fbyH e+UHjD15Qh3kfkgiMNl/qU95RqmhRjnGyh04oNxE1ZfPwQYwZifZCKb0ZRK7dAi7zfEWnRX8F7d vW6FkhTvlyEwVu/TBlcrRPqrXyMLdxpjnipbylAQff/bEVIyrP8xBwxrW7vA/SDtQ0YBIDMTfIZ NQs9xYlpoUuyEsvMVVsIWjfUPlDbGMkbdMjzVgOsEcbOhLCX4bW3B0OezGLSyeCcRFs+NSqSACy HBXGO2AR3/YKiBs3DgGBI+rULfa/+pCcLDUECR/CPBECwz/G9KvoVS1Z8D6tbEXFWj9DaOTg8tO 4xhyRUe7ZtWQa9eQHrXjp/IfZ9OHsHqfNC0WnamHWHbPwm14PjkfLNnj/2+AJRHl9ssCcyeVSo7 SihGV4ffH5SQODBAECF0A02AHFhawJTdi/o8p0sKEKV1/7Bq1Zv//ZersAZ6lT04To1X8qLnZrr iIuqQdSJG5o+Ovgm5fWMle521oDXWrG3oQWk= X-Received: by 2002:a05:6000:2405:b0:487:11e0:cb4b with SMTP id ffacd0b85a97d-4871fa85a20mr1266967f8f.4.1789718858432; Fri, 18 Sep 2026 01:07:38 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:37 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 01/20] libcamera: sysfs: Add devicePath() helpers Date: Fri, 18 Sep 2026 08:59:39 +0100 Message-ID: <20260918080734.1228227-2-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add sysfs::devicePath() helpers to resolve the sysfs path of the device via a character device from either the device node or the device major and minor numbers. The helper is used in V4L2Device::devicePath(), replacing the baked in logic there. Signed-off-by: Naushir Patuck --- include/libcamera/internal/sysfs.h | 3 ++ src/libcamera/sysfs.cpp | 61 ++++++++++++++++++++++++++++++ src/libcamera/v4l2_device.cpp | 13 ++----- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/include/libcamera/internal/sysfs.h b/include/libcamera/internal/sysfs.h index aca60fb6c8ab..1b30317c0dea 100644 --- a/include/libcamera/internal/sysfs.h +++ b/include/libcamera/internal/sysfs.h @@ -15,6 +15,9 @@ namespace sysfs { std::string charDevPath(const std::string &deviceNode); +std::string devicePath(const std::string &deviceNode); +std::string devicePath(unsigned int deviceMajor, unsigned int deviceMinor); + std::string firmwareNodePath(const std::string &device); } /* namespace sysfs */ diff --git a/src/libcamera/sysfs.cpp b/src/libcamera/sysfs.cpp index 3d9885b080c6..7c441a17f86d 100644 --- a/src/libcamera/sysfs.cpp +++ b/src/libcamera/sysfs.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -26,6 +27,26 @@ LOG_DEFINE_CATEGORY(SysFs) namespace sysfs { +namespace { + +/* + * Resolve the "device" symbolic link of a sysfs character device directory to + * the sysfs path of the physical device associated with it. + */ +std::string resolveDevicePath(const std::string &charDevDir) +{ + char *realPath = realpath((charDevDir + "/device").c_str(), nullptr); + if (!realPath) + return {}; + + std::string path{ realPath }; + free(realPath); + + return path; +} + +} /* namespace */ + /** * \brief Retrieve the sysfs path for a character device * \param[in] deviceNode Path to character device node @@ -49,6 +70,46 @@ std::string charDevPath(const std::string &deviceNode) return dev.str(); } +/** + * \brief Retrieve the sysfs path of the physical device for a character device + * \param[in] deviceNode Path to character device node + * + * Retrieve the sysfs path of the physical device associated with the character + * device \a deviceNode. The path is absolute and contains no symbolic link. + * + * \return The device path on success or an empty string on failure + */ +std::string devicePath(const std::string &deviceNode) +{ + std::string path = charDevPath(deviceNode); + if (path.empty()) + return {}; + + return resolveDevicePath(path); +} + +/** + * \brief Retrieve the sysfs path of the physical device for a character device + * \param[in] deviceMajor The character device major number + * \param[in] deviceMinor The character device minor number + * + * Retrieve the sysfs path of the physical device associated with the character + * device identified by \a deviceMajor and \a deviceMinor. The path is absolute + * and contains no symbolic link. + * + * Unlike the device node variant of this function, resolving the device from + * its device numbers requires no access to the device node itself. + * + * \return The device path on success or an empty string on failure + */ +std::string devicePath(unsigned int deviceMajor, unsigned int deviceMinor) +{ + std::ostringstream dev("/sys/dev/char/", std::ios_base::ate); + dev << deviceMajor << ":" << deviceMinor; + + return resolveDevicePath(dev.str()); +} + /** * \brief Retrieve the path of the firmware node for a device * \param[in] device Path in sysfs to search diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 258c82b8d238..8b9c3a357149 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -454,17 +454,10 @@ const struct v4l2_query_ext_ctrl *V4L2Device::controlInfo(uint32_t id) const */ std::string V4L2Device::devicePath() const { - std::string devicePath = sysfs::charDevPath(deviceNode_) + "/device"; - - char *realPath = realpath(devicePath.c_str(), nullptr); - if (!realPath) { + std::string path = sysfs::devicePath(deviceNode_); + if (path.empty()) LOG(V4L2, Fatal) - << "Can not resolve device path for " << devicePath; - return {}; - } - - std::string path{ realPath }; - free(realPath); + << "Can not resolve device path for " << deviceNode_; return path; } From patchwork Fri Sep 18 07:59:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28314 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 7BE73C3292 for ; Fri, 18 Sep 2026 08:07:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6BDF068707; Fri, 18 Sep 2026 10:07:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Gbp7xuFn"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BC74F686C6 for ; Fri, 18 Sep 2026 10:07:39 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-485843116d2so53039f8f.1 for ; Fri, 18 Sep 2026 01:07:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718859; x=1790323659; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ZWhMINbuK4baItUqch+JY7Iq/0TW3FnKP6ajxJKtPzE=; b=Gbp7xuFnP9w7r/Ug/LaioYoc5DDugcBf3k0blu80IS8/dK273OssahePcNkRi6zD2v w4nn5mMxs7tsSYmYfRZTBxdykWNwH8pzHURA9cS8OEVCATw7XPD7e2vmXhIVKDqGcpwN khl4FODbLtEB1OCVax5rcnRHnZFH0tmYvZeODJSEhC8M5W9sVIJEQq4n1SrmmiS+Af/W 4UVMS8RMbczXAJcUG+ov4Qt8OTMFlSNL+pXCJTlvQF2NT6E2/V+tH5MB3IKHsZa6UcnZ 9r/nj8u/GRWqNJYYHDzibAB25J9fkcxdu6OVkqk8sNko6Ci+4ewmaYhIlejb3uMzHlsc dOlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718859; x=1790323659; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ZWhMINbuK4baItUqch+JY7Iq/0TW3FnKP6ajxJKtPzE=; b=fDVVa25rasIB1yshFrRqQ85ULhAb/HwkBUvsvFqhBR1aJYVWLTSaOWDNUbnFIXK9F4 NHSuXSfqjJgbKS4efmj01afM9YH3ExJNhlmLzcLiSPBUpz5Mtvx4/r7QsWzYrz3VUuP9 j6nTDXJKiOBHit1YW7/Ea/uAhBOl/cWLpGy+QWDwrqicQziXF1E3DjrXYXcIlGi/hzJM nS1pNgennzLGskklJ5sFFe5Y5dM3Q1fO1eW1hEY11f9zW8zt5fUEk1MWICsttqDlHVsE soFRIduXAjmbbMne0eBFnuxRv/5wz4h+7tVixCwCY1e2iIeDp43/L3sCEQsDLk42Jm30 EZuw== X-Gm-Message-State: AFuF++niK8wOdLAZtKrDDUlKcicqRqbIKFsnfKwU7cop5zwc5iKQq1T9 DPdsphEmIYxCcwFqELCJAPunRWmSAzxFJvISML8xJ9SoGPkuIGvFciNlYAR3mzsWJx3FBRdaUYc Idz/pIco= X-Gm-Gg: AYBFou1qRG97TG37Asiwf7cCOuj85fdzHQNB/ZkeGSGr2dQTFwSDYyHPZqnkYs7uZWj gIqNfG0y1E9JkQfLukXZQ6e8wguA0v0QQXCa5fqqsQwPyXJAYnGSD8PfMgbZLVw+RnB7rUv452S 4ZgMTp5dvZZvYBG3I8YzeklfKNWpyPyP/z3575VbwGtAiAgrjTJgBYtamXngkgXeRppvYiuiCB9 3H26OG2oDxXsIk1czDttkdRtf9Ahln1Mgtfuql7AFVjTiEl4DmpScFvrZQUNbtm9qzY/N6K4CbA ygly76c2tcAR3vwiFphTnZ26bKBH5KfCy4twa1oD0L4UEe1SaiRt0W8rC0pPvfkuDiuXYnlc4aC EoS0ccn3DakZhwMpmI8xJuogi7eSPEoePysUQVg1yLLXKGw+lJ4xBPstK7eYgvKmHKiDUrzI4xc vts+k+cnPWyF53jfQDCJuhvMUOCn3H09W2uOA0juzIIexk+7dDfhxQJDJ7AgHzqwWNO0wnfJGcP tDwxWsRcQnAoKLecZCckmwUyUEUE2F0lYZy2HIW22YQLRvXmSTk9Z5XRkP4pjGpVNXNuaG2CIMC mFeYzLJBI8rLTeVd4p0bXHKQCV1iQJPFpK1fJpYMUtXLEcB+3MxbAfFttZOJMWrIVnESiz5Yy81 zck2dS0hNeRCIbvxSHa++P8cOucQ58JhP0dg= X-Received: by 2002:a05:600c:83cd:b0:49e:6806:5712 with SMTP id 5b1f17b1804b1-49fc7ddb90emr11955315e9.2.1789718859239; Fri, 18 Sep 2026 01:07:39 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:38 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 02/20] libcamera: v4l2_subdevice: Refactor the model name derivation Date: Fri, 18 Sep 2026 08:59:40 +0100 Message-ID: <20260918080734.1228227-3-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The heuristic that derives a model name in V4L2Subdevice::model() operates on the media entity name only, but is only reachable through a V4L2Subdevice instance. Move it to a static modelFromEntityName() helper and reimplement model() in terms of it, so that a model name can be derived from an entity name without instantiating a subdevice. Signed-off-by: Naushir Patuck --- include/libcamera/internal/v4l2_subdevice.h | 1 + src/libcamera/v4l2_subdevice.cpp | 60 ++++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/include/libcamera/internal/v4l2_subdevice.h b/include/libcamera/internal/v4l2_subdevice.h index c37a82afa881..9593d4f45f64 100644 --- a/include/libcamera/internal/v4l2_subdevice.h +++ b/include/libcamera/internal/v4l2_subdevice.h @@ -160,6 +160,7 @@ public: int setRouting(Routing *routing, Whence whence = ActiveFormat); const std::string &model(); + static std::string modelFromEntityName(const std::string &entityName); const V4L2SubdeviceCapability &caps() const { return caps_; } static std::unique_ptr diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp index a69de154e8bb..703c82d7f715 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp @@ -1702,38 +1702,46 @@ int V4L2Subdevice::setRouting(Routing *routing, Whence whence) */ const std::string &V4L2Subdevice::model() { - if (!model_.empty()) - return model_; + if (model_.empty()) + model_ = modelFromEntityName(entity_->name()); - /* - * Extract model name from the media entity name. - * - * There is no standardized naming scheme for sensor or other entities - * in the Linux kernel at the moment. - * - * - The most common rule, used by I2C sensors, associates the model - * name with the I2C bus number and address (e.g. 'imx219 0-0010'). - * - * - When the sensor exposes multiple subdevs, the model name is - * usually followed by a function name, as in the smiapp driver (e.g. - * 'jt8ew9 pixel_array 0-0010'). - * - * - The vimc driver names its sensors 'Sensor A' and 'Sensor B'. - * - * Other schemes probably exist. As a best effort heuristic, use the - * part of the entity name before the first space if the name contains - * an I2C address, and use the full entity name otherwise. - */ - const std::string &entityName = entity_->name(); + return model_; +} + +/** + * \brief Derive a model name from a media entity name + * \param[in] entityName The name of the media entity + * + * There is no standardized naming scheme for sensor or other entities in the + * Linux kernel at the moment. + * + * - The most common rule, used by I2C sensors, associates the model name with + * the I2C bus number and address (e.g. 'imx219 0-0010'). + * + * - When the sensor exposes multiple subdevs, the model name is usually + * followed by a function name, as in the smiapp driver (e.g. + * 'jt8ew9 pixel_array 0-0010'). + * + * - The vimc driver names its sensors 'Sensor A' and 'Sensor B'. + * + * Other schemes probably exist. As a best effort heuristic, use the part of + * the entity name before the first space if the name contains an I2C address, + * and use the full entity name otherwise. + * + * This function operates on the entity name alone, so that a model name can + * be derived without instantiating a subdevice. + * + * \return The model name + */ +std::string V4L2Subdevice::modelFromEntityName(const std::string &entityName) +{ static const std::regex i2cRegex{ " [0-9]+-[0-9a-f]{4}" }; std::smatch match; if (std::regex_search(entityName, match, i2cRegex)) - model_ = entityName.substr(0, entityName.find(' ')); - else - model_ = entityName; + return entityName.substr(0, entityName.find(' ')); - return model_; + return entityName; } /** From patchwork Fri Sep 18 07:59:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28315 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 23F1DBE175 for ; Fri, 18 Sep 2026 08:07:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 959CA68716; Fri, 18 Sep 2026 10:07:45 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="TFFPjSSl"; dkim-atps=neutral Received: from mail-wm2-x11.google.com (mail-wm2-x11.google.com [IPv6:2a00:1450:4864:31::11]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D6F426849B for ; Fri, 18 Sep 2026 10:07:40 +0200 (CEST) Received: by mail-wm2-x11.google.com with SMTP id 5b1f17b1804b1-49fbca514f8so223705e9.2 for ; Fri, 18 Sep 2026 01:07:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718860; x=1790323660; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=CUvh61Du7EjaxlpR8gCpn+9wnTbmCkWMDtxvQxb9LrM=; b=TFFPjSSljOJwQswmEwjyNGEnzPfOPDPPJdYYHdilGeL2FIA/vHvHo19H0gplckrJ43 oup3tGjZ+IfTmo0w2zs+xZEE0rmtn6qVBaabTu/rIFOptb7/FSVi1y2tLwF41n9H0umk 6jxXa8r3PjqA84IiZC2+6EgsfDRzwFiTf4W1oxFBFCPZD9ccw4yONnD77oNtjZWX48Ll wyEHk+vvQXb28UUNYy3iMDEGJhjRhxt/876yFHTp9lG8kuQwUAi3KmLWWR+vomOyPSOl t2Ix1F6iOMfW9Sv9AoEO96G3kbpOUJGwEDDNdL8AOUXKTrZBvZQQQ+lOvxBdqA0dXRih H1Rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718860; x=1790323660; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=CUvh61Du7EjaxlpR8gCpn+9wnTbmCkWMDtxvQxb9LrM=; b=dVbY1zuM8dtVwn5fThCCYzFFz8eFszGGIUu8T81CAuLVB+11+1tPbR6D6cEq7ilkaF LRAPi6vA3c5GSoAnmcgXw4ClT4JHtR/FemHmKnPbfc1HFUKNB7z+O8Y/kIVymj/mba6Y 5cK9rkh92uGfFSviW3u8ks2fZKV98uzXeqXe21pBN8CAn8VdMclDsMmIb85Ez9gC6xo/ umCQ7kH+2dmlmJcfxXIDhSQAj6vnO+CMTMPf1veooTN/maAFULlf4j9Q1iQbEEXhaHt2 rnlVLOsTonvMEHBTdwD2xb9zobR4H/wB7xIwhpydN6fJ7Mf8InTsXLGnOvaLZLhsd35O yxLQ== X-Gm-Message-State: AFuF++mh0dIzjJFWb9hI1iJRDye8380RQIXpck8sqWym7lIwO8vT3xHi vW4kPdXsiAg9hBPqpS8op4GmsD9klVx6UpdcH2S3CSmOhxXP/Fsi3stu0NJ73+NL5Op5NGtx/FK QLjwGZWA= X-Gm-Gg: AYBFou0sd3tTNxHgVwLIGp+24T+8BqL20kByPOT4r0YpsLEWVXMa3I5Ow9Vw428aDwn 8CteqQ9Eygfe0skSpBrfpHYVQjKY3extJ89s75S0YQM9dp6aGbzPQqNP3q8yBIg4NXey2ufRxEi VSwwMFjpSCgzHSrJ+7J3UuB0Nquu4OO1gu0G9kZswSOoak9ggdKYosBt5lGtLvJHWH+ZY2ofxbU vVMTXq7F906/92NtflVOS5G9EkzWmSIr0HyrlLfC0xSQ1ay6qLrcCRMrwnX+x/7F8n0k7MVoM4c Sin8l37Ab1M51MusaEEUXH0lnUM81v4qe6ZVWsjgWt9ES9aMEDtbW5wVa/O7/3h1CBFLbyPe8Xm /BrVQ9DOc1aYHz3D2rmcpmDckWNS83gg5GUoO1PGIBsTn/Wm6asHitdT0hUX+ujBi7py/PpoBcp GtDcXrtl37TQBKWXCtkdQKkKy5jD7VKMjBsFWG++uu0sikptTZX6WSEeAlvm4IbBy5AEah+8Gey HaauB63PIloEBXRZMjBbaCuutRZ2FvYFhAEQtDhD9kNoWzGzI442+fvqDwDW777y7WWIYD5JjFf TMt/bPJsL+dYzvwR/mKbv4F10ud4SEna7YB/djMZxRX/7o+It50GorlGI7Rc0/FVNXIx4aTwmgg N8Vau0mQzhPhy2BOLbQI8HcEPmQwOG0X+N7Y= X-Received: by 2002:a05:600c:3b99:b0:49e:6683:d227 with SMTP id 5b1f17b1804b1-49fc7bbfe4bmr15815225e9.0.1789718859890; Fri, 18 Sep 2026 01:07:39 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:39 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 03/20] libcamera: camera_sensor: Generate sensor IDs from a media entity Date: Fri, 18 Sep 2026 08:59:41 +0100 Message-ID: <20260918080734.1228227-4-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a CameraSensorFactoryBase::generateId() helper that computes the sensor ID from a MediaEntity using sysfs without opening the device. Update CameraSensorLegacy and CameraSensorRaw to generate the sensor ID with this new helper. This change is in preparation for generating camera IDs at device enumeration time. Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_sensor.h | 1 + src/libcamera/sensor/camera_sensor.cpp | 27 +++++++++++++++++++ src/libcamera/sensor/camera_sensor_legacy.cpp | 6 ++--- src/libcamera/sensor/camera_sensor_raw.cpp | 3 +-- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h index 2a2f1f2c936d..17080ba32d37 100644 --- a/include/libcamera/internal/camera_sensor.h +++ b/include/libcamera/internal/camera_sensor.h @@ -92,6 +92,7 @@ public: virtual ~CameraSensorFactoryBase() = default; static std::unique_ptr create(MediaEntity *entity); + static std::string generateId(const MediaEntity *entity); const std::string &name() const { return name_; } int priority() const { return priority_; } diff --git a/src/libcamera/sensor/camera_sensor.cpp b/src/libcamera/sensor/camera_sensor.cpp index 580d3626216c..05654354ea1b 100644 --- a/src/libcamera/sensor/camera_sensor.cpp +++ b/src/libcamera/sensor/camera_sensor.cpp @@ -13,6 +13,7 @@ #include #include "libcamera/internal/media_object.h" +#include "libcamera/internal/sysfs.h" /** * \file camera_sensor.h @@ -474,6 +475,32 @@ std::unique_ptr CameraSensorFactoryBase::create(MediaEntity *entit return nullptr; } +/** + * \brief Generate a unique ID for a camera sensor + * \param[in] entity The media entity that corresponds to the camera sensor + * + * The ID is derived from the firmware description (device tree or ACPI) of the + * device associated with the sensor's media \a entity. It is unique and stable + * as long as the system firmware is not modified. Sensors that the firmware + * does not describe have no ID. + * + * The ID is computed from sysfs only, using the device numbers stored in the + * media graph, without opening or otherwise accessing the sensor device. + * + * \sa sysfs::firmwareNodePath() + * + * \return The sensor ID on success or an empty string on failure + */ +std::string CameraSensorFactoryBase::generateId(const MediaEntity *entity) +{ + const std::string devPath = sysfs::devicePath(entity->deviceMajor(), + entity->deviceMinor()); + if (devPath.empty()) + return {}; + + return sysfs::firmwareNodePath(devPath); +} + /** * \fn CameraSensorFactoryBase::name() * \brief Retrieve the camera sensor factory name diff --git a/src/libcamera/sensor/camera_sensor_legacy.cpp b/src/libcamera/sensor/camera_sensor_legacy.cpp index 83e2c2593215..99661f6fd02e 100644 --- a/src/libcamera/sensor/camera_sensor_legacy.cpp +++ b/src/libcamera/sensor/camera_sensor_legacy.cpp @@ -302,10 +302,8 @@ int CameraSensorLegacy::init() int CameraSensorLegacy::generateId() { - const std::string devPath = subdev_->devicePath(); - /* Try to get ID from firmware description. */ - id_ = sysfs::firmwareNodePath(devPath); + id_ = CameraSensorFactoryBase::generateId(entity_); if (!id_.empty()) return 0; @@ -315,6 +313,8 @@ int CameraSensorLegacy::generateId() * Verify it's a platform device and construct ID from the device path * and model of sensor. */ + const std::string devPath = sysfs::devicePath(entity_->deviceMajor(), + entity_->deviceMinor()); if (devPath.find("/sys/devices/platform/", 0) == 0) { id_ = devPath.substr(strlen("/sys/devices/")) + " " + model(); return 0; diff --git a/src/libcamera/sensor/camera_sensor_raw.cpp b/src/libcamera/sensor/camera_sensor_raw.cpp index 6344a34fc4c5..423b14af2890 100644 --- a/src/libcamera/sensor/camera_sensor_raw.cpp +++ b/src/libcamera/sensor/camera_sensor_raw.cpp @@ -37,7 +37,6 @@ #include "libcamera/internal/camera_sensor_properties.h" #include "libcamera/internal/formats.h" #include "libcamera/internal/media_device.h" -#include "libcamera/internal/sysfs.h" #include "libcamera/internal/v4l2_subdevice.h" namespace libcamera { @@ -571,7 +570,7 @@ int CameraSensorRaw::initProperties() properties_.set(properties::Model, utils::toAscii(model_)); /* Generate a unique ID for the sensor. */ - id_ = sysfs::firmwareNodePath(subdev_->devicePath()); + id_ = CameraSensorFactoryBase::generateId(entity_); if (id_.empty()) { LOG(CameraSensor, Error) << "Can't generate sensor ID"; return -EINVAL; From patchwork Fri Sep 18 07:59:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28316 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 7800CC3292 for ; Fri, 18 Sep 2026 08:07:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E2E3D6870B; Fri, 18 Sep 2026 10:07:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="q8AdOMB9"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 28480686F7 for ; Fri, 18 Sep 2026 10:07:41 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843178b1c8so62431f8f.0 for ; Fri, 18 Sep 2026 01:07:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718861; x=1790323661; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=v2RPFmD/SXTku6+ijEFzF9ylGtmdyCFFKZdl09UOI8Y=; b=q8AdOMB9Hw7MkJiHJdxUAufsrEG5ThENaEFuaVXuxkmV3594fOtKaviXa4L8YC7FmA KdfY0IG+HSOfmkPVvRVJ63Gv3zI83ASQQLnSVbJxj1hZCkH4g3+MXOwpKs42Aacw87Ac xQoU3PjAjUShT6YUc6zldY2A7g+TC1nC/1/TPHFy5nF6W/qsVoUFJPDc9wyiwl/26LAj AW3joX6uPHhI7yq8+6jos9vF02kse3nSEkKsZZVq0qyv7ngOXTev/com6X7kiIO857K7 cKFtueyNu8cDSgkGEEFhjDKU4DrZcvEH8vBkJ9t3IVQdh2+ojM2AjyIuHWU5ID8I4vMT 0h6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718861; x=1790323661; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=v2RPFmD/SXTku6+ijEFzF9ylGtmdyCFFKZdl09UOI8Y=; b=vV0Y93fJFM2e0xTT167cZYGDKElnUOn2mfybUqPYXn0VmzhkIJhNBjQlZgopm5XUf5 pZAqwDpQPyvMPLjuAHcqtLsRvNEItnJHwYo5m+dXrVgisBf/vMTHAYKP1nKyhzCQ8rUa 7Qkd3epno82q8W8vghcYp0Ulx/h1qPuKvXPSc51TJ9iUsHBERmXabRyHQnWS5XMBkKhX BF/46OoPw6IzSEvOFaAwqUk5aIlBtdGBFDr/MFMbfYyT4osL6HQ2vNAEBDCX2xszfhxK S4Bepfioi+a+MFeOWaY2t2M1LKkUn5WpSyZT8ESD3Kfl/AGQWFhbEueF1BtJn/oLHXSY bUaQ== X-Gm-Message-State: AFuF++ngeVXfJVMTNbu7KneKFXPNk8L7sI6bjYpCE8kAzL/VMMASLfI0 WZJ6URi58xV0A+EGeYTXji00Mp38FsINm0astILKqlWph2ivDhxVzFIXtaSt1YKd7CK0HejvRdo mz7inrUE= X-Gm-Gg: AYBFou1tdExoJtG7IGp59jeS8UAwyAymADNKcvJTijh29TjbJ+/lIZTTxJJBA9wNxjm cdTNZNwmoP0JV9jnwbARxm1Ok80RWz/KKF2ifQOZx+jEK9M6yNLNvbslng0+H1DRwEnQuYOEqil E6+UR6QSPrrtUCQW5vwbG29Gc6ViSvsetln55kHRqWoWa/F2UHwnPH981aROVvpZiSnO/vhiXLj Fz14oF7UGDevO2b/SYeb88fGCyF8JTSOLWVmICqBtpKERIbh+F1OM9cIq7x14pgE6z9Ty9defTy nOEWL19iQ81qQvz/BxrSWWNOPyo/QhWtUACEKZNlM0o7qPwz5Tbs38P8FX6v5MzPd9YXByZpVE8 GaN+t/TpXhav/YuXcA6Xc1ZX5fmbxHWEPAOlRMDdXsPky5F3dbNI30p6/hWv2spWFMT5R6BcWjO jLiJsMSa4NeldEHNEYqpRHpyQznigkDaHhvE5OHmAbFyS2jFOuiO4SrHPQyAATYz1flWfeorUZJ GlmE4Uybg9EXvTS3INMuRLeYnSyE+XHEsp1iBz761ZATwbGp7V3KX2k3/cySHPMfKGRPD83V3u4 lsKUxV5bjYE9VLp2ItZsyAJNFHm4Wcj4vHmWQ/Gp1qjIMyAtBStBnbTqoYsd8EW6PIFtmGNQFo4 6zpEsEudjTNaJMasR/DrTlssokGuE7fGl62Q= X-Received: by 2002:a5d:5d02:0:b0:487:421:7c83 with SMTP id ffacd0b85a97d-4871f92df61mr1330745f8f.0.1789718860704; Fri, 18 Sep 2026 01:07:40 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:40 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 04/20] libcamera: device_enumerator: Add non-acquiring searchAll() Date: Fri, 18 Sep 2026 08:59:42 +0100 Message-ID: <20260918080734.1228227-5-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a searchAll() function that returns all media devices matching a DeviceMatch instead of the first match only. Unlike the acquire and advance behaviour used with search() in pipeline handler match() implementations, searchAll() allows callers to inspect every matching media device without acquiring any of them. This will be used by future commits for device enumeration. Signed-off-by: Naushir Patuck --- .../libcamera/internal/device_enumerator.h | 1 + src/libcamera/device_enumerator.cpp | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/libcamera/internal/device_enumerator.h b/include/libcamera/internal/device_enumerator.h index 5e1aec9924c5..14fd5750a0ab 100644 --- a/include/libcamera/internal/device_enumerator.h +++ b/include/libcamera/internal/device_enumerator.h @@ -45,6 +45,7 @@ public: virtual int enumerate() = 0; std::shared_ptr search(const DeviceMatch &dm); + std::vector> searchAll(const DeviceMatch &dm) const; Signal<> devicesAdded; diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index 0ce65e89a67f..6eaa52604a50 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -362,4 +362,35 @@ std::shared_ptr DeviceEnumerator::search(const DeviceMatch &dm) return nullptr; } +/** + * \brief Search available media devices for all pattern matches + * \param[in] dm Search pattern + * + * Search in the enumerated media devices that are not already in use for + * matches described in \a dm. Unlike search(), all matching media devices are + * returned instead of the first match only. The caller shall not use the + * returned devices for capture without acquiring them first. + * + * \return A vector of matching MediaDevice instances, empty if no match is + * found + */ +std::vector> DeviceEnumerator::searchAll(const DeviceMatch &dm) const +{ + std::vector> matches; + + for (const std::shared_ptr &media : devices_) { + if (media->busy()) + continue; + + if (dm.match(media.get())) { + LOG(DeviceEnumerator, Debug) + << "Successful match for media device \"" + << media->driver() << "\""; + matches.push_back(media); + } + } + + return matches; +} + } /* namespace libcamera */ From patchwork Fri Sep 18 07:59:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28317 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 6B770C3356 for ; Fri, 18 Sep 2026 08:07:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B147068726; Fri, 18 Sep 2026 10:07:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="TE2y5t2X"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6663A68716 for ; Fri, 18 Sep 2026 10:07:43 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843af75de5so45613f8f.3 for ; Fri, 18 Sep 2026 01:07:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718863; x=1790323663; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=G8Rujl4eHFMq0pxRHkPtyNcbpkOHl2MFCUZ4OoW5xoo=; b=TE2y5t2XX1kxYwV272NTxh4XRSRfCVYgHb9u3KmvPsh4gMqfitbiYSmhx61I8khmRP vebuoNd/L1Bntm+a4EC+Cv6tytL+OvpYd/UA/zWpyC8IQjpte36JxfJXQZFWEnKotMWA qR4Cc5fyKe8tHHeH3CX4pWQjWfwOA/ytzndDyDc48jlgyOzwhjlRhm5CadCEz7zjRzdE 1xItTVWwAnGTM9Uys+UD3TIgqn8kXULxITLxc4gnKLaT/3+loYt+lBc2CK2nf4ZHw7ZV C26KxRXjWjt22LpUod5pS1ZkVWEpAwWrz8VbyjN+hdgLs3H1E7u9VJy3AG0BgNGXwtLj QYxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718863; x=1790323663; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=G8Rujl4eHFMq0pxRHkPtyNcbpkOHl2MFCUZ4OoW5xoo=; b=tpfbDktQbn/zrAfl8Yd2+FBQfGAcuViuinJkpfom5/7i5B7YNEwgvWsqzqvxdy1qgw dJGi1TG4K7sVjRQhf6SzoTM/YkHSrlJpQtFP706TmozzSvwCSVeXf/dUSPn2MhXUvAeq YLQ78aeef/iNbjGx7O59awYLE5lAQ0v3s2NhzcRHtGuF0/ELVfKrifpFjOjQqd4px0Vh PdZl3MGza40PGhdY+Lx0PSgCpkDK5TJkBYfWCR9Ps1it42hfYuiBppCTPWApNvTfV11A jlbnbj3wDdWE9Jv3zYsltfZyp7LZGRkE5rIqTrAOgaNKzvGMH+BQzFMn7WDRoJaDjUuK sStg== X-Gm-Message-State: AFuF++m7ac7V0pzYyoIDoTjhcpKl74t5i2nDJXXfBU5P7MX1j7zeSJ6g ZeJ2g3DFtlrsDARbUCY+37xS1Kqaso0ICqx6Fni1u5c1u9mjHAwF4SMuyT4Dam0myjEWYldqA4m Ev1eMtjk= X-Gm-Gg: AYBFou0YOcexE0hkNpEu/zqnVsV4LhNJ1shUmH+4rs2bhLtKa6NF4Wbsha7VTgKn+rC gxTNbenfrj3VDLSLUWuKL/NTU5iDlR6Qb9dzKIrm7W0Rq3bhHFAr2xlntsFDjjX3l7i+VpUQZLa HKvlJ4qhdma0vLIjHodue8dfJeMp53d4wDXLZnYoC0pU39DPKAW/pjhYEH6P1GzO+FBSPPUDbvS Som6p5mhIEo2JJiP6FOoADcyt/N4WZkU8D/9GWEl8VQlYrccqA/5JvdTjIBKhZkQwEch1DzbwKe 5597SU+6OIwKKxOhxenfs8YfcPz1hNUQTmD2gNGDc+2DDCl3SgYaEqBCgW3uYwcA4hSa4K838dc GLzgdgRo4uC1bI5sVfo0kf5g6rtBogA8L8Ps/iamoKj0vaBMioyXqPV6zRe/XBdsi+7NU7AGGf2 Ab+KokpAJPyzBRBZ9IWzP7w/0B36jujTvT258JHMLVkwob6LhEkS3hLOCatse0cD57/qM7pMfIk FkuygKdpV07vXCi3w3ldlPYjHl0ORqGKasr0G9mC+adoat5PmacEaSrP1PVxvxPTcFtj/MNmLs5 5qQhmTwS/rCUfKCZKOLjYd+NUgFp0z1QJzNcx/JylIlb5o1T1NIBroIqtIXeajk9IK0P6wcdd7S sX/jj8iEXzeRw3miYkHOfVZ4fLxuVP01E+ZI= X-Received: by 2002:a05:6000:2dc2:b0:487:ae2:4d0e with SMTP id ffacd0b85a97d-4871faa0f16mr1203784f8f.5.1789718862827; Fri, 18 Sep 2026 01:07:42 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:42 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 05/20] libcamera: Add the CameraDescriptor class Date: Fri, 18 Sep 2026 08:59:43 +0100 Message-ID: <20260918080734.1228227-6-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a CameraDescriptor class that describes a camera discovered during device enumeration, before any camera initialisation has taken place. It exposes the camera ID, guaranteed to be identical to the corresponding Camera::id(), and the subset of camera properties known at enumeration time. The private data records the producing pipeline handler factory, the required media devices and the sensor entity name, which will later allow the camera manager to initialise the camera from its descriptor. Signed-off-by: Naushir Patuck --- include/libcamera/camera_descriptor.h | 34 +++++ .../libcamera/internal/camera_descriptor.h | 37 +++++ include/libcamera/internal/meson.build | 1 + include/libcamera/meson.build | 1 + src/libcamera/camera_descriptor.cpp | 141 ++++++++++++++++++ src/libcamera/meson.build | 1 + 6 files changed, 215 insertions(+) create mode 100644 include/libcamera/camera_descriptor.h create mode 100644 include/libcamera/internal/camera_descriptor.h create mode 100644 src/libcamera/camera_descriptor.cpp diff --git a/include/libcamera/camera_descriptor.h b/include/libcamera/camera_descriptor.h new file mode 100644 index 000000000000..8da4b316fd42 --- /dev/null +++ b/include/libcamera/camera_descriptor.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Raspberry Pi Ltd + * + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace libcamera { + +class CameraDescriptor final : public Extensible +{ + LIBCAMERA_DECLARE_PRIVATE() + +public: + static std::shared_ptr create(std::unique_ptr d); + + const std::string &id() const; + const ControlList &properties() const; + +private: + LIBCAMERA_DISABLE_COPY(CameraDescriptor) + + CameraDescriptor(std::unique_ptr d); +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/camera_descriptor.h b/include/libcamera/internal/camera_descriptor.h new file mode 100644 index 000000000000..7c1bf52aec12 --- /dev/null +++ b/include/libcamera/internal/camera_descriptor.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Raspberry Pi Ltd + * + */ + +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace libcamera { + +class MediaDevice; +class PipelineHandlerFactoryBase; + +class CameraDescriptor::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(CameraDescriptor) + +public: + Private(); + + const PipelineHandlerFactoryBase *factory_; + std::vector> mediaDevices_; + std::string entityName_; + std::string id_; + ControlList properties_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index fd375134a5c4..b5a144e9e539 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -7,6 +7,7 @@ libcamera_internal_headers = files([ 'byte_stream_buffer.h', 'camera.h', 'camera_controls.h', + 'camera_descriptor.h', 'camera_lens.h', 'camera_manager.h', 'camera_sensor.h', diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 30ea76f9470a..91c76a10781a 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -4,6 +4,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera' libcamera_public_headers = files([ 'camera.h', + 'camera_descriptor.h', 'camera_manager.h', 'color_space.h', 'controls.h', diff --git a/src/libcamera/camera_descriptor.cpp b/src/libcamera/camera_descriptor.cpp new file mode 100644 index 000000000000..b63f4c5fc330 --- /dev/null +++ b/src/libcamera/camera_descriptor.cpp @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Raspberry Pi Ltd + * + */ + +#include "libcamera/internal/camera_descriptor.h" + +#include + +#include +#include + +/** + * \file libcamera/camera_descriptor.h + * \brief Describing cameras ahead of initialisation + */ + +namespace libcamera { + +/** + * \class CameraDescriptor + * \brief Describe a camera known to the system but not yet initialised + * + * A CameraDescriptor represents a camera discovered during device enumeration, + * before any camera initialisation has taken place. Descriptors are produced by + * CameraManager::enumerate() without acquiring any device, and carry the + * information about a camera that is available at enumeration time. + * + * The descriptor holds the camera identifier, guaranteed to be identical to + * the Camera::id() of the corresponding Camera instance, and a list of + * properties known at enumeration time. A descriptor can be passed to + * CameraManager::initialize() to create the corresponding fully initialised + * Camera. + */ + +#ifndef __DOXYGEN_PUBLIC__ +/** + * \class CameraDescriptor::Private + * \brief Base class for camera descriptor private data + */ + +/** + * \brief Construct a CameraDescriptor::Private instance + */ +CameraDescriptor::Private::Private() + : factory_(nullptr), properties_(properties::properties) +{ +} + +/** + * \var CameraDescriptor::Private::factory_ + * \brief The factory of the pipeline handler that produced this descriptor + * + * This is set by the camera manager when it collects the descriptors reported + * by a pipeline handler, and is used to create a pipeline handler for the + * camera when the descriptor is initialised. + */ + +/** + * \var CameraDescriptor::Private::mediaDevices_ + * \brief The media devices the camera is part of + * + * The media devices needed to initialise the camera. The first entry is the + * media device that identifies the camera's pipeline instance, and is used to + * route the camera to a live pipeline handler holding it. + */ + +/** + * \var CameraDescriptor::Private::entityName_ + * \brief The name of the camera's main media entity + * + * The entity that identifies the camera within its media device, for instance + * the camera sensor for a CSI receiver or the default video node for a USB + * camera. + */ + +/** + * \var CameraDescriptor::Private::id_ + * \brief The camera identifier + * \sa CameraDescriptor::id() + */ + +/** + * \var CameraDescriptor::Private::properties_ + * \brief The properties of the camera known at enumeration time + * \sa CameraDescriptor::properties() + */ +#endif /* __DOXYGEN_PUBLIC__ */ + +/** + * \brief Create a camera descriptor instance + * \param[in] d Camera descriptor private data + * + * The caller is responsible for populating the private data before creating + * the descriptor. + * + * \return A shared pointer to the newly created camera descriptor object + */ +std::shared_ptr +CameraDescriptor::create(std::unique_ptr d) +{ + return std::shared_ptr(new CameraDescriptor(std::move(d))); +} + +/** + * \brief Retrieve the ID of the camera + * + * The camera ID is identical to the Camera::id() of the Camera instance + * created by initialising this descriptor. It is guaranteed to be unique and + * stable so the same camera will have the same ID across both unplug/replug and + * boot cycles. + * + * \return ID of the camera + */ +const std::string &CameraDescriptor::id() const +{ + return _d()->id_; +} + +/** + * \brief Retrieve the properties of the camera known at enumeration time + * + * Camera properties are metadata that describe the camera. Only the subset of + * properties that can be determined at enumeration time (without initialising + * the camera) is reported here. The complete property list is available from + * Camera::properties() once the camera has been initialised. + * + * \return The list of camera properties known at enumeration time + */ +const ControlList &CameraDescriptor::properties() const +{ + return _d()->properties_; +} + +CameraDescriptor::CameraDescriptor(std::unique_ptr d) + : Extensible(std::move(d)) +{ +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 17c1b2cb3479..ba51f5ee47b3 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -2,6 +2,7 @@ libcamera_public_sources = files([ 'camera.cpp', + 'camera_descriptor.cpp', 'camera_manager.cpp', 'color_space.cpp', 'controls.cpp', From patchwork Fri Sep 18 07:59:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28318 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 52F16C3357 for ; Fri, 18 Sep 2026 08:07:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 35F406870A; Fri, 18 Sep 2026 10:07:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="eHnMXKfH"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 385B46870F for ; Fri, 18 Sep 2026 10:07:44 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-48435bcf9d9so56205f8f.1 for ; Fri, 18 Sep 2026 01:07:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718864; x=1790323664; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=iV5drikcs2jU12OmeHjXo9iBFPd9LgBhoEPYV/Xjtqc=; b=eHnMXKfHVKpvMq3dfV4gTNJrbfAvcm8BVypWZ9aeyCdY8oIjWB79EWxit7fld8FRo4 1NKzGw5OT2hEPJn/rBZBwtKhwZVp/NEtiefA6TGSaN1Dm8pTVf6mo5/QpFjHJAa6EqND 6LiBqstScv++/hoCowifRrWKRl0starn2I4HZzMAIKNcntMC6xyBQh8iAy3pcdZPAfFz jMKucLWhol3yMiDcluySijQJGxNu/TZJvBrc7xtTWsuUvGSAZS5uwdAHr+U93DX6hLcf L5UYx9u2ZzTOyi0Wha8WWh5x1k7bJqcDiaaeN9Bd8t4MIDfZMvZC7zUHouwB7zTnbdfL z3pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718864; x=1790323664; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=iV5drikcs2jU12OmeHjXo9iBFPd9LgBhoEPYV/Xjtqc=; b=Y1USHX6twdtZ3lkuyHfzcDj0ltCSGa+eiUTfbVzGrnFItFp7w0iX+sz6fNsiib9PJY FsdwhgYF1IIuSlvimSzxeRsFh1quPVmq7NegNaTUDiBmL2Vz1lqPsc5ZTv0qZRZbL6Ee wHXBl95G9obTa8Y40Oq/lQ5PykADO9oHtj/N1yLRWRkL805Ura3dG+zDfZvaC01W9vav RqLW2Jwyh4rXSr1s9zCxOEkOh6V9U3r+CrCY6t5iGenzQ/XPjd8A8rZCEHR5vP34ZIOK qv1ynlOIUuZtl/uwY7icXBOG4dtQZJ6IfdeuXUGasecZucxvJ/qlY5VnIyyzqsxiiJb1 g5Ug== X-Gm-Message-State: AFuF++lOi174pQUAHjNHE2coFAL/yIo0tyJVZO8eR6R+TysaSG2GGbyj yVIZD3PjdCrxNSpiClf3rIeOCJjHEOhZ/UoB64tGnIrwvz3Ph6sFsGpzcKUHS0SvlJTrFb9iiaM mpar8RMs= X-Gm-Gg: AYBFou2cT+jpJ6sIw+s9zZPqcD3rEodvz1LMWMfQOu4MGIhCQ9qG+sURW4tvd+4P7EZ tzNglpWLrc1RWUCx3gJxn8PrZDXtyAosNZ4BB8e6mqh/jmh+RSbm3OoLf4lFwl7DMFz0YDvylhG diJMmsfqSevKIccgYAd+n9eeye6WC8muXnFdkZOI0muwr8upEGzvAIGZGy2k51uWJdhJgGbA5lh BVLSngWf5lDrpm7VhFOaxo0/Bcpsw6cTLbO/BLSRebusQBmMddigDkVxUfxtT5Qua8z7oBivCnx G5M0Ow6i1KFOyy/OvBEdEokoMNlTL+zvk7RPI998+kUmf9SkgQuvSKNn1FPuKZMfQSjj7j55THn sbhOiF9mEsm3BVH1c/e7oheX6Ume7bXCxF9vRA/jO1WPlpbIjxRzCup5fN5M7TdvjvA3QxP7Oa7 JoSoA2yCJSqPWziRYYEwyQxmLIiGSO+u3yd5vboHBVKNavlfLpdDB3OlgfCYli7gT0vFz2yPR8B 6X7EWpGXUyJ9A8JcrvF2N7FtdOKcg/0lV4fueGo9SBsT3uurW6f/ZYIi6dme/2JIr1LsKlXYuWz MzV94B1hEqPZXN0z6SCEdJHrsExelpRqFQVn6q+4wfmfbS1pHc7vLdvxzTEpqPVlLyIpi9Re884 KNeIIuZQJJy4XQ5qo+F5wuKdWlREVmigL/OB/lasiKAI33A== X-Received: by 2002:a05:6000:4283:b0:485:8df0:dca with SMTP id ffacd0b85a97d-4871fa7286dmr1456886f8f.3.1789718863574; Fri, 18 Sep 2026 01:07:43 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:43 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 06/20] libcamera: pipeline_handler: Add survey() and createCamera() Date: Fri, 18 Sep 2026 08:59:44 +0100 Message-ID: <20260918080734.1228227-7-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add two virtual functions splitting the match() contract into a cheap enumeration half and a heavy per-camera initialisation half. survey() reports descriptors for the cameras a pipeline handler supports without acquiring media devices or altering hardware state. createCamera() initialises and registers a single camera from one of those descriptors. The base implementations report no descriptors and -ENOTSUP respectively, so pipeline handlers may keep using match() unchanged. Also add a usesMediaDevice() helper that the camera manager will use to route descriptors to pipeline handlers in a future commit. Signed-off-by: Naushir Patuck --- include/libcamera/internal/pipeline_handler.h | 5 ++ src/libcamera/pipeline_handler.cpp | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index 6a018fe42866..c22c69e70fae 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -24,6 +24,7 @@ namespace libcamera { class Camera; class CameraConfiguration; +class CameraDescriptor; class DeviceEnumerator; class DeviceMatch; class FrameBuffer; @@ -40,8 +41,12 @@ public: virtual ~PipelineHandler(); virtual bool match(DeviceEnumerator *enumerator) = 0; + virtual int survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors); + virtual int createCamera(const CameraDescriptor *descriptor); std::shared_ptr acquireMediaDevice(DeviceEnumerator *enumerator, const DeviceMatch &dm); + bool usesMediaDevice(const MediaDevice *media) const; bool acquire(Camera *camera); void release(Camera *camera); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index a803f6208c32..30079fa9f665 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -115,6 +115,62 @@ PipelineHandler::~PipelineHandler() * created, or false otherwise */ +/** + * \brief Survey the media devices for cameras this pipeline handler supports + * \param[in] enumerator The enumerator providing all media devices found in the + * system + * \param[out] descriptors The vector to which the descriptors of the cameras + * found are appended + * + * This function inspects the media devices provided by the \a enumerator and + * produces a camera descriptor for every camera that this pipeline handler + * supports. It shall not acquire any media device, open any device node, or + * alter any hardware state. + * + * Unlike match(), a single call shall report the cameras of all matching + * pipeline instances in the system, in the same order as repeated match() calls + * would register them. + * + * Pipeline handlers that do not implement this function do not support camera + * enumeration through CameraManager::enumerate(), and their cameras are only + * created through match(). + * + * A pipeline handler that supports surveying shall return 0 and append a + * descriptor to \a descriptors for every camera it finds. Pipeline handlers + * that don't support surveying return -ENOTSUP from the default implementation. + * + * \context This function is called from the CameraManager thread. + * + * \return 0 on success or a negative error code otherwise + * \retval -ENOTSUP The pipeline handler does not support surveying + */ +int PipelineHandler::survey([[maybe_unused]] const DeviceEnumerator *enumerator, + [[maybe_unused]] std::vector> *descriptors) +{ + return -ENOTSUP; +} + +/** + * \brief Create and register the camera described by a descriptor + * \param[in] descriptor The descriptor of the camera to create + * + * This function performs the initialisation of a single camera previously + * reported by survey(). It shall acquire the media devices for the camera, + * unless this pipeline handler instance has already acquired them for a + * previously created camera, perform the same per-camera initialisation as + * match(), and register the camera with the camera manager. + * + * \context This function is called from the CameraManager thread. + * + * \return 0 on success or a negative error code otherwise + * \retval -ENOTSUP The pipeline handler does not support camera creation from + * a descriptor + */ +int PipelineHandler::createCamera([[maybe_unused]] const CameraDescriptor *descriptor) +{ + return -ENOTSUP; +} + /** * \brief Search and acquire a MediaDevice matching a device pattern * \param[in] enumerator Enumerator containing all media devices in the system @@ -148,6 +204,25 @@ PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator, return media; } +/** + * \brief Check if this pipeline handler instance uses a media device + * \param[in] media The media device to check for + * + * \context This function shall be called from the CameraManager thread. + * + * \return True if \a media has been acquired by this pipeline handler + * instance, otherwise false + */ +bool PipelineHandler::usesMediaDevice(const MediaDevice *media) const +{ + for (const std::shared_ptr &m : mediaDevices_) { + if (m.get() == media) + return true; + } + + return false; +} + /** * \brief Acquire exclusive access to the pipeline handler for the process * From patchwork Fri Sep 18 07:59:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28319 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 014C7C3358 for ; Fri, 18 Sep 2026 08:07:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 013736872E; Fri, 18 Sep 2026 10:07:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="gEgODjnf"; dkim-atps=neutral Received: from mail-wr2-x0f.google.com (mail-wr2-x0f.google.com [IPv6:2a00:1450:4864:30::f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3EC3B68721 for ; Fri, 18 Sep 2026 10:07:46 +0200 (CEST) Received: by mail-wr2-x0f.google.com with SMTP id ffacd0b85a97d-482e1b429f7so64340f8f.2 for ; Fri, 18 Sep 2026 01:07:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718866; x=1790323666; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=axggi41KQRiaLtC5fkpnui7rDk7gH9OBcWZpESWdAZM=; b=gEgODjnfT2qRPK/kiPHLg2J8eQ7SzXuzVPrloNNsj6oCS8X/lqY0UPoHlwDY5ZJPkR 0HdKCoZE2ZV31ne8u5kLlFw+pGGjbSDzjwpQvY5DrYi2tGCGV6nMjfG+GKqvi/Hu54bC 1+lLqdZvnRG4+5wWzwmYzSBrtExGCwT5PreXT7DNbxc494Fvt4cxfMwlewSdSQhRUR32 kPG6WG69DV3piIlYCIHUM5AGp5KYlY94IuWMkmQQSg+0M01R6goLyo0gA4UY0jlre4Bb HRsB+BS+N46KQKF3rzlMnWn3CWJC1kfFpVh2Lv7t0dx/abxdnTWSahrL1fmacm9ioQEj 5eMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718866; x=1790323666; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=axggi41KQRiaLtC5fkpnui7rDk7gH9OBcWZpESWdAZM=; b=Tsn3eAYEhgz45LEqzMNpwAECdwN8nQHRA8WrNYmTpvx3xvjxOhMKPTlAto3/tlelfU yncSySsIKlHp9rlPucql1+wEL6NYsoXKNjMy05MhnhUFW6jZ8vJkchbmepvsU0SPKgWd m2ByFbX/CJxWpiG+226mQvpkR8C/TDPxoJufrEMmD5biUonpU+bydmqiWGOq5BYuLym2 i2JaLyUBSGp7qOybQSxt5MPPvLuQVpeBQaCxEwLjQtJbgxLLuJ+dwMNnf7rOGBXU2W5M 3wUtXqoUmU+IiF4Rzsl6Wwl7t5XjqucKzdeaKg73fhlOHqRHbsc3JUIjJ9lBBSevsO3s 9jiw== X-Gm-Message-State: AFuF++kzlY5bSW3bsBST0Kc+ejA9/q/ehdLNBpSwpjc/sp+7plHQH45R 8boy+x5M85OQgg1t5jCtEdvWzjYiQFgZ0+ey5lFcWsafgzn/fnLQAjoL2EfkWj2kS4Rl0zKNMW+ OI5OQilg= X-Gm-Gg: AYBFou1Sr2EH94b/ix1S1KNjSLjkgoV8doNuEAvMMigIm5yGByIy2OPJwUGbK/zNS3P Hw2r+O06mxb8bBTBuSdHNQfgtpfF2H8GmZzAeH5ScbTq18bUAMrJUAaC4oE8+LVDplW5UpXq0Ko SEbGc9CsMfT46LDy3jg7OKBsaW05Rk6WWEbCX0sQQLgrUqIYYl2+cLICKsc3vwGPO1BM5NEgLni tbFp/tP6zykKN3XPTkDiO0c9OoE5DzeZlZXkdGLtan6MsMZX3nQanObNOS6WqavTA+wvma8T8wO c3Zdvn09w03RPybvxuKhcRMlAHAU+dxp2rr7C/QTR6vwQibZVkryF9QlxHPp2tamZY67uU2/Qot G9SJJlASZnvhpoy+7QKLYOyn9cvVKI1ZGID1O5a1ZAUU5PhKoVG5qxH84detLqLJUD7KwLWLHqh V9Ak0bAKTy369uz3Or89wNfi23fbUvkuLYc3MlttgIBQR0t89ydUi985CYwf8UeOwkFZ51ZUhbL MhJH560pYwpH+o+CmBXll+QTHs12IQo8MdYqsV0SHJy6TflmdspkZkon87bUdnstmO7WafmdpwL h5vh5PjelY4sqZSPVV6Z0PesLDblHr6dsWFPfkVAhGM48fGdXSdVse57zyCA1odWTuhZ99l8vBL fDhpPU3GRXM0wcFFF5EmA725wjsjUZoYZwyM= X-Received: by 2002:a05:6000:41f9:b0:487:a9f:4fa2 with SMTP id ffacd0b85a97d-4871fa282e5mr1168584f8f.1.1789718865499; Fri, 18 Sep 2026 01:07:45 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:45 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 07/20] libcamera: pipeline_handler: Allow acquiring a specific media device Date: Fri, 18 Sep 2026 08:59:45 +0100 Message-ID: <20260918080734.1228227-8-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add an acquireMediaDevice() overload that acquires a given MediaDevice instance rather than the existing search mechanism. Camera creation through a descriptor already knows exactly which media devices the camera needs, so this is a simpler shortcut. This will be used in future commits to acquire a camera through the enumeration API. Signed-off-by: Naushir Patuck --- include/libcamera/internal/pipeline_handler.h | 1 + src/libcamera/pipeline_handler.cpp | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index c22c69e70fae..1f82ff998514 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -46,6 +46,7 @@ public: virtual int createCamera(const CameraDescriptor *descriptor); std::shared_ptr acquireMediaDevice(DeviceEnumerator *enumerator, const DeviceMatch &dm); + bool acquireMediaDevice(std::shared_ptr media); bool usesMediaDevice(const MediaDevice *media) const; bool acquire(Camera *camera); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 30079fa9f665..6023d028f40b 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -204,6 +204,29 @@ PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator, return media; } +/** + * \brief Acquire a specific MediaDevice + * \param[in] media The media device to acquire + * + * Acquire the media device \a media for this pipeline handler instance. As with + * the search variant, the caller shall not release the device explicitly, it + * will be automatically released when the pipeline handler is destroyed. + * + * \context This function shall be called from the CameraManager thread. + * + * \return True if the media device was acquired, or false if it was already + * in use + */ +bool PipelineHandler::acquireMediaDevice(std::shared_ptr media) +{ + if (!media->acquire()) + return false; + + mediaDevices_.push_back(std::move(media)); + + return true; +} + /** * \brief Check if this pipeline handler instance uses a media device * \param[in] media The media device to check for From patchwork Fri Sep 18 07:59:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28320 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 510DFC3359 for ; Fri, 18 Sep 2026 08:07:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C42B768721; Fri, 18 Sep 2026 10:07:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="hN3D/3io"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BD5E56849B for ; Fri, 18 Sep 2026 10:07:46 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843971bdd0so58204f8f.1 for ; Fri, 18 Sep 2026 01:07:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718866; x=1790323666; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=GuCWaR7GOfdOg7PbLYPUwC1++CD3y43VeTiFu4okMpQ=; b=hN3D/3ioPXVrJfr1kbbKRV0bI9Zf6ZiGf1G/YGI7B42ON5uELSkPno3nc0ffbWTeFY /W6FiNVHBftO7vfUSl+SV/MJBy4xRzUOdt0ue8q84yImmOcg/i00uS+cX2KIWIR7he3D hk99qyE1RWMoosbd3sF1NjnqdY1kIWT4F4KdQpryRq85agwCKRsgB7EpwWqxUXQjVfwO 0KOyC+NqRBEFfVhq4c5hk18GYETwlm71mUPyU0ew9Cj4QOjUu2BI05a7pV0XTlsjEEnI 4nqtbYWh03mi4AZp0SoD2EcF3uq/OmSifxHERWiObMmgs4Sga1LP5F4ZHYURKIxreIUS woGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718866; x=1790323666; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=GuCWaR7GOfdOg7PbLYPUwC1++CD3y43VeTiFu4okMpQ=; b=ch1JPfb+FAL9DRPsJyk23erQOl0XX+lW/s3N2h7+qqwS0ug3koWWulXD/oiBBNdSUY OYIJXooTFYDFpww+2GPf4YYYGU/E4gSskxfIOEWmUi4bmAmIztDW79XzrGFkr8IzQoeW dDQ3czFWdSqe1do+5WHN+kTR5/ZMpavM0hcZg88iWUvkMnvmsIhe8XBeTvodu9KdmKxB OKjSsR6PevcoJVPTdpydmmZPwnUE2477vU+5CPsCSzbt3g2ECTH5wowX1SvqvpYzd2aw o4XLw0NFfw2W48pPOnx0uWPT2DcXeQv9rPQpAlv8Nw45xCq8xZzvViTY+jf2uzqWjyvW LmZQ== X-Gm-Message-State: AFuF++k66Z+PnTkLWvjNLGf1U2C4f5g9fl5dOJCDL2k1yyUTcHRCnTE1 uoSJu5a3+n/bSJ1L45xlCaZ6fDl+IxCpKI8dDgwzMndcR5rU164tmfE4HBXTB0UhM6VPDnkm1jT 7b+tpvq0= X-Gm-Gg: AYBFou0Yqp7Hedmil1o+2Mt+B0BpnXPpEgErETDDmHv3AXmx3AzwjkVX4nU09uokkDu q05jxtR1EeDr3AC5uJRTVUFeWwxzjaQh2UJRWZE2cH6aOZ682xYLBDhF8ySOFFt6PANYSr6XrQK 1IUlvtYgL6ug0R02wKIe4wNWab+tYRK+0D68UOuNH4xXFlmWJn2ynwn7H1YgSaAtPNsgv1DE4D6 uNQVhvcfgt8yxaxC4zNwKs0nY3xiA3OlllSs9ye6UHOJ2NhtTKMweRlhYuQznzqJAMJTuZBb/WH ByRme2K8IXgYOFGuIkyUnQcmLGQFD+7g1tcZE8ibtFrthg2OXJF++MOfpOHB0aSDUuYmvFXURy8 i9Lux+H3VU8um5A1SgRHG8jCcB6RvlmNhqVta9o8eGCLOoKJ479SRBT1XVD0noc/zCPwGkXJw3i 8FKrt7HCkgoiUiw1fd+UeiHabqksocrJs2dm2NqAMp6rYMNoW3M1QWkKwhy6seLLmXVk7wSgaWG HCigtXQDUJ5amZSkQknfbsD6LxCjgthOE7XT21L2Nc4AeQvl9dw1ZINgc3UbXVI3LJqArnPkOwm mr71SbOL1853mH99reWlfhe+0Z5JA3asm5DwpMFSDW2fnMFqBwDXSZmtjnKVRHbKmO2IvU5pJl5 MA44rS3FIwjLQ2xv/1HB6i8xwcaWelP+cCA4= X-Received: by 2002:a05:6000:4010:b0:487:981:4e07 with SMTP id ffacd0b85a97d-4871fa86624mr1351100f8f.4.1789718866247; Fri, 18 Sep 2026 01:07:46 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:45 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 08/20] libcamera: camera_manager: Defer IPAManager construction to first use Date: Fri, 18 Sep 2026 08:59:46 +0100 Message-ID: <20260918080734.1228227-9-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The IPAManager scans, loads and validates every IPA module on disk, but none of that work is needed until a pipeline handler creates an IPA for a camera. Construct the IPAManager lazily on first access instead of unconditionally during camera manager initialisation. Access is done exclusively through the camera manager thread, so no additional locking is required. Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_manager.h | 2 +- src/libcamera/camera_manager.cpp | 24 ++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index 755928ce60a5..cfedee433857 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -44,7 +44,7 @@ public: return configuration_; } - IPAManager *ipaManager() const { return ipaManager_.get(); } + IPAManager *ipaManager(); protected: void run() override; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 0dd4e0c590a1..784ae012e280 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -91,11 +91,25 @@ void CameraManager::Private::run() cleanup(); } -int CameraManager::Private::init() +/* + * Retrieve the IPA manager, constructing it on first use. IPA modules are + * only needed once a camera is initialised, so this defers the module scan + * until that point. + */ +IPAManager *CameraManager::Private::ipaManager() { - CameraManager *const o = LIBCAMERA_O_PTR(); - ipaManager_ = std::make_unique(*o); + ASSERT(Thread::current() == this); + + if (!ipaManager_) { + CameraManager *const o = LIBCAMERA_O_PTR(); + ipaManager_ = std::make_unique(*o); + } + return ipaManager_.get(); +} + +int CameraManager::Private::init() +{ enumerator_ = DeviceEnumerator::create(); if (!enumerator_ || enumerator_->enumerate()) return -ENODEV; @@ -270,8 +284,8 @@ void CameraManager::Private::removeCamera(std::shared_ptr camera) /** * \fn CameraManager::Private::ipaManager() const - * \brief Retrieve the IPAManager - * \context This function is \threadsafe. + * \brief Retrieve the IPAManager, constructing it on first use + * \context This function shall be called from the CameraManager thread. * \return The IPAManager for this CameraManager */ #endif /* __DOXYGEN_PUBLIC__ */ From patchwork Fri Sep 18 07:59:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28321 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 7548BBE175 for ; Fri, 18 Sep 2026 08:07:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E7CA46849B; Fri, 18 Sep 2026 10:07:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="GOMJU3VV"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B352468729 for ; Fri, 18 Sep 2026 10:07:47 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-482e1b30c94so52421f8f.1 for ; Fri, 18 Sep 2026 01:07:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718867; x=1790323667; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SZFCXJgd6dwoSKX2hlSdUpN5Yp5J4RmsW6senPio2yo=; b=GOMJU3VVHKxr1MKbLosmukP8s5a8kbUK4WgAhYJXjQzYgBpZIjt1PLtYHfpshZCdz+ rzjoZkkrcr59tEI1ekEsbgXFT0hDh+MgDNnVeqRxDOkgctiAE/7jCmdYxaF51RUPQCao LqYC88q6HF8DBFG62bFhIdMyHdVMDwdMuVMCb6LOgB3K7FmWprZJwvvxoRVmJKWT3BQk R7N9yTzJTYG8b1IWGp0JQ+rTts1EQcy4U3ijZmQlnHLy+7ihb4YyesAp/eVjTd1qik/u 6qXC+DOFWu0nG3Bi5hFBYrcri66FSNjteDzvaAMCKRA+guX2I78y+aUAP7bj4diz5bPU Aaqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718867; x=1790323667; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=SZFCXJgd6dwoSKX2hlSdUpN5Yp5J4RmsW6senPio2yo=; b=ur/hWYaULDTPfPFreZq0SAsC0tnGV7nnmPuWjoypkHOENtQv8UYLbi2BrUCoH6yO5P ypHLty/pE0/XL37u+aAXTkRl5ri4kDHY6xVC31IPBssqS0KWzbvsS8noItfJSzCQYDXV ryqm/gE3044JEg8ROn4h/dcAm3uEZ7cpo0Av2L+CoqK8WwCpLJEJHstuSTyIzw2yBjHA p5LB14FdYF7SJ284v6sPqDeD8/3hPpXdXAEoPUNYCuqgyQozkNScE0clhFRvcrqshUvV wTIVnvYYsJQHiLHO9zau6+R8K+oF3i2ddLIxZv+suXZMByE+Vb8w7nv1hI9zGyGwbn2U 8EsA== X-Gm-Message-State: AFuF++lakfjXWBfkaPI6KyVSp8R+1Lj4fdpVWZ+IPZBvZNSKUQaqYB7Q hlU+jrkCWBjz1/860yfMWob17QWgZc+GCJhvD6+KZWEKcuvfOsHlmoU9pM5ElWLEeKJxYQ40eyj PjeYb+BI= X-Gm-Gg: AYBFou3zOHyr7iAVE2ci5KMfbn0bgTpehqqFVSrl8A9bkHgkFPF6LcZdwmBTOMtxW0P wECnyp66MkOLuKAPxvVcEN9zidLQHTXOiMImBnaLlORam3CMsqJL4Y/l7F5ynISouQYekI9SciD koM6skHT4GfUx8aTE6tPZp/iGCCGno9Oyxw1ADfITqAGJjj732v6aBPbt7U2s0uKed4y0laq3vr jK+a6W7P+QDs7ngiHazId0R31pwNF+0375OwYRhhknqra4tVQqYcdwBQRPSal4hq2qpf7TqW54j CsWDDL9lVqJTurXdWUm3MY3AwhTomNS+g/BYpLCq87VAbxSEttcNhyeVPAbhKoPmAtfTVXl4jFn SNJOMqFab5NG9V+X+wLl0ragEejOfEHXCfOCCbdvlVVf0O07XTyZ+z2XWovdG1wp2UDwrss12cO XJCDCKW/K01uw0NOPv42JDItApcRQU7lT0rRMra2pgNNsSIJ+y37SBF12ZiJaE25jl/6DU2w1Bo jTLNnipft9mPc23MjYvHnkQz+yQNbWGIEWjqsKbBIH1InbPM1QDkIbUdwvGFyfHu/qMlsjOoZes I5oMqKwd28pz8KGDuy9BOaLW98HSW4ehEnRk74n4vEXCA0eUqvmTEWDGzjNEv33L7fq0amwnth3 Np+VK3YwrAZm9fhcpSzONyU31xBMBqEXj8Ns= X-Received: by 2002:a05:600c:1d1b:b0:49e:7cc6:ec88 with SMTP id 5b1f17b1804b1-49fc7e00555mr12123645e9.1.1789718867130; Fri, 18 Sep 2026 01:07:47 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:46 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 09/20] libcamera: camera_manager: Track active pipeline handler instances Date: Fri, 18 Sep 2026 08:59:47 +0100 Message-ID: <20260918080734.1228227-10-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The camera manager drops its reference to a pipeline handler as soon as match() returns, leaving the instance owned solely by the Camera object. Store a vector of weak references to matched pipeline handler instances. This will allow camera initialisation from a descriptor to link to an existing pipeline handler instance rather than creating a second instance for media devices that are already acquired. Weak references leave the existing ownership and lifetime model unchanged. Add a findMatchingHandler() helper that returns the active pipeline handler instance that has acquired a given media device, pruning expired references as it goes. This will be used in a future commit. Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_manager.h | 9 +++++++ src/libcamera/camera_manager.cpp | 30 +++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index cfedee433857..89c8e4eda766 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -26,6 +26,8 @@ namespace libcamera { class Camera; class DeviceEnumerator; class IPAManager; +class MediaDevice; +class PipelineHandler; class PipelineHandlerFactoryBase; class CameraManager::Private : public Extensible::Private, public Thread @@ -53,6 +55,7 @@ private: int init(); void createPipelineHandlers(); void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory); + std::shared_ptr findMatchingHandler(const MediaDevice *media); void cleanup() LIBCAMERA_TSA_EXCLUDES(mutex_); /* @@ -70,6 +73,12 @@ private: std::unique_ptr enumerator_; + /* + * Active pipeline handler instances, accessed from the CameraManager + * thread only. + */ + std::vector> pipes_; + std::unique_ptr ipaManager_; const GlobalConfiguration configuration_; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 784ae012e280..bd9cc155b961 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -178,7 +178,37 @@ void CameraManager::Private::pipelineFactoryMatch(const PipelineHandlerFactoryBa LOG(Camera, Debug) << "Pipeline handler \"" << factory->name() << "\" matched"; + + pipes_.push_back(pipe); + } +} + +/* + * Find the active pipeline handler instance that has acquired the media device, + * if any. Expired entries are pruned from the registry as a side effect. Called + * from the CameraManager thread only. + */ +std::shared_ptr +CameraManager::Private::findMatchingHandler(const MediaDevice *media) +{ + ASSERT(Thread::current() == this); + + std::shared_ptr match; + + for (auto it = pipes_.begin(); it != pipes_.end();) { + std::shared_ptr pipe = it->lock(); + if (!pipe) { + it = pipes_.erase(it); + continue; + } + + if (!match && pipe->usesMediaDevice(media)) + match = std::move(pipe); + + ++it; } + + return match; } void CameraManager::Private::cleanup() From patchwork Fri Sep 18 07:59:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28322 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 BEFE4C3292 for ; Fri, 18 Sep 2026 08:07:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5EA6B6872F; Fri, 18 Sep 2026 10:07:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="OI0rWIoL"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9245F6872C for ; Fri, 18 Sep 2026 10:07:48 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-48435bcf9d9so56215f8f.1 for ; Fri, 18 Sep 2026 01:07:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718868; x=1790323668; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Qz5jf9P35F/52+lUmbLJSC1RBbr7eDCTO3oQLAEKVGg=; b=OI0rWIoL5NQhfvVnNtvjWpmnly/wNEy25QcJ5Jxac9AxQSkvEo0znNPa4n/DwP/uJS bFSq35dIHWrYsc0m4/DM5finWR8qKNIiw6xShrqf7Yv74KWOz4incDiPSNLns9S6uoEi CYPav/uMBr96zP2D9d5hE5k5A9RC2Yf+AlSd8GtDqGXhCquNwerx/y1i8pS05yHPAgHm TgMhIDO/KPB5ssFm0lKJiQ0wVGauj6RXZhB3cqEqNJkFqwP792wOuNKhmUKju8h5dUJb Eo/miupnvd5hvly8GkKC6aKhqd77yYrAKzhYeGo6pQAMZzDjaclFDXP5Bz3sTIdX935f rtTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718868; x=1790323668; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Qz5jf9P35F/52+lUmbLJSC1RBbr7eDCTO3oQLAEKVGg=; b=mgZNG9OLd9kqxltSw3RBIp56pE8QMKst0R9j8F4QnVX79z87xDiICuM6+H/CtnTYvu yzQjlz/wSOQq6YSMAoNJZi79ZhdUdXsIhGT1w4SStivbcN6ibaEZJ1HCDHoS//8UQCgc H2AC/NgYl+BLNUJYBfrJ2EN4BM3ngFEt1ritU46hrJms+TFkxT5A3tuU5bSocDB7SNDw 7AxZpYEu06kOAqvMmZpCOhnvN9llWCBrAvn7tyZLxZptWnHvoJa+WYPwi+O05xppYpId fPiSAvqHL7/y/yWyyJUKQXpCfbunHFtG9ivbGQLhAf7i7j/I68bkCm+wfOOo6j4F00hV dPiA== X-Gm-Message-State: AFuF++kvNSb007Gwp5jkfjkxEC9I0DVVCdUTxDvcqpHFRzP5v+4bD29g btCm1L6b6Dt2urR4JGKVjVWgRKAhnwEILoSQKqPV+hk6bbT9aeEwcIhAvWcYOVgDaju0btZgJtA JoAK92lc= X-Gm-Gg: AYBFou3llkPdfSrPrveG4Jc7KJl+YT9Du0ALI40WEePEsXOAgEifaEby5+uNCZe95vd B+AcAogDnqAZGyOsPT7LniiHQhTfSy5wgwvCaXvJ7OaJeiVssZXf+cad5Kl2MgzFu1u8vHT+mxq WhylkdKDSMZD1XATee3IVagbWA51JFFAlj3T3j/X7cE1QAn2Od+0lWCuluM7520ue5uQH6pffCJ dRTWBfwuNv5EWdo96ucyfrQwGUNXTMQnL8zIXBF4muYKYuJqeVzN988UTyPT03N3UNNjOyf0rik rnVvFXZeIW2+SkH6vWLkfyVSC5ij/hhAzlL4MG6/eMIie7wXpBum2GhTPu89VMFucVfcTdn55TY r+lzsCvnsugFunohcxj8jm6bLLuBn+7xHx9acPAqGSuIsbaZP15f2LbUpoOlCkFGhEDrEnDosh9 xjsgnLbUakGiFE7i4IifoCbvAUfgG7ZREx189ZTA6Vu37h/y8H0SyNB32OtlqGQG9RpdExcuixI dmY1ZY4G0KYanhDea74YA6cQYDkvOc7l8V4rySCZga2G4GYmHN3lbuU0x78JgrSYOy/T6a28772 4xRLON6d6grerUis7v99mzpKjcHDhkD9Clv8/6KvO0GDgVxYtTX7i1kD0axqVvDzVT9/fG2rR4r i3rHjyFRJa70GqfZJFYuOlAFtBRRtGiauLtg= X-Received: by 2002:a05:6000:41d8:b0:487:342:d144 with SMTP id ffacd0b85a97d-4871fa33199mr1219448f8f.2.1789718868015; Fri, 18 Sep 2026 01:07:48 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:47 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 10/20] libcamera: camera_manager: Extract the pipeline handler factory list Date: Fri, 18 Sep 2026 08:59:48 +0100 Message-ID: <20260918080734.1228227-11-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Split the selection of the pipeline handler factories to consider out of createPipelineHandlers() into a pipelineFactories() helper returning them in match order. This is done to avoid code duplication as the camera enumeration (added in a future commit) needs the same list. Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_manager.h | 1 + src/libcamera/camera_manager.cpp | 37 ++++++++++++++------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index 89c8e4eda766..e6df676bb655 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -53,6 +53,7 @@ protected: private: int init(); + std::vector pipelineFactories() const; void createPipelineHandlers(); void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory); std::shared_ptr findMatchingHandler(const MediaDevice *media); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index bd9cc155b961..55cf650b8e66 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -120,8 +120,15 @@ int CameraManager::Private::init() return 0; } -void CameraManager::Private::createPipelineHandlers() +/* + * Retrieve the pipeline handler factories to consider, in match order. When a + * list of preferred pipelines is defined in the configuration, the ordered list + * is exclusive. Otherwise all factories are returned in registration order. + */ +std::vector CameraManager::Private::pipelineFactories() const { + std::vector selected; + /* * \todo Try to read handlers and order from configuration * file and only fallback on environment variable or all handlers, if @@ -129,11 +136,9 @@ void CameraManager::Private::createPipelineHandlers() */ const auto pipesList = configuration().listOption({ "pipelines_match_list" }); + + /* The configured list is exclusive, skip all other factories. */ if (pipesList.has_value()) { - /* - * When a list of preferred pipelines is defined, iterate - * through the ordered list to match the enumerated devices. - */ for (const auto &pipeName : pipesList.value()) { const PipelineHandlerFactoryBase *factory; factory = PipelineHandlerFactoryBase::getFactoryByName(pipeName); @@ -143,10 +148,10 @@ void CameraManager::Private::createPipelineHandlers() LOG(Camera, Debug) << "Found listed pipeline handler '" << pipeName << "'"; - pipelineFactoryMatch(factory); + selected.push_back(factory); } - return; + return selected; } const std::vector &factories = @@ -157,12 +162,20 @@ void CameraManager::Private::createPipelineHandlers() LOG(Camera, Debug) << "Found registered pipeline handler '" << factory->name() << "'"; - /* - * Try each pipeline handler until it exhaust - * all pipelines it can provide. - */ - pipelineFactoryMatch(factory); + selected.push_back(factory); } + + return selected; +} + +void CameraManager::Private::createPipelineHandlers() +{ + /* + * Try each pipeline handler until it exhausts + * all pipelines it can provide. + */ + for (const PipelineHandlerFactoryBase *factory : pipelineFactories()) + pipelineFactoryMatch(factory); } void CameraManager::Private::pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory) From patchwork Fri Sep 18 07:59:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28323 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 44628C335A for ; Fri, 18 Sep 2026 08:08:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 99CFF6849B; Fri, 18 Sep 2026 10:07:59 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="WLXLrAy5"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D88BB68712 for ; Fri, 18 Sep 2026 10:07:49 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-48435ae9ca4so68043f8f.3 for ; Fri, 18 Sep 2026 01:07:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718869; x=1790323669; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Riz3/b6/DlGts9lPleK/2NKhUOk8MiC+6sV1WDmdEVI=; b=WLXLrAy5HcMQ8mF0koFX/qBt+hX4ZGUOu5PsPVQdATneVhnffYS89ZjXVq6xvaWrm4 3jNtRLuSZYx3SN6zNjaWHJhaVZxZerGtx/L30RpnQYu8zQ0fReKi1LfjtshTTQ86r+bX sAPj8FPOSI8rRE8B57U+wEs+JwAnSoyX+RVPoGWCRYDlBafAUqZmdq8Lnt+UN3Omt8F8 QyNeE+/rmcL/fx7JXm06D5Z0fwy/+pVFPD9BOSbjxGZI7fTvkKfT4Z0qaQCQK8De9PJV 9pv+sDW2bdL64ICqL+7myFmx+ncgHJ+qz6VcKXXO/7Gitxdxu1+nHIs986/tETmoZ4DU IVXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718869; x=1790323669; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Riz3/b6/DlGts9lPleK/2NKhUOk8MiC+6sV1WDmdEVI=; b=O1z7h3Lt7vzNKUfBFB+vV5pEXR1EWWmT5UDri1zYET0aYAOI7YEXlQt0CrIOZJfc9h 3H33HoreBM1MqXqRvJpjgRHiYct20JuuExh5qUHArSrRUxXT3JHI2OSsyzWEdQHQBpk7 wklmcNB6OwF2c6vB+tyjPi8gC4eqJc2upcHVapAXSmTpbyhPoKeTjAfu0JBuNbj3Nn1/ +PvFgWTq3x4q7JEuRmZ+7l+V+vsKL8w7hgoBeX1uDFuvb8M6+N5HUjNZxddgDKv3Bmtk ywkJm3rqz1Gh5xxWku7uVHyI8tbu0nbM9JFakPOvb6aYFMOQ49eH7O6Vr1jjB7XDEy7p 226g== X-Gm-Message-State: AFuF++lvfjxAXyX1AtHAhX3BlhLUqx+Ku7JqAQnVsXzdcJz51jE5wcMe hgKhcG8bQ1lx2Dq8GswUUJ23osQSrXk+rY4XbFqS+JAYaDijxbIwCJq8FQjcPv1+0DaxwCCfKmv 6XPlOwM0= X-Gm-Gg: AYBFou3/OiSq1XmSk1KHjwoHmxuu/aBT/c0bMCN8PAgcm4rzApudgoi75eMqodYzsqQ bD56RC56FvOUH4+kAGxNCY9EIU6OPl02+1dO2+gSxSZB87bmXOvcV5p6mf7V5jUuqj+KMzJABEF LiY5BbDiV+LKudHy7B2kPwCQT7H0Awf7dFsNtW6ZbeB1/37BcQ6tdeycPfGvQIzDH5j5kZ6rrK9 8whsQw1Moep595TxICZFWHgBGqSfQIaFftcKieQ9PLprpRbSvuDFnaNepSj46u5wcenNU2Q7WUR Bt4HLL1NZNgV4FVSirgfwJ8yxU+APl/OtFZhsGlEFBxrOEMuBzx49sOxhXtX7O2i4C+qZ0C8acb GMOOOu7Ov+v4QZCWomrRUCJk2kUeCbfADgvxruUfj2GEm7zkfB563lP7sWheFMxvWgAYdaDrX2o qzZ5WjbxNMGC+J/1xlJlZyBDm+Y+BvBpylSPs/ThrzxGHBn8b/s7kg+pLh9ru7rs2I28dz2bjuQ crUYE8fqI8PAXjzYnB5sjjtkKE4/aPx/WODnbN5q6UhRR1IkxMLU6tjZM8OBD6LQ7jw+2ZRa8K/ MMMvgywt2DNZ8h2Tgb+QWXnsjNgjjeJ+7OESeDKU9uFbUkLi+jUSIQkf8uNSnQB7ingXWGTuLpY Y9Vg/WQMAqeoI23b2BPZHcw60BaexIGYB53B4 X-Received: by 2002:a05:6000:3104:b0:487:8ba:8b4c with SMTP id ffacd0b85a97d-4871fa26da2mr1355877f8f.1.1789718869071; Fri, 18 Sep 2026 01:07:49 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:48 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 11/20] libcamera: camera_manager: Marshal work onto the camera manager thread Date: Fri, 18 Sep 2026 08:59:49 +0100 Message-ID: <20260918080734.1228227-12-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The upcoming enumerate() and initialize() interfaces are called from the application thread, but the device enumerator, pipeline handlers and media devices they operate on are all bound to the camera manager thread, so their work must be marshalled onto it. CameraManager::Private is a Thread and not an Object, so it cannot be the target of Object::invokeMethod(). Derive it from Object as well and move it to its own thread on construction. Changes in future commits can then start the thread and invoke work on it regardless of the order in which they are called. notify_all() replaces notify_one() as more than one caller can now be waiting in startThread() for the same initialisation. Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_manager.h | 6 ++- src/libcamera/camera_manager.cpp | 44 +++++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index e6df676bb655..ed90d1d471a0 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -30,7 +31,7 @@ class MediaDevice; class PipelineHandler; class PipelineHandlerFactoryBase; -class CameraManager::Private : public Extensible::Private, public Thread +class CameraManager::Private : public Extensible::Private, public Thread, public Object { LIBCAMERA_DECLARE_PUBLIC(CameraManager) @@ -54,6 +55,7 @@ protected: private: int init(); std::vector pipelineFactories() const; + int startThread() LIBCAMERA_TSA_EXCLUDES(mutex_); void createPipelineHandlers(); void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory); std::shared_ptr findMatchingHandler(const MediaDevice *media); @@ -72,6 +74,8 @@ private: bool initialized_ LIBCAMERA_TSA_GUARDED_BY(mutex_); int status_ LIBCAMERA_TSA_GUARDED_BY(mutex_); + bool started_ LIBCAMERA_TSA_GUARDED_BY(mutex_) = false; + std::unique_ptr enumerator_; /* diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 55cf650b8e66..55532179a61c 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -41,14 +41,45 @@ LOG_DEFINE_CATEGORY(Camera) CameraManager::Private::Private() : Thread("CameraManager"), initialized_(false) { + /* + * Bind this Object to its own thread, so that work can be marshalled + * onto the camera manager thread with invokeMethod(). + */ + moveToThread(this); } int CameraManager::Private::start() { - int status; + return startThread(); +} + +/* + * Start the camera manager thread if not started yet, and wait for its + * initialization to complete. Returns the initialization status. + */ +int CameraManager::Private::startThread() +{ + bool start = false; + + { + MutexLocker locker(mutex_); - /* Start the thread and wait for initialization to complete. */ - Thread::start(); + if (!started_) { + started_ = true; + initialized_ = false; + start = true; + } + } + + if (start) + Thread::start(); + + /* + * Wait for initialization to complete, whether this call started the + * thread or another one did, as the caller may otherwise proceed before + * the thread is ready. + */ + int status; { MutexLocker locker(mutex_); @@ -78,7 +109,7 @@ void CameraManager::Private::run() status_ = ret; initialized_ = true; mutex_.unlock(); - cv_.notify_one(); + cv_.notify_all(); if (ret < 0) { cleanup(); @@ -228,6 +259,11 @@ void CameraManager::Private::cleanup() { enumerator_->devicesAdded.disconnect(this); + { + MutexLocker locker(mutex_); + started_ = false; + } + /* * Release all references to cameras to ensure they all get destroyed * before the device enumerator deletes the media devices. Cameras are From patchwork Fri Sep 18 07:59:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28324 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 8EADEC335B for ; Fri, 18 Sep 2026 08:08:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9575268731; Fri, 18 Sep 2026 10:08:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Web+8TsV"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 462066871D for ; Fri, 18 Sep 2026 10:07:50 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843971bdd0so58212f8f.1 for ; Fri, 18 Sep 2026 01:07:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718870; x=1790323670; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=hQtDw7mJlL5nO9incxTRfwsaDb5FXUBKLJTT7J8VsSA=; b=Web+8TsVgxVsDBPkYM2OH/thXTjEa3jbKywumprNXsenrOrFTLPKJtD+KcJq0GcCZW 3ySb1mEEk9Bbbog7Qe4t/JhTyUdkNIFzg8M+YIH9PzCD+5O0FDndzk52SZ7wPd48BscQ W2teacMdQ32smmLipsn8tMLvodlvOlG6p6bE4wk3WG51x6vT9jr2tuhQjGFiEAbGmifa nN5K046IGaEnNPJBdnnv23Gu0pgsZ01ECqQbtDDIyAaaEpjjMekzh+PHwsDkoKeiY4zu Ax3kXultQwPaInZS+XIqz/4ADc6DgEbKd4hno5Pc005ABljX+N0BqoC298WOwiDWNx/D 93SA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718870; x=1790323670; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=hQtDw7mJlL5nO9incxTRfwsaDb5FXUBKLJTT7J8VsSA=; b=GYtgw2GIuDPA0RZAs/0cMEwsmildjlGofQvpdwaaDNHHK0y6ykaCngN3eh2GnVxjjA NOFd1EnXVfkyBp/8IJ987g1F8KjOlAM8Nr7auoEpsHegIPPrvhO/j/sOqVSsJhYBMErZ QwzdLu90Y1d8xMPTdnBiHEP6QeyJWvEYQ5Q8HEZ96nt2ub/RO2FDfLYspsPKCRQ5EiDH fj5yQZcqS/9bOADV/SMDPq1+qqgZhghnRMjuUHAhcivMQbvWdKjJipFcI/dKy+UEHXwU GaMR2w3M+OPO1IfK3cK7vjHIyhBp7IB51qhJpS5YbqO3QGFWYBPC6OHOTU0YaSvMghki gaJA== X-Gm-Message-State: AFuF++kxCnBpKG7529knauZX9GbqbIO0Jc98nrobnxXw28zL4FttNtu1 E+NESSCY4UoTaAe7ipfR/pqrd+RkoLkt49NLPEObULWzyD2PCWMe4UMRzIN6gQsu8WCg3cgWoxz inJdtTS4= X-Gm-Gg: AYBFou36hdGbQWFa7hbZ35cSzwGA1DMUx7mBuk2qCr8JPK5Q6SbmeBt3UJOY8Zy4AIA kbeHGZ9KuDjiZj2jOBQJ0QfubpaQaqKaQH9VD9HeXC0Vd8ZELY/5/i9FrWWx24A69s5CAN0h40O dRYXuHX3JWmtxTUeOlDu/DgMreVkTUH2ChHsShlMxLY6qrNP0aAoM5P/6jNQg+y73cYLh9PgxOO Se74V+DkCYMcnQekUHLvAbiMmpN2AxgkszfEnaI19dSxcwg4q3PUS8C6kQIXp5g0F9hLForvU7x q3gsuNXSId/9kSceqBNXTdl1JCrNQIyU4mKGv6nm/xN1Rjp8WEqDkGbWxM2dCFZrotKk1YWxl4n tI/yFAwnCYRm4Fft2w2p4xMSG8YOZ4CFt9+eZ/Du6q30zSryaOQi7+JXGiW0A5Txb6j/RNQgrSa +cV7k32ABYf2PoscOiSznJxPklxYuH3Kzg5KNlJmsOmp1YOunKlh0/GR2y0oY/4bMArKdEinPWP E9zyAVa6PjYgnfQ/98HMltSgIIknIdgn/YnMTMt41lTxaDp4x415Htn+g+xkWbFEbjKlyhF0qBK x05rtGp+lBt+YumTSYwvIPu0prgF3BhaKbUm6a+qL0DGZZ0mjPnXnAaF/ny7ownMmXXVkjP+MVV Gq5OHwsUeHZbE+tzXhdOAVi7KTlVpfEJDg1L4 X-Received: by 2002:a05:600c:35c2:b0:49d:1da4:511b with SMTP id 5b1f17b1804b1-49fc7dd94c0mr14519425e9.1.1789718869784; Fri, 18 Sep 2026 01:07:49 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:49 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 12/20] libcamera: camera_manager: Add CameraManager::enumerate() Date: Fri, 18 Sep 2026 08:59:50 +0100 Message-ID: <20260918080734.1228227-13-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a CameraManager::enumerate() function that returns a descriptor for every camera that the pipeline handlers can report without actually creating it. The descriptors are cached as media devices in use are skipped by later surveys. At present, Cameras cannot be created from a descriptor yet, so enumerate() is informational only. Signed-off-by: Naushir Patuck --- include/libcamera/camera_manager.h | 3 + include/libcamera/internal/camera_manager.h | 9 ++ src/libcamera/camera_manager.cpp | 98 +++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 27835500f781..5e0b95ef1aab 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -20,6 +20,7 @@ namespace libcamera { class Camera; +class CameraDescriptor; class CameraManager : public Object, public Extensible { @@ -31,6 +32,8 @@ public: int start(); void stop(); + std::vector> enumerate(); + std::vector> cameras() const; std::shared_ptr get(std::string_view id); diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index ed90d1d471a0..10e7797c4a29 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -25,6 +25,7 @@ namespace libcamera { class Camera; +class CameraDescriptor; class DeviceEnumerator; class IPAManager; class MediaDevice; @@ -39,6 +40,7 @@ public: Private(); int start(); + std::vector> enumerate(); void addCamera(std::shared_ptr camera) LIBCAMERA_TSA_EXCLUDES(mutex_); void removeCamera(std::shared_ptr camera) LIBCAMERA_TSA_EXCLUDES(mutex_); @@ -59,6 +61,11 @@ private: void createPipelineHandlers(); void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory); std::shared_ptr findMatchingHandler(const MediaDevice *media); + + std::vector> surveyThread() LIBCAMERA_TSA_EXCLUDES(mutex_); + void surveyFactory(const PipelineHandlerFactoryBase *factory) LIBCAMERA_TSA_EXCLUDES(mutex_); + std::shared_ptr initializeThread(std::shared_ptr descriptor); + void cleanup() LIBCAMERA_TSA_EXCLUDES(mutex_); /* @@ -75,6 +82,8 @@ private: int status_ LIBCAMERA_TSA_GUARDED_BY(mutex_); bool started_ LIBCAMERA_TSA_GUARDED_BY(mutex_) = false; + /* Known camera descriptors, accessed from the CameraManager thread only. */ + std::vector> descriptors_; std::unique_ptr enumerator_; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 55532179a61c..5cd0f50de12a 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -7,13 +7,17 @@ #include "libcamera/internal/camera_manager.h" +#include + #include #include #include +#include #include #include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_descriptor.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_manager.h" @@ -99,6 +103,19 @@ int CameraManager::Private::startThread() return 0; } +/* + * Enumerate the cameras in the system without initialising them, starting the + * camera manager thread on first use. Called from the application thread. + */ +std::vector> CameraManager::Private::enumerate() +{ + int ret = startThread(); + if (ret) + return {}; + + return invokeMethod(&Private::surveyThread, ConnectionTypeBlocking); +} + void CameraManager::Private::run() { LOG(Camera, Debug) << "Starting camera manager"; @@ -255,6 +272,62 @@ CameraManager::Private::findMatchingHandler(const MediaDevice *media) return match; } +/* + * Survey all pipeline handler factories and return the known camera + * descriptors. Descriptors are cached because later surveys skip the media + * devices in use by live pipeline handler instances, and would otherwise drop + * the cameras already initialised. Called on the CM thread. + */ +std::vector> CameraManager::Private::surveyThread() +{ + ASSERT(Thread::current() == this); + + for (const PipelineHandlerFactoryBase *factory : pipelineFactories()) + surveyFactory(factory); + + return descriptors_; +} + +/* + * Survey the cameras of a single pipeline handler factory and cache their + * descriptors. Called on the CM thread. + */ +void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *factory) +{ + ASSERT(Thread::current() == this); + + CameraManager *const o = LIBCAMERA_O_PTR(); + + std::shared_ptr pipe = factory->create(o); + std::vector> descriptors; + int ret = pipe->survey(enumerator_.get(), &descriptors); + if (ret == -ENOTSUP) { + /* The pipeline handler does not support surveying. */ + return; + } else if (ret < 0) { + LOG(Camera, Error) + << "Failed to survey cameras for pipeline handler " + << factory->name() << ": " << strerror(-ret); + return; + } + + for (std::shared_ptr &descriptor : descriptors) { + /* + * Record the factory that produced the descriptor, so that + * initialize() can create a pipeline handler for it. + */ + descriptor->_d()->factory_ = factory; + + auto match = [&](const auto &d) { + return d->id() == descriptor->id(); + }; + if (std::any_of(descriptors_.begin(), descriptors_.end(), match)) + continue; + + descriptors_.push_back(std::move(descriptor)); + } +} + void CameraManager::Private::cleanup() { enumerator_->devicesAdded.disconnect(this); @@ -264,6 +337,8 @@ void CameraManager::Private::cleanup() started_ = false; } + descriptors_.clear(); + /* * Release all references to cameras to ensure they all get destroyed * before the device enumerator deletes the media devices. Cameras are @@ -457,6 +532,29 @@ void CameraManager::stop() d->wait(); } +/** + * \brief Enumerate the cameras in the system without initialising them + * + * Enumerate the devices in the system and return a descriptor for every + * camera found, without initialising any camera. + * + * Only cameras of pipeline handlers that support surveying are reported. + * Cameras of other pipeline handlers are created by start() and reported by + * cameras() as before. + * + * Descriptors are reported in match order, grouped by pipeline handler. + * Descriptors of cameras removed from the system remain listed until the + * camera manager is stopped. + * + * This function starts the camera manager if it is not yet running. + * + * \return A list of descriptors for the cameras found in the system + */ +std::vector> CameraManager::enumerate() +{ + return _d()->enumerate(); +} + /** * \fn CameraManager::cameras() * \brief Retrieve all available cameras From patchwork Fri Sep 18 07:59:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28326 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 E38B7C3357 for ; Fri, 18 Sep 2026 08:08:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5D80368749; Fri, 18 Sep 2026 10:08:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="pW37OB/9"; dkim-atps=neutral Received: from mail-wm2-x11.google.com (mail-wm2-x11.google.com [IPv6:2a00:1450:4864:31::11]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9CDBC68716 for ; Fri, 18 Sep 2026 10:07:52 +0200 (CEST) Received: by mail-wm2-x11.google.com with SMTP id 5b1f17b1804b1-49d0726cdbcso101565e9.0 for ; Fri, 18 Sep 2026 01:07:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718872; x=1790323672; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=7qRtzYlYKVyrkMDHqDMLTekBE7Ym2M0Rw27LXU60ouY=; b=pW37OB/9GN6gWIveFZUEJL+pCaH89zxMzqMfmcgq6b/4BESwu7/ZmrhsgO/Raf/mEY WS5cBb3Dm4hywWpImHz6ic23AnjMy53NgMtYG/Tzi6K4JRKf+MY2H3ylsU3+H56F/93F CYJz8wd6XeM0TpohycR2Hs30zVVCtrKEyN0j78ob4VWUbm0cw4kB0hWlvY6Ia0p2I1Xf 4iPm1rHS53kO6q+8AxkP2YQVuiXEkVo/lJS0vZpbiU63wRnSOjLAPLSHR46JUJWDPkQG NDQ5CLJO8pF9CG4Gl3ByMe2UQRoIFkTmg/C/kpKLUR1BgG3yOtDpRkKucNxvD/X4WTow Rnfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718872; x=1790323672; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=7qRtzYlYKVyrkMDHqDMLTekBE7Ym2M0Rw27LXU60ouY=; b=aVlJ25O9t+QBmaA4Zbv3R5LyWhERRA8GR/j2NJ0hI+7H8ThdGu1Eutpq3kfC/Lm7a6 bKk/aYdQAipeKsvw8LwwHEAKdOxr7fxVCD48SUaEgAfwxT0lpveHodAYhU8cWwCwE1wN hFuZUdr+Tf3RLRt9Oa6Iv0QhVIdJ4ghnpjEO2JNGvbSSvmJYjJEK81Ro6USlUJqFhXC2 lYru3mPXdTrouCIS7NZoj7pUdcYZ+R4VP+zhzf9us1ROBeJNPRCqhibXWuzZJMEMRRdd 4FMmhPa1ZkNu6hW7Kj4Zr/JEWyQKaoBTEq3EAR4OUCfPmwxTMIxlXbFFUt21G0nmdCJn W4ww== X-Gm-Message-State: AFuF++kNT6Y0lhV+Idg4S2fMhdCnlhdU2kvjFReX3vwl1pZTDwi+sZCP KgALeZJjP0gQpzdx0d5U90is1PKB4/gaQkHH6cNSI9MfNWJFOCJfgdF8+MGF7J4Tla1RI/sQzTV oxg6jXQw= X-Gm-Gg: AYBFou1Lifr8G63o+nrFrSKGTMNQDw5wH8qtrB9y+Z+ed0JPuBZc6OYhImqX9FGz1n1 cGHIWIvuNM2sfsrDvpKSUe1G8k563+P7Kwr3VQyVDToumX1KIsm73AKReGIbW8nNFVUE8cYvfoE hMd4u8cKQR2whL++vBBdXns3EnVTn/skljj5C+h8QdY1iUlFQmPAjjoOhx7Nh3kcVM5DgMSpUc8 pxNFANkS/70r/mf4TAd/NVdBOjSn+QeKSU/HEyc+8o4cOdvyG08oFrQraT/dH6GCjAIyxyKyCJs ukEu2dUVU7iavQzWhk9MjZ5CGwSx0rJL4pHvGekwX3kXoLbiEIo+5haqcO11vk+PLXfM3j9oEZU sVdC52MJWZV5DYTQDf7fie4mNmzMTodiR2yuwS2ETzQz5A+aSa9CHniP4hWuRUpFCh/BR2fVYbC 3XY5KUdDS4Vu2PrkleN4inozhzua6ktCrvRA+2PTTEao6bTiwcG1w4Zy81FkpnVanNvSgTvSD5s CB5RzsKAGbKDOv0oLmqxlDYVi9oNyP3XRUco0Ixoqzdd1Etehqr/xEZJ1hhYks19DnHH67kT4uh acSav6weps1Gtb/Ei8l/xELYhxUKq4mglwINXmChAEn3Bsej88vwi/oq+MWANEmQPDUKuDEbuCZ vpR7Q0z+mMWvTCpaVuNfHS4PmARymtwzfFl8= X-Received: by 2002:a05:600c:83cd:b0:49e:6806:5712 with SMTP id 5b1f17b1804b1-49fc7ddb90emr11963925e9.2.1789718872105; Fri, 18 Sep 2026 01:07:52 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:51 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 13/20] libcamera: camera_manager: Add CameraManager::initialize() Date: Fri, 18 Sep 2026 08:59:51 +0100 Message-ID: <20260918080734.1228227-14-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a CameraManager::initialize() function creating the camera described by a descriptor returned by enumerate(). Cameras sensors behind a video mux and sharing the same CSI2-RX, share one pipeline handler instance, as they do with match(). initialize() therefore routes the descriptor to the live instance that already holds the descriptor's media device and only creates a new instance otherwise. Initialising a camera that already exists returns the existing one. Signed-off-by: Naushir Patuck --- include/libcamera/camera_manager.h | 1 + include/libcamera/internal/camera_manager.h | 1 + src/libcamera/camera_manager.cpp | 107 +++++++++++++++++++- 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 5e0b95ef1aab..65fb8a08c5d3 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -33,6 +33,7 @@ public: void stop(); std::vector> enumerate(); + std::shared_ptr initialize(const std::shared_ptr &descriptor); std::vector> cameras() const; std::shared_ptr get(std::string_view id); diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index 10e7797c4a29..d727d4859c0d 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -41,6 +41,7 @@ public: int start(); std::vector> enumerate(); + std::shared_ptr initialize(const std::shared_ptr &descriptor); void addCamera(std::shared_ptr camera) LIBCAMERA_TSA_EXCLUDES(mutex_); void removeCamera(std::shared_ptr camera) LIBCAMERA_TSA_EXCLUDES(mutex_); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 5cd0f50de12a..5ca578bd0614 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -21,6 +21,7 @@ #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/global_configuration.h" #include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" /** @@ -116,6 +117,26 @@ std::vector> CameraManager::Private::enumerate return invokeMethod(&Private::surveyThread, ConnectionTypeBlocking); } +/* + * Initialise the camera described by \a descriptor. Called from the + * application thread. + */ +std::shared_ptr +CameraManager::Private::initialize(const std::shared_ptr &descriptor) +{ + if (!descriptor) + return nullptr; + + { + MutexLocker locker(mutex_); + if (!started_ || !initialized_ || status_ < 0) + return nullptr; + } + + return invokeMethod(&Private::initializeThread, ConnectionTypeBlocking, + descriptor); +} + void CameraManager::Private::run() { LOG(Camera, Debug) << "Starting camera manager"; @@ -328,6 +349,57 @@ void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *fac } } +/* + * Create and register the camera described by \a descriptor. The camera is + * created by the live pipeline handler instance that has already acquired the + * camera's media device (if any), so that cameras sharing a pipeline instance + * join it. Otherwise a new pipeline handler instance is created. If the camera + * has already been initialised, the existing instance is returned. Called on + * the CM thread. + */ +std::shared_ptr +CameraManager::Private::initializeThread(std::shared_ptr descriptor) +{ + ASSERT(Thread::current() == this); + + CameraManager *const o = LIBCAMERA_O_PTR(); + + /* If the camera has already been created, return it. */ + std::shared_ptr camera = o->get(descriptor->id()); + if (camera) + return camera; + + const CameraDescriptor::Private *dPriv = descriptor->_d(); + const std::vector> &mediaDevices = + dPriv->mediaDevices_; + if (mediaDevices.empty()) + return nullptr; + + /* + * Route the descriptor to the live pipeline handler instance holding + * the media device, or create a new instance. + */ + std::shared_ptr pipe = + findMatchingHandler(mediaDevices.front().get()); + bool joined = !!pipe; + + if (!pipe) + pipe = dPriv->factory_->create(o); + + int ret = pipe->createCamera(descriptor.get()); + if (ret) { + LOG(Camera, Error) + << "Failed to create camera '" << descriptor->id() + << "': " << strerror(-ret); + return nullptr; + } + + if (!joined) + pipes_.push_back(pipe); + + return o->get(descriptor->id()); +} + void CameraManager::Private::cleanup() { enumerator_->devicesAdded.disconnect(this); @@ -338,6 +410,7 @@ void CameraManager::Private::cleanup() } descriptors_.clear(); + pipes_.clear(); /* * Release all references to cameras to ensure they all get destroyed @@ -462,6 +535,10 @@ void CameraManager::Private::removeCamera(std::shared_ptr camera) * will enumerate all the cameras present in the system, which can then be * listed with list() and retrieved with get(). * + * Applications that do not need every camera in the system can instead use + * enumerate(), which reports a CameraDescriptor for each camera found without + * initialising any of them, followed by initialize() for the cameras required. + * * Cameras are shared through std::shared_ptr<>, ensuring that a camera will * stay valid until the last reference is released without requiring any special * action from the application. Once the application has released all the @@ -523,7 +600,8 @@ int CameraManager::start() * * After the manager has been stopped no resource provided by the camera * manager should be consider valid or functional even if they for one - * reason or another have yet to be deleted. + * reason or another have yet to be deleted. This includes the camera + * descriptors returned by enumerate(), which can no longer be initialised. */ void CameraManager::stop() { @@ -536,7 +614,9 @@ void CameraManager::stop() * \brief Enumerate the cameras in the system without initialising them * * Enumerate the devices in the system and return a descriptor for every - * camera found, without initialising any camera. + * camera found. A camera can then be initialised from its descriptor with + * initialize(), avoiding the cost of initialising cameras the application + * will not use. * * Only cameras of pipeline handlers that support surveying are reported. * Cameras of other pipeline handlers are created by start() and reported by @@ -555,6 +635,29 @@ std::vector> CameraManager::enumerate() return _d()->enumerate(); } +/** + * \brief Initialise the camera described by \a descriptor + * \param[in] descriptor The descriptor of the camera to initialise + * + * Create and initialise the camera described by a \a descriptor returned by + * enumerate(). The returned camera is fully initialised, identical to a camera + * created by start(), and is also reported through cameras(), get() and the + * cameraAdded signal. + * + * If the camera has already been initialised, the existing instance is + * returned. + * + * \context This function may be called from any thread, but shall not be + * called concurrently with start() or stop(). + * + * \return A shared pointer to the initialised Camera, or nullptr if the + * camera could not be initialised. + */ +std::shared_ptr CameraManager::initialize(const std::shared_ptr &descriptor) +{ + return _d()->initialize(descriptor); +} + /** * \fn CameraManager::cameras() * \brief Retrieve all available cameras From patchwork Fri Sep 18 07:59:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28325 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 5EC98C3356 for ; Fri, 18 Sep 2026 08:08:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EDB4C68734; Fri, 18 Sep 2026 10:08:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="NMfTrdLw"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9102268726 for ; Fri, 18 Sep 2026 10:07:53 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843178b1c8so62460f8f.0 for ; Fri, 18 Sep 2026 01:07:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718873; x=1790323673; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SxPK7aIO8MYNrG29Gy4vLkP6fLXJuag8dJw3roVdh9E=; b=NMfTrdLweNzDjvDqA5z6Kjvvda/Lu6KnkCfD3Pzo302I5VR2JgtFxRiWn7rB6DjF7B Gjj9WgcGVYW/RCfiALork/iYUnyQ8Tth/g/9JS1fdENCC4zduxAVoBREcJvT3vrasjTl Okl+EYaguQfkNv2iEmy/hHC/biDVcDZImMcPTZ5ExD7FKgU5tMGf6eK4L/sD51V56sJl rbTQWDEXdJ915bHCWRBHo8UL0ZcB2P2qFIHSg1rT/KcQcTMZZNXfB6b6i7UgKX9xe7yF SPasuYEYTAx6EJ7oFMFBNBZLzYCbCQO+Rm/gbYnTxkoM347iBoexYF/9qqo+E1O8ZuWC 4saw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718873; x=1790323673; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=SxPK7aIO8MYNrG29Gy4vLkP6fLXJuag8dJw3roVdh9E=; b=rng2uuwva0BCX2KQ3XiYiRUb6yj0kZmagU97pPLtHe5eUY3v+4uMhjLYCQyUZEoep8 K1q+IBoMLmRB/fSzJQZb38yNf5t5VRwBwASf8B54/xApaOBvUrwKu1agrEs78012Qupd SfYzkms2MaFOphFxhxiZtZXAxVbDK0oATIhOpZV8UC99e+YeMJcP62NmoT6jYpiGG/cn DsvUgaSGQ2TddKwE3agRvxanB2/jTdtSSO8Ind8b3j9Lo7axwN/Dh0NPBMcFm5CFDMIn 1aSQOtG8JDKt+vlXsTiwrQnNuZCyyMJLcltL8e9qi7DNNSg0/lZ61FjqSMD4bSVNVkj/ xfPw== X-Gm-Message-State: AFuF++m6fPEA+DbQ3Bz7AU9xAfRGXHfMng6vRq42RWCIN5cYqwrfIadN tO3Brz89nFArNAEy/e5pQsbyOxDfMdgIMi+cQVUS4/sm4qHzPF06lFpb/19UaREFIj/syJHImBF 0b4gJAVw= X-Gm-Gg: AYBFou1KjHnhZe0ptySdBHOnSWRg0EgqYk4AAn1RWq+xnCa5A4D6gNJUIvgEPE05Nw/ mMmsyGUDQkQ8mwJOriiUNfL33JLT7EIuBXIreF4C7geINfyk/hXAN7oFU5vIFrZYO4SYojqAY10 6rgwZa7w751l1NhIraO2EMu1uLdyeXDZdXRy6nYHnUsRpAFRo0IMzdnJPsV3D8aXt3ZO5QwCiG4 iwtJnxzUa5uv2RTj3zv5mEfh/Fq+sWNsFyzOD24XCqttxk7WuSIKVdRVKuufVK1QOzzdbPwDCdJ xxliWvXoI3qHO53j4fL8iB4Mr35VPdkFQdEZ4+4DyQRYiUbrA2Ax/N/MijEeO3tyoSmQP35FiNT VaGsD/o/9LteUEI1r8zOnzsADUKkl7gSOwXWTNQIioL+LX6Pn+dxyJ/emNL3tfRWbtc1jirQFpy MMHRIEdejlNTD5sOMsHtqiVAuaMGKCBxbo7ypgMRLyl9g5rt/nq8f5IbhiuNK9E+vP1FFFPHNH2 gXe1wu1SPx0fU8wasZHjvd0PACZ/C8CTgvpY+iarTHEgHIqFWA8lbqoZEDXbadkNktHUEqGPxBN KMVqr2AuZzgksFMaSEacDb8F2CgarPyygX2EICwDIAd3fVXU5Vl1DA13zMlHfFVhKs45kAhUCuk F6grHOISlaK4klw0fU+ejliIkrPTEvseGDd0= X-Received: by 2002:a05:6000:230a:b0:487:342:d145 with SMTP id ffacd0b85a97d-4871f9fb207mr1191335f8f.2.1789718872881; Fri, 18 Sep 2026 01:07:52 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:52 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 14/20] libcamera: camera_manager: Create cameras through enumeration Date: Fri, 18 Sep 2026 08:59:52 +0100 Message-ID: <20260918080734.1228227-15-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Implement start() as the enumeration of the cameras followed by the initialisation of those not yet created. Pipeline handlers that do not support surveying continue creating their cameras through match(), and their cameras are not reported by enumerate(). Cameras are enumerated and initialised one pipeline handler at a time, so that they are created in the same order as repeated match() calls would create them. This keeps the order of cameras() unchanged for applications that do not use enumerate(). Signed-off-by: Naushir Patuck --- include/libcamera/internal/camera_manager.h | 4 +- src/libcamera/camera_manager.cpp | 64 +++++++++++++++------ 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h index d727d4859c0d..b5c55bea6e5e 100644 --- a/include/libcamera/internal/camera_manager.h +++ b/include/libcamera/internal/camera_manager.h @@ -59,12 +59,12 @@ private: int init(); std::vector pipelineFactories() const; int startThread() LIBCAMERA_TSA_EXCLUDES(mutex_); - void createPipelineHandlers(); void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory); std::shared_ptr findMatchingHandler(const MediaDevice *media); std::vector> surveyThread() LIBCAMERA_TSA_EXCLUDES(mutex_); - void surveyFactory(const PipelineHandlerFactoryBase *factory) LIBCAMERA_TSA_EXCLUDES(mutex_); + int surveyFactory(const PipelineHandlerFactoryBase *factory); + void createCameras() LIBCAMERA_TSA_EXCLUDES(mutex_); std::shared_ptr initializeThread(std::shared_ptr descriptor); void cleanup() LIBCAMERA_TSA_EXCLUDES(mutex_); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 5ca578bd0614..33ca28f76ba1 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -55,7 +55,18 @@ CameraManager::Private::Private() int CameraManager::Private::start() { - return startThread(); + int ret = startThread(); + if (ret) + return ret; + + /* + * Create every camera in the system by enumerating followed by + * initialisation. Cameras already initialised from a descriptor are + * left untouched. + */ + invokeMethod(&Private::createCameras, ConnectionTypeBlocking); + + return 0; } /* @@ -183,8 +194,7 @@ int CameraManager::Private::init() if (!enumerator_ || enumerator_->enumerate()) return -ENODEV; - createPipelineHandlers(); - enumerator_->devicesAdded.connect(this, &Private::createPipelineHandlers); + enumerator_->devicesAdded.connect(this, &Private::createCameras); return 0; } @@ -237,16 +247,6 @@ std::vector CameraManager::Private::pipeline return selected; } -void CameraManager::Private::createPipelineHandlers() -{ - /* - * Try each pipeline handler until it exhausts - * all pipelines it can provide. - */ - for (const PipelineHandlerFactoryBase *factory : pipelineFactories()) - pipelineFactoryMatch(factory); -} - void CameraManager::Private::pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory) { CameraManager *const o = LIBCAMERA_O_PTR(); @@ -311,9 +311,10 @@ std::vector> CameraManager::Private::surveyThr /* * Survey the cameras of a single pipeline handler factory and cache their - * descriptors. Called on the CM thread. + * descriptors. Returns the result of the survey, -ENOTSUP if the pipeline + * handler does not support surveying. Called on the CM thread. */ -void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *factory) +int CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *factory) { ASSERT(Thread::current() == this); @@ -324,12 +325,12 @@ void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *fac int ret = pipe->survey(enumerator_.get(), &descriptors); if (ret == -ENOTSUP) { /* The pipeline handler does not support surveying. */ - return; + return ret; } else if (ret < 0) { LOG(Camera, Error) << "Failed to survey cameras for pipeline handler " << factory->name() << ": " << strerror(-ret); - return; + return ret; } for (std::shared_ptr &descriptor : descriptors) { @@ -347,6 +348,35 @@ void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *fac descriptors_.push_back(std::move(descriptor)); } + + return 0; +} + +/* + * Create every camera in the system. For each pipeline handler, enumerate its + * cameras and initialise them, or match it when it does not support surveying. + * Looping through one pipeline handler at a time keeps the cameras in the same + * order as repeated match() calls would create them. Cameras that already exist + * are left untouched. Called on the CM thread. + */ +void CameraManager::Private::createCameras() +{ + ASSERT(Thread::current() == this); + + for (const PipelineHandlerFactoryBase *factory : pipelineFactories()) { + int ret = surveyFactory(factory); + if (ret == -ENOTSUP) { + pipelineFactoryMatch(factory); + continue; + } else if (ret < 0) { + continue; + } + + for (const std::shared_ptr &descriptor : descriptors_) { + if (descriptor->_d()->factory_ == factory) + initializeThread(descriptor); + } + } } /* From patchwork Fri Sep 18 07:59:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28327 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 C2B6FC335C for ; Fri, 18 Sep 2026 08:08:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 62C316873D; Fri, 18 Sep 2026 10:08:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="hwJmGzmw"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1BBD168729 for ; Fri, 18 Sep 2026 10:07:54 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-482e1b55da9so52035f8f.2 for ; Fri, 18 Sep 2026 01:07:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718873; x=1790323673; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=u35DaInxKIpWOADY4U2RwpXQnFNjn7InXH9ThmG/FGY=; b=hwJmGzmwGFP9pr6xQVxER0qxdLD1OBfIcmrV4+hhBKkYUdxYljM4slpoNl0m/iYfzz f/KnWCbZeDOsn68rj6BYxEh5gn6T1lPGN34lheAdmTDyXoehSb4r778HrlFY2LgalQRn tRjoGxb36mYOq6rnrREbcj5y9mrQb8u/31cJnEjCPK74FB3N0JrGZJFTbDDFgQKCmkgd zu0R/2IFj7838Y535a+KRlnFmorkkQXgTbpTo45oTCbvKUR2Rfq1TwiRjppoXN2rtNRd p2ETdr1H0i95bpjJRgoZra5xbLcnXS2WVKVqhGIifArhoqHio+4ax1pMWEY9t9qokt+s WRzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718873; x=1790323673; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=u35DaInxKIpWOADY4U2RwpXQnFNjn7InXH9ThmG/FGY=; b=H3Ja7p1+zcPbutM2jScIIykc6aEns83Ktqc5FeObzjWVPXEvEF9og8DJ41t1NN8qqZ M7IU5SAizSmC6tLBqUhe4t5Wc3FxpKU5SGeCu2EwXaWQAH0sUX1NeEuyNJ+sg/uX4W/T XiJfplt62T2AmhbgKWxfTFmO/MTSXJSGAB+CeVq2v0i0/PFTPXxTZzamhna/Bd225TbA z0EO7VfgbAryUb0Ii5HzeMj/57Qcp7WaG3m9Qj8NqT4fLrT5lE+D1Z9IsL19u8ciKYoX SqEE65r6PnQ+2oEc9BsOXDR56Ozl3tvLgkaz7WZxQBRPJf58YlqGMwyv/zuHkkWli8Yg 5N/w== X-Gm-Message-State: AFuF++nTZYv1fTB9E0MqAHelqNU5TbyFeMO8+WPUBkfUKOBkkhEOCJi0 gdNDXKHdILKfEeBG7OY4Asc940+FaCCZ399CwipZWK2l3qq9yHeDR2ex9T/bNdNpu/Mxa8gWgBq Mk7i8M+c= X-Gm-Gg: AYBFou2GhXcXClSWOE26xytypYSTDlrJeNxX8l+dHr9NIaX1i++osBhAJyMn0piC9nO W0C/a19giv5f2XNrSreXdHbzMB0u8XEgyBBu7nBJIuQjeS2SHoUhpoe9StgDKV3SZj4Ha0ohtmT q8zhgQpOvo/JXiVdpjd+tSLVWQNjxvX5C6sbcS6uN+roZmaqoLU3hutI8zuhARAjSJT3ak500Ji rgut+UcCsiwoUeZzdbnzkT50JGwSUcxW8srY9kH+QRFluTqqp0wNqCR6YElkQso6WBwR9QmK6Ei ApzbT77oLHuAD8RT4SjlEeNE8OMyHy+qdpsrQD17xHuM/jXVncpSVTeNLe90tfJorXfemSZ84Ru psf/NJUY9aPFmT3IWpGnOHs8RBAF0pSbiAggi4nLfXLDt5Gq0i29nxKEktGPqARDBC1GEgQw9ia JrGySZkJHgfcYmopU/3lUO52U7Gz3mJmmO9NiLmHHzH/GbbcX2cv4dOYm1HEAaPDoE3oiNrBxE2 KbZFIUsMVK2pnFl4FFYMz7VvcUr4c9qkgUIkdfFoHJok5kxIqxVOSYiOpI9kkeq7GEKTv1zS6Ow GYJW4FwTcQueiclVSeYxSwUHsTEHesXEBy91nwR8MCnYIOUndDH3a1sv/rmR8eOQwF1WZaCHmlg 6/FYPqE0qVSyd1atG/5nopOraitSRUVYtjVVvH5/X6HVlVw== X-Received: by 2002:a05:6000:18a3:b0:487:a7a:72cd with SMTP id ffacd0b85a97d-4871fa97755mr1486091f8f.5.1789718873536; Fri, 18 Sep 2026 01:07:53 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:53 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 15/20] libcamera: pipeline_handler: Make match() optional Date: Fri, 18 Sep 2026 08:59:53 +0100 Message-ID: <20260918080734.1228227-16-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The camera manager creates the cameras of pipeline handlers that support surveying without ever calling their match(), which leaves the match() implementations of those handlers unreachable. Give match() a default implementation matching no device, so that pipeline handlers supporting enumeration can implement survey() and createCamera() only. Signed-off-by: Naushir Patuck --- include/libcamera/internal/pipeline_handler.h | 2 +- src/libcamera/pipeline_handler.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index 1f82ff998514..4baac2fa87fa 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -40,7 +40,7 @@ public: unsigned int maxQueuedRequestsDevice = 32); virtual ~PipelineHandler(); - virtual bool match(DeviceEnumerator *enumerator) = 0; + virtual bool match(DeviceEnumerator *enumerator); virtual int survey(const DeviceEnumerator *enumerator, std::vector> *descriptors); virtual int createCamera(const CameraDescriptor *descriptor); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 6023d028f40b..7bc778b053b6 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -109,11 +109,20 @@ PipelineHandler::~PipelineHandler() * If this function returns true, a new instance of the pipeline handler will * be created and its match() function called. * + * Pipeline handlers that report their cameras through survey() have them + * created by the camera manager without this function being called, and + * therefore do not need to implement it. The default implementation matches + * no device. + * * \context This function is called from the CameraManager thread. * * \return true if media devices have been acquired and camera instances * created, or false otherwise */ +bool PipelineHandler::match([[maybe_unused]] DeviceEnumerator *enumerator) +{ + return false; +} /** * \brief Survey the media devices for cameras this pipeline handler supports From patchwork Fri Sep 18 07:59:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28328 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 8A0CFC335D for ; Fri, 18 Sep 2026 08:08:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D49B968752; Fri, 18 Sep 2026 10:08:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="imSO05m8"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C5E5868728 for ; Fri, 18 Sep 2026 10:07:54 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-482e1b30c94so52442f8f.1 for ; Fri, 18 Sep 2026 01:07:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718874; x=1790323674; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Bev3Ztd+jvcwWH3cZh1zm1zvxaVEXCcNo0HFAKbPWM8=; b=imSO05m8f5inVG5l5LQIeoltILw3FjdAZIq7Q8ew/xRm4VmmGWRjO4q9f7oKGKHaHZ tOYRGKPFEadUQeTaWiEZsPalwFXTqqKsLyue+gJNALBzZP00XepwC5Ar8qBlROT79fT/ dnM0z2IT0KWJRm2dwBmtzQxdPC4FDXE6BKm5aZWXpO9uTuD7qgVWEKCPDP6qSdw2Z/VW SBvn/di7Gcc0ov1z5g1a222F9c8aBI0vYPYyDAVrHaAeAjMEMiaBSsFpqrg9MJuIhvBI 1WjndSEblrTXJsqc0WWcoFyqwl9C0h8AhMUw0Oa+RN13NEE4Xq0pmLzqks+RKKp6G2KD INqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718874; x=1790323674; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Bev3Ztd+jvcwWH3cZh1zm1zvxaVEXCcNo0HFAKbPWM8=; b=s1Jcfd5z/K69PiZftLieYC2k2Z3YO+4EIT3Dwk8arA86GSwz7A98FDn5LZV+STzCQZ y6WrQ2nAvFMgDOPnS/VjvXC4UnRtZA0EvVx7TgV4q4vDBRPBwJNAnOGWHdYDWnKxDAcw kBH9g22e+9vnK9q+E7/3nyye++mvoaxwj5rX9pGiBVkRi9kToGV4ojndw/idGBhOdcxc xmcA4BBnlxvT9xVKX7G1UF2GpfagupLCGafZPtbbqyTh7y6UQpBUVgoDvJlxYQZg20LR lwVgucHQFD03Oek+C8pQ9KvY94RD9hsl51YNyU+hXaz8iJqcYmfqQ6om2uA3F2SaHxHd kN2A== X-Gm-Message-State: AFuF++kuurxiNHABffHAynhSr1dsIL6xEXdWi9Veglcc+h+RZboXblje szrJS+4M+UZKP17UaYOMvOBarCATUfRH7U5nEGdDTN+Azwb4G5cYIHXumtzwK8PNYvZwuZj0NWF YyOn3yzw= X-Gm-Gg: AYBFou0iZImGSfinoW8hxQ3SSYEAhQb7tDbGE6IppIK0ZsfMDDHB+UmqkCtyehC9GHl 1rkP7PSmxO23Jls4WNbb5qFmrWNnzfqhmY51k9Hag6Ej80WI1JcWGnqQjc4LzeKMdZbE7OB3fIL DfUtFogMsy88ey1bUk4LqO8OT4wMunW+AMHBrfhtGujmDvuU+MJB+K0dvxMPs9lfe7UrZlSGMg2 HZVu6mqC6gKHoTaMkh/4fyw/fJgofHscoJ+NuD00Awq4NUdUpQPLdQZ/TuJAY3+pAmMGXjFMNyL L6BaWWOs0DDtQj/454ZQfXf4TlMgrnilQTJeqeadzkX18e39SLZ/rv2+27pMXX9JNe+/7HZ6FR8 zUslF7vb+ZwTWch3pzuqm7nlJ7q2x0DP2sV2Dy03s3gu6CVF+JP/m4lVyA7FIv7GHG4BAQiX5UR WUd4xR09HoOj2FUk5klKbLmcMl3rp7iuQEGmdFnpzuK/ZD5OByQuBLst61WIFhovUIC2te0suBj FwvsNsd731dyKRiv5nZoo8LDi6YYXXFdLOHU/1GYGgt+Z8n8dJ4IfMjGJnJ7lc9vKLK/2OUeBKh 6WFKzDGgTy5QBkwP3KIZASBmAjE7yBngYsjhT88c1a99Ao7jhST4HNvCX0L/1v60/iRkx33NuNu QyO9dQA42HsXFK/+mdooSafQDCxo4YvqVNr4= X-Received: by 2002:a05:6000:1a8c:b0:487:803:ba34 with SMTP id ffacd0b85a97d-4871faa2329mr1269116f8f.6.1789718874180; Fri, 18 Sep 2026 01:07:54 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:53 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 16/20] pipeline: rpi: Add platform helpers for camera enumeration Date: Fri, 18 Sep 2026 08:59:54 +0100 Message-ID: <20260918080734.1228227-17-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The vc4 and pisp match() implementations differ only in the media devices they look for, the frontend link name, the camera data they allocate and, for pisp, a check of the PiSP variant. Expose these differences as platforms specific helpers in PipelineHandlerBase and implement them for both vc4 and pisp. In a subsequent commit, a generic survey() and createCamera() can be built on them. The existing match() implementations are left untouched. Signed-off-by: Naushir Patuck --- .../pipeline/rpi/common/pipeline_base.h | 12 ++++ src/libcamera/pipeline/rpi/pisp/pisp.cpp | 69 +++++++++++++++++++ src/libcamera/pipeline/rpi/vc4/vc4.cpp | 22 ++++++ 3 files changed, 103 insertions(+) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index ca12a4875faf..16c509d6a769 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -245,6 +245,18 @@ protected: void mapBuffers(Camera *camera, const BufferMap &buffers, unsigned int mask); + /* Platform-specific helpers used by survey() and createCamera(). */ + virtual DeviceMatch frontendMatch() const = 0; + virtual DeviceMatch backendMatch() const = 0; + virtual const char *frontendLinkName() const = 0; + virtual bool platformSupported([[maybe_unused]] const MediaDevice *frontend, + [[maybe_unused]] const MediaDevice *backend) const + { + return true; + } + virtual std::unique_ptr + allocateCameraData(MediaDevice *frontend, MediaDevice *backend) = 0; + virtual int platformRegister(std::unique_ptr &cameraData, std::shared_ptr unicam, std::shared_ptr isp) = 0; diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp index 159c6b05ee7b..34de291f4092 100644 --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp @@ -869,12 +869,81 @@ private: return static_cast(camera->_d()); } + DeviceMatch frontendMatch() const override + { + DeviceMatch cfe("rp1-cfe"); + cfe.add("rp1-cfe-fe-image0"); + cfe.add("rp1-cfe-fe-stats"); + cfe.add("rp1-cfe-fe-config"); + return cfe; + } + + DeviceMatch backendMatch() const override + { + DeviceMatch isp("pispbe"); + isp.add("pispbe-input"); + isp.add("pispbe-config"); + isp.add("pispbe-output0"); + isp.add("pispbe-output1"); + isp.add("pispbe-tdn_output"); + isp.add("pispbe-tdn_input"); + isp.add("pispbe-stitch_output"); + isp.add("pispbe-stitch_input"); + return isp; + } + + const char *frontendLinkName() const override + { + return "csi2"; + } + + bool platformSupported(const MediaDevice *frontend, + const MediaDevice *backend) const override + { + const libpisp::PiSPVariant &variant = + libpisp::get_variant(frontend->hwRevision(), + backend->hwRevision()); + return variant.NumFrontEnds() && variant.NumBackEnds(); + } + + std::unique_ptr + allocateCameraData(MediaDevice *frontend, MediaDevice *backend) override; + int allocateBuffers(Camera *camera) override; int platformRegister(std::unique_ptr &cameraData, std::shared_ptr cfe, std::shared_ptr isp) override; }; +std::unique_ptr +PipelineHandlerPiSP::allocateCameraData(MediaDevice *frontend, MediaDevice *backend) +{ + const libpisp::PiSPVariant &variant = + libpisp::get_variant(frontend->hwRevision(), + backend->hwRevision()); + if (!variant.NumFrontEnds() || !variant.NumBackEnds()) { + LOG(RPI, Error) << "Unsupported PiSP variant"; + return {}; + } + + std::unique_ptr cameraData = + std::make_unique(this, variant); + PiSPCameraData *pisp = + static_cast(cameraData.get()); + + pisp->fe_ = SharedMemObject + ("pisp_frontend", true, pisp->pispVariant_); + pisp->be_ = SharedMemObject + ("pisp_backend", BackEnd::Config({}), pisp->pispVariant_); + + if (!pisp->fe_.fd().isValid() || !pisp->be_.fd().isValid()) { + LOG(RPI, Error) << "Failed to create ISP shared objects"; + return {}; + } + + return cameraData; +} + bool PipelineHandlerPiSP::match(DeviceEnumerator *enumerator) { constexpr unsigned int numCfeDevices = 2; diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp index 200e3b58724a..f6af31072eee 100644 --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp @@ -158,6 +158,28 @@ private: return static_cast(camera->_d()); } + DeviceMatch frontendMatch() const override + { + return DeviceMatch("unicam"); + } + + DeviceMatch backendMatch() const override + { + return DeviceMatch("bcm2835-isp"); + } + + const char *frontendLinkName() const override + { + return "unicam-image"; + } + + std::unique_ptr + allocateCameraData([[maybe_unused]] MediaDevice *frontend, + [[maybe_unused]] MediaDevice *backend) override + { + return std::make_unique(this); + } + int allocateBuffers(Camera *camera) override; int platformRegister(std::unique_ptr &cameraData, std::shared_ptr unicam, From patchwork Fri Sep 18 07:59:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28329 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 42FECC335E for ; Fri, 18 Sep 2026 08:08:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5E5C768757; Fri, 18 Sep 2026 10:08:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="P1zdt1eI"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A6B1568715 for ; Fri, 18 Sep 2026 10:07:55 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-482e1b55da9so52039f8f.2 for ; Fri, 18 Sep 2026 01:07:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718875; x=1790323675; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pFv9ns3rAhbj1IiOwzZSokLG0z3FvA5oTFaFhxT6bQQ=; b=P1zdt1eINwLtYADnQCY4xWnXwCxUtv+x0ow78uGJx4sf4Bb2l4mm+C4guHN+DfbqNM MlzYbR+QZO8okDnhdiJ2PnCurfK+Cv+fRXSpNNN9flfjgJurZwgm5Mr58pUD5VJTYPNu 1giLgfqa4nkPYUf8u9hZmT+/QBLkB0TvqPyv6DdcQj/1HRQiGFEyKvcBabjCLhH1SIob 7eZ+GhOTTh7F9ViawVnTXO7ZUuQIVVFB8MBguv64B2QSYy9S06w9eMkddmpC3Nu4WrbF JNG+Nsw78f3lsoTo6WGrBpClXGCGhfbVWLa/fqvh7PaFi8MGw7alcf4HkSZw8+5LDwJn XhPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718875; x=1790323675; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=pFv9ns3rAhbj1IiOwzZSokLG0z3FvA5oTFaFhxT6bQQ=; b=s+fSfpvjpJ/obqFFVTLw2y/0L2DQaTZxJrOJ6/5ZmIserUqa9Z7GkeSpZhG7hp4f/y APJCb0doBHaXGzmpBRCKcuxnnvoHBPOuiw2uXpVRSUSzJdTxo4GFlcBiO/8OhsJLcyz9 A89GpBhgwMyvxUI8ZXIy0i/1tr2hNwC61bxgbjsC4RtsyuyTMulhOx5Obu9unE1qldsM J7t4eGUqePkoozQChEXxsfSlx7lBeVvAZB79HhW6GVB1eMxSpMEh53Qgj+hwlEp4EYOA XdCyd3qCSGfukmMkW8Ob+VkZpmKEfWBnCEb3MVQBFTkIYH08BIhCjBUiHe7qOTX1qJtP KBbQ== X-Gm-Message-State: AFuF++l7QeBd3K2IQXKZY+Keq6UfgcjRie+oAXyFMZnSEgZnXW8CotwV GeUzgJIgjeg/IrDZbhSGAMQsgUMEjoGUx2hmJ3NKDb+lGhH+Y2xoAQWOzgeEfV5+IwklU9gnYMP cJ8mG+So= X-Gm-Gg: AYBFou2yC7D5ZRbXMn41eDu/ymbGvqX2j1n1lN47nS9MieT5fx3OQLh7qsbnmUL065m UJQDl7uHTwNDRh9U4BZfIkLr1hfxXYntZK5/1i4VipDy5LeFFVM87m9HiGB0ww2oLml9cUcWI+j 4+VNB8bZrAJSp1nrvie0Uz4zSWFkCOst+sHNOEOLNdU0zJrJA3DYhc1uthLo6zJk+it5+BvHzAT CPTSH7gCzPNcHuxgqswrK5o4NS+76hrIohDmqk94ntFhijstSKovABQpeWyasmQPMXp8b2PrtCk 9erdM/74zxngXuDfY12KZHqe+nSbXXPWoY2OsMS9cDmQfl7JC01a0B0iT87iaTmBnkdrpkRMwLM rUxF6Xf/g+YyL4qfaVP/8bQWhnaz1FhKHNwYU+HLIJO2Yn2rlOU/vNJj3VS7MMgTBbhwcvGJx+0 hwB6QnlFxotydKtF6rRw3BWSg45tktm+gNtQ3I8MaLqArQKdYCkFZaF416FQ8GxiL2LwtMyUx9D p4ggixLHvzxqDX59UoL2JSN4cpHrIRfg5gWvBTbgMde9o5Wpv+/ttT1cJK8jMyvr78FCEa9ih/C MUwDUXTGd3HjDRcxt6hNiK0xQZsL3dtzu00ZXRkNU/dJ6fg5/Dx1e0OxHk2DcOkemjjQL1l0gt2 NyF9aWl6lt7yh3cSzi8ZXmpb+aGB/LlmIw/XsdXDyd8zZIw== X-Received: by 2002:a05:6000:4283:b0:487:aff:995e with SMTP id ffacd0b85a97d-4871fa7433fmr1336913f8f.3.1789718875004; Fri, 18 Sep 2026 01:07:55 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:54 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 17/20] pipeline: rpi: Implement survey() and createCamera() Date: Fri, 18 Sep 2026 08:59:55 +0100 Message-ID: <20260918080734.1228227-18-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Implement survey() and createCamera() for the vc4 and pisp platforms. survey() pairs each frontend with a backend in match order and reports a descriptor for every sensor entity on the frontend. The camera ID comes from sysfs and the model from the entity name, so no device is acquired or opened. createCamera() creates a single camera from a descriptor. It acquires the media devices unless this pipeline handler instance already holds them, so that cameras behind a shared frontend end up in the same instance. The vc4 and pisp match() implementations are no longer used and removed. Signed-off-by: Naushir Patuck --- .../pipeline/rpi/common/pipeline_base.cpp | 99 +++++++++++++++++++ .../pipeline/rpi/common/pipeline_base.h | 4 + src/libcamera/pipeline/rpi/pisp/pisp.cpp | 90 ----------------- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 57 ----------- 4 files changed, 103 insertions(+), 147 deletions(-) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 71f3f26a4ffe..57188b8343e3 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -15,11 +15,15 @@ #include #include +#include #include #include #include +#include "libcamera/internal/camera_descriptor.h" #include "libcamera/internal/camera_lens.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/yaml_parser.h" @@ -793,6 +797,101 @@ int PipelineHandlerBase::queueRequestDevice(Camera *camera, Request *request) return 0; } +int PipelineHandlerBase::survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) +{ + /* + * Pair each available frontend instance with a backend instance, in + * the same order as match() acquires them, and report a descriptor + * for every sensor entity attached to the frontend. No device is + * acquired or opened. + */ + std::vector> frontends = + enumerator->searchAll(frontendMatch()); + std::vector> backends = + enumerator->searchAll(backendMatch()); + + for (unsigned int i = 0; i < frontends.size() && i < backends.size(); i++) { + MediaDevice *frontendDevice = frontends[i].get(); + MediaDevice *backendDevice = backends[i].get(); + + if (!platformSupported(frontendDevice, backendDevice)) + continue; + + for (MediaEntity *entity : frontendDevice->entities()) { + if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) + continue; + + std::string id = CameraSensorFactoryBase::generateId(entity); + if (id.empty()) { + LOG(RPI, Warning) + << "Failed to generate an ID for sensor " + << entity->name(); + continue; + } + + std::string model = + V4L2Subdevice::modelFromEntityName(entity->name()); + + auto data = std::make_unique(); + data->id_ = std::move(id); + data->properties_.set(properties::Model, + utils::toAscii(model)); + data->mediaDevices_ = { frontends[i], backends[i] }; + data->entityName_ = entity->name(); + + descriptors->push_back(CameraDescriptor::create(std::move(data))); + } + } + + return 0; +} + +int PipelineHandlerBase::createCamera(const CameraDescriptor *descriptor) +{ + const CameraDescriptor::Private *data = descriptor->_d(); + + if (data->mediaDevices_.size() != 2) + return -EINVAL; + + const std::shared_ptr &frontendDevice = data->mediaDevices_[0]; + const std::shared_ptr &backendDevice = data->mediaDevices_[1]; + + /* + * Acquire the media devices, unless this pipeline handler instance + * already holds them because a camera behind the same frontend has + * been created before. + */ + if (!usesMediaDevice(frontendDevice.get())) { + if (!acquireMediaDevice(frontendDevice)) + return -EBUSY; + if (!acquireMediaDevice(backendDevice)) + return -EBUSY; + } + + MediaEntity *sensorEntity = nullptr; + for (MediaEntity *entity : frontendDevice->entities()) { + if (entity->name() == data->entityName_) { + sensorEntity = entity; + break; + } + } + + if (!sensorEntity) { + LOG(RPI, Error) << "Sensor entity '" << data->entityName_ + << "' not found"; + return -ENODEV; + } + + std::unique_ptr cameraData = + allocateCameraData(frontendDevice.get(), backendDevice.get()); + if (!cameraData) + return -EINVAL; + + return registerCamera(cameraData, frontendDevice, frontendLinkName(), + backendDevice, sensorEntity); +} + int PipelineHandlerBase::registerCamera(std::unique_ptr &cameraData, std::shared_ptr frontend, const std::string &frontendName, diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index 16c509d6a769..6cc1684c74b0 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -223,6 +223,10 @@ public: const V4L2SubdeviceFormat &format, BayerFormat::Packing packingReq); + int survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) override; + int createCamera(const CameraDescriptor *descriptor) override; + std::unique_ptr generateConfiguration(Camera *camera, std::span roles) override; int configure(Camera *camera, CameraConfiguration *config) override; diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp index 34de291f4092..221e058222ea 100644 --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp @@ -861,8 +861,6 @@ public: { } - bool match(DeviceEnumerator *enumerator) override; - private: PiSPCameraData *cameraData(Camera *camera) { @@ -944,94 +942,6 @@ PipelineHandlerPiSP::allocateCameraData(MediaDevice *frontend, MediaDevice *back return cameraData; } -bool PipelineHandlerPiSP::match(DeviceEnumerator *enumerator) -{ - constexpr unsigned int numCfeDevices = 2; - - /* - * Loop over all CFE instances, but return out once a match is found. - * This is to ensure we correctly enumerate the camera when an instance - * of the CFE has registered with media controller, but has not registered - * device nodes due to a sensor subdevice failure. - */ - for (unsigned int i = 0; i < numCfeDevices; i++) { - DeviceMatch cfe("rp1-cfe"); - cfe.add("rp1-cfe-fe-image0"); - cfe.add("rp1-cfe-fe-stats"); - cfe.add("rp1-cfe-fe-config"); - std::shared_ptr cfeDevice = acquireMediaDevice(enumerator, cfe); - - if (!cfeDevice) { - LOG(RPI, Debug) << "Unable to acquire a CFE instance"; - break; - } - - DeviceMatch isp("pispbe"); - isp.add("pispbe-input"); - isp.add("pispbe-config"); - isp.add("pispbe-output0"); - isp.add("pispbe-output1"); - isp.add("pispbe-tdn_output"); - isp.add("pispbe-tdn_input"); - isp.add("pispbe-stitch_output"); - isp.add("pispbe-stitch_input"); - std::shared_ptr ispDevice = acquireMediaDevice(enumerator, isp); - - if (!ispDevice) { - LOG(RPI, Debug) << "Unable to acquire ISP instance"; - break; - } - - /* - * The loop below is used to register multiple cameras behind - * one or more video mux devices that are attached to a - * particular CFE instance. Obviously these cameras cannot be - * used simultaneously. - */ - unsigned int numCameras = 0; - for (MediaEntity *entity : cfeDevice->entities()) { - if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) - continue; - - const libpisp::PiSPVariant &variant = - libpisp::get_variant(cfeDevice->hwRevision(), - ispDevice->hwRevision()); - if (!variant.NumFrontEnds() || !variant.NumBackEnds()) { - LOG(RPI, Error) << "Unsupported PiSP variant"; - break; - } - - std::unique_ptr cameraData = - std::make_unique(this, variant); - PiSPCameraData *pisp = - static_cast(cameraData.get()); - - pisp->fe_ = SharedMemObject - ("pisp_frontend", true, pisp->pispVariant_); - pisp->be_ = SharedMemObject - ("pisp_backend", BackEnd::Config({}), pisp->pispVariant_); - - if (!pisp->fe_.fd().isValid() || !pisp->be_.fd().isValid()) { - LOG(RPI, Error) << "Failed to create ISP shared objects"; - break; - } - - int ret = registerCamera(cameraData, cfeDevice, "csi2", - ispDevice, entity); - if (ret) - LOG(RPI, Error) << "Failed to register camera " - << entity->name() << ": " << ret; - else - numCameras++; - } - - if (numCameras) - return true; - } - - return false; -} - int PipelineHandlerPiSP::allocateBuffers(Camera *camera) { PiSPCameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp index f6af31072eee..a638fe6de052 100644 --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp @@ -150,8 +150,6 @@ public: { } - bool match(DeviceEnumerator *enumerator) override; - private: Vc4CameraData *cameraData(Camera *camera) { @@ -186,61 +184,6 @@ private: std::shared_ptr isp) override; }; -bool PipelineHandlerVc4::match(DeviceEnumerator *enumerator) -{ - constexpr unsigned int numUnicamDevices = 2; - - /* - * Loop over all Unicam instances, but return out once a match is found. - * This is to ensure we correctly enumrate the camera when an instance - * of Unicam has registered with media controller, but has not registered - * device nodes due to a sensor subdevice failure. - */ - for (unsigned int i = 0; i < numUnicamDevices; i++) { - DeviceMatch unicam("unicam"); - std::shared_ptr unicamDevice = acquireMediaDevice(enumerator, unicam); - - if (!unicamDevice) { - LOG(RPI, Debug) << "Unable to acquire a Unicam instance"; - continue; - } - - DeviceMatch isp("bcm2835-isp"); - std::shared_ptr ispDevice = acquireMediaDevice(enumerator, isp); - - if (!ispDevice) { - LOG(RPI, Debug) << "Unable to acquire ISP instance"; - continue; - } - - /* - * The loop below is used to register multiple cameras behind one or more - * video mux devices that are attached to a particular Unicam instance. - * Obviously these cameras cannot be used simultaneously. - */ - unsigned int numCameras = 0; - for (MediaEntity *entity : unicamDevice->entities()) { - if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) - continue; - - std::unique_ptr cameraData = std::make_unique(this); - int ret = RPi::PipelineHandlerBase::registerCamera(cameraData, - unicamDevice, "unicam-image", - ispDevice, entity); - if (ret) - LOG(RPI, Error) << "Failed to register camera " - << entity->name() << ": " << ret; - else - numCameras++; - } - - if (numCameras) - return true; - } - - return false; -} - int PipelineHandlerVc4::allocateBuffers(Camera *camera) { Vc4CameraData *data = cameraData(camera); From patchwork Fri Sep 18 07:59:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28330 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 6EA66C335F for ; Fri, 18 Sep 2026 08:08:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EEAAC6849B; Fri, 18 Sep 2026 10:08:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="pxWVWZFm"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7A10568712 for ; Fri, 18 Sep 2026 10:07:56 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-484399babcaso57297f8f.0 for ; Fri, 18 Sep 2026 01:07:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718876; x=1790323676; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=3a6X9cuXND1R7NoyBs8TDwFSx68X5NaB1JmGtl9PbSo=; b=pxWVWZFmqODXoUwh/qqcCFtobN3UWYcdll0+h9nNicSO849BZ87fOByHwRnN1g7gr9 G/GB4O7Uf9VWClHsE2F6h6ZqfphW40HZchz2goij9qPKMZ1DP1cEXYrO9xlJ1Efj9g5/ NxHGLUQl1OzRAMogmeI7ezkevhvfM6Rdj8QRdYF+8jc7E4RKHGcDMLQSLhoRhawAuucA y7AgZYN3vmGoh2Eu5k4RS2aHa0WUO5O9aw2seOCAVFjrCgm3lqB/lhs+Xlk5ylJzH9QZ Ncr96uVBRyYI7aOkYIO1ix6j5yelCnF7YhxBVtGQvQpPXfYNcgVstVJ++H92icsaW6IF srVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718876; x=1790323676; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=3a6X9cuXND1R7NoyBs8TDwFSx68X5NaB1JmGtl9PbSo=; b=VcmkZHBaWYVLi+yQdjO9r5NxO+vHPY23uFoiggXNj02VlY7ml+HOsJ1DzCP89ll72A awHh6kDyYOu8mWLNUicfk3KDL57PobLl0OdM0DhPnuXIUzTQUqv1SfhhaKgEDYDeskg2 4EqErcK9KOJ8R8nB+bZqCU5AM2sMuAU4ZMIowXn630qDw/Xej5uNXhzneqlq4O/HzW2s 1MdGuB0ts3/ByYNCwUU5OA2brO649wL8moyXBF9YVlqIZpuD36XPKaUkMMRzM7i5AFM0 kvgBFWjpjj5caXBqaoOqa04TmCgOqMY1/ZpwX6fvdZLDBcVbx05odC6r2qA0ytWVRn8r l4pw== X-Gm-Message-State: AFuF++nVK7T4YlIxE+fhvYnOProH6qk0n8FDVmwhV9pubw5J10Sfo77X wAolIdMbOc76cjHBWDSV9voyLfIW4Wo/UROahoOerQsTw8TjbPT1LpzqjPdOsmQL3MfVWmlYIBt yApVPjkk= X-Gm-Gg: AYBFou3c6w1mwGqBdYWX5a3FFJCiqfET1CT4ISEWjsDHJrGdJNLQ7L2L9KoLcL7OqH7 P8f0ii32Zi0AMMYX9QmB/ikOk5pj5yyYRUfLjFTKFvgobLqJ8oQfZ5BpaiHpxP9xKOx8TVlPJp3 G4Ii3lvpDAkzPv9fOdqNNe6Ijjy2FhW0Fd0w16rsop52RkmmhoLMeO86lS+F1Q95GRScQSYdllZ HlvcULBwjjOkPFp3tka98nTw9YbRK476dlJmkmA5fUIOgdq86uiN/7xXMGM1y1Efmw1RFpMqD9W JfKkMfTxnUo8YsicrR3zZuLFUjXh9sYJW7wbj+IeVfo+IclCcGLEfxEJyfZyqZyYueBJKmmsWXP AXoJer+lArG5tC/zQRaEttSEj8vhTMVymtvlTrwdsdTjYi9ggZX67j09sZAJrkm+x3pAku/OdA/ MspTis556a32Hz1z7IyW0RkUDISLlojH8OfN+7W1O1SZF4YSIAIm5+6WPE6DEfIE5x2BKozhcva peWGebHrYMyZYyizw51nQp4lyXLZ7i2oeSFSUhqGU1BghAZw4sXPF33F7EjnZoQBeY28GXFJNsC muglU9gfVNOZn0LqoIIEkAj+la8BTIMzdaRdAB4wySOJb9cc//D3cTVv3rOVjFVlwp6aSowdywc zWVfpfndAWS4NQnwnuCI4713Ad6sey8/ZFS3ojyuFLDp9pw== X-Received: by 2002:a05:6000:400e:b0:487:fa3:7630 with SMTP id ffacd0b85a97d-4871faae24cmr1315832f8f.6.1789718875913; Fri, 18 Sep 2026 01:07:55 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:55 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 18/20] pipeline: uvcvideo: Factor out camera ID generation Date: Fri, 18 Sep 2026 08:59:56 +0100 Message-ID: <20260918080734.1228227-19-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Extract the camera ID computation from UVCCameraData::generateId() into a helper taking the sysfs path of the device backing the video node. The path is available from the media entity device numbers without opening the video device, which will allow generating the same ID when surveying cameras at enumeration time. This will be used in a subsequent commit when adding the camera survey mechanism to the uvcvideo pipeline handler. Signed-off-by: Naushir Patuck --- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 127 ++++++++++--------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 4b09bd6e2f7c..5fa1bdb0ff0e 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -129,6 +129,72 @@ std::optional v4l2ToExposureMode(int32_t x) } } +/* + * Generate the camera ID from the sysfs path of the UVC device video node. The + * path is derived from the device numbers of the media entity, so the ID can be + * generated without opening the video device. + */ +std::string generateIdFromPath(const std::string &path) +{ + /* Create a controller ID from first device described in firmware. */ + std::string controllerId; + std::string searchPath = path; + while (true) { + std::string::size_type pos = searchPath.rfind('/'); + if (pos <= 1) { + LOG(UVC, Error) << "Can not find controller ID"; + return {}; + } + + searchPath = searchPath.substr(0, pos); + + controllerId = sysfs::firmwareNodePath(searchPath); + if (!controllerId.empty()) + break; + } + + /* + * Create a USB ID from the device path which has the known format: + * + * path = bus, "-", ports, ":", config, ".", interface ; + * bus = number ; + * ports = port, [ ".", ports ] ; + * port = number ; + * config = number ; + * interface = number ; + * + * Example: 3-2.4:1.0 + * + * The bus is not guaranteed to be stable and needs to be stripped from + * the USB ID. The final USB ID is built up of the ports, config and + * interface properties. + * + * Example 2.4:1.0. + */ + std::string usbId = utils::basename(path.c_str()); + usbId = usbId.substr(usbId.find('-') + 1); + + /* Creata a device ID from the USB devices vendor and product ID. */ + std::string deviceId; + for (const char *name : { "idVendor", "idProduct" }) { + std::ifstream file(path + "/../" + name); + + if (!file.is_open()) + return {}; + + std::string value; + std::getline(file, value); + file.close(); + + if (!deviceId.empty()) + deviceId += ":"; + + deviceId += value; + } + + return controllerId + "-" + usbId + "-" + deviceId; +} + } /* namespace */ UVCCameraConfiguration::UVCCameraConfiguration(UVCCameraData *data) @@ -628,66 +694,9 @@ int UVCCameraData::init(std::shared_ptr media) bool UVCCameraData::generateId() { - const std::string path = video_->devicePath(); + id_ = generateIdFromPath(video_->devicePath()); - /* Create a controller ID from first device described in firmware. */ - std::string controllerId; - std::string searchPath = path; - while (true) { - std::string::size_type pos = searchPath.rfind('/'); - if (pos <= 1) { - LOG(UVC, Error) << "Can not find controller ID"; - return false; - } - - searchPath = searchPath.substr(0, pos); - - controllerId = sysfs::firmwareNodePath(searchPath); - if (!controllerId.empty()) - break; - } - - /* - * Create a USB ID from the device path which has the known format: - * - * path = bus, "-", ports, ":", config, ".", interface ; - * bus = number ; - * ports = port, [ ".", ports ] ; - * port = number ; - * config = number ; - * interface = number ; - * - * Example: 3-2.4:1.0 - * - * The bus is not guaranteed to be stable and needs to be stripped from - * the USB ID. The final USB ID is built up of the ports, config and - * interface properties. - * - * Example 2.4:1.0. - */ - std::string usbId = utils::basename(path.c_str()); - usbId = usbId.substr(usbId.find('-') + 1); - - /* Creata a device ID from the USB devices vendor and product ID. */ - std::string deviceId; - for (const char *name : { "idVendor", "idProduct" }) { - std::ifstream file(path + "/../" + name); - - if (!file.is_open()) - return false; - - std::string value; - std::getline(file, value); - file.close(); - - if (!deviceId.empty()) - deviceId += ":"; - - deviceId += value; - } - - id_ = controllerId + "-" + usbId + "-" + deviceId; - return true; + return !id_.empty(); } void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info, From patchwork Fri Sep 18 07:59:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28331 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 5F2C7C3360 for ; Fri, 18 Sep 2026 08:08:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C18336873E; Fri, 18 Sep 2026 10:08:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="sS/+Bn+y"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4A6A568721 for ; Fri, 18 Sep 2026 10:07:57 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-4843971bdd0so58228f8f.1 for ; Fri, 18 Sep 2026 01:07:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718877; x=1790323677; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ykexR92vSEb9rcyPQHfJWlqmYtXhEco9jPeZViYeKIs=; b=sS/+Bn+yLvA2NjnNbbEO6eW2K6kq6uyymFYrD2la9dt1ugz4lTGM1S7NNVHkVvl6SY 6pdRsoR7QIlcBOmlao6iNVv5/QrTG4lfODdUFCBlABxD3D3k3yFeJml1P4vUln+OaG3G 2mjwABK36L7r4XRCyxR6dpPRpXRyVm6eOHgoLJqXGV045noA14k2Lcxa/ISaM9DiLyCp n4fVmF64wDSlf7QqrRhL3I0V+ViOR6VUWNN3Kf0DiR7IvizuX8Bhh0odRW6ALM0ELp3c Ds9BLZuWrCs4HIiyyPRbxbxnmXn/kt082wDUbjhjIQueFHdMyrEUEGj/CpHAtjTF8tiL bNSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718877; x=1790323677; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ykexR92vSEb9rcyPQHfJWlqmYtXhEco9jPeZViYeKIs=; b=cCWpaMfwLb6qXWreJu3yQhgvhiZ+iFeBb55Eou/R5kDjc2wW/nahbkIMZOjfE5v7LJ CBZaN8fOrw+yRI70go/QZTc01nFQNbnYgtIiDvME+l5gw6pWKjDLK9WjHQ27Itj9Px4H zlvDPw8Mdw6Qg4DtyeGXJKFvz80aACdnmdWAIdsI7f3V0pgDFnHAV4rwzodo+1vIU8Fm W1U+RpiI+SYXO7CEnry+qv2L8rpwK1QH7I6FinQcPObUFX0nMuotd0/I3BB3ZSBjwz8g dp10geVynBg567xaYhmjX2U18jMNNvENJDh/FovU398uQ8qmrdr0ngSo8gF1w5Rv9Rjm 30aw== X-Gm-Message-State: AFuF++nVw2p0hZGvIWXF4brn7OqCv87U+K3J4ZfDcgOYHXf8eSW5XauA DShM9V3e1q6XlkwdLArOVK4W6znF/tF8lQcEJWfUJlD9jMuqAY8JHEDOe8tmtN6jEUWpZlz71nQ WsKzwddk= X-Gm-Gg: AYBFou3me8cPxeDjUNi94I5IHzLmzzAAsdnneZVKssFDUhZc6fACBcGB4+WfyFFpmk2 uQEm3NCY+Igx1t9m5NObZKRew1r1RwiGNqkOlO/gUaJL4pDNqVA8p7maCpiumKllwMA5CoAc9xR dNp/tYLwHWI6ur9GI5TbWRDhB3wbYhS/OyJh4qIdpVM1SFrKLkXQ2aV8FFhgLh009sMV13kkQCH A4fpMOedd8C3H+kLmP+0SNV9kmEcrzg77rfM+5VYDik5TAiphfYWj3X/P0Fjt/R7Uzpvqs8S0SL obNsBdCvj8OAusiDq9Ag/MozhxD50asNJc02PpXRJfSw273JtQUegBKKKTSH78K7QN+AXfoYWPw 6UKzg8gRDFC1jpd8Q4J3JxwMYNDzLLP7pjhgA7iaaw1tfQ+Q1T8hOrol8sNwP00G01pg+P9zrXt ri0e+NIX12jMJoI/nwYSF+k9Ktcv5BkM3F3GBs3eFBq6R82XWMzq4Ll8YSOTvaU3EGZ6+w6VDqC 1Pq3/efUDeEPDnraPkteioY7zuYTIv/wwqQWR5dTtHnoFXJeu1wRZy2fdxubEbWgJRJt1cpl9Ja q7C69giYWBugMAtG/cjTz5BWRQmAUxMOeqOAwCWfLIJexmdR8E9MKjqrHZEiIG3jQ7ddCAVeVM1 g4rxUvIlNhMUHslERGYpNUM8RXPYxc6Aiu4wVpRTqPen/cA== X-Received: by 2002:a05:6000:41d8:b0:487:1251:20fb with SMTP id ffacd0b85a97d-4871fa333bbmr1126138f8f.2.1789718876671; Fri, 18 Sep 2026 01:07:56 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:56 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 19/20] pipeline: uvcvideo: Implement survey() and createCamera() Date: Fri, 18 Sep 2026 08:59:57 +0100 Message-ID: <20260918080734.1228227-20-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Report a descriptor for every UVC camera in the system, identified by the default video node of its media entity. The camera ID is generated from the sysfs path derived from the video device numbers and the model from the media device model, so no device is acquired or opened while surveying. createCamera() acquires the media device of the described camera and creates it through the camera creation code previously in match(), which has now been removed. Signed-off-by: Naushir Patuck --- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 123 +++++++++++++------ 1 file changed, 84 insertions(+), 39 deletions(-) diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 5fa1bdb0ff0e..9d6dd1ec9bcc 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -21,12 +21,14 @@ #include #include +#include #include #include #include #include #include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_descriptor.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" @@ -46,7 +48,7 @@ public: { } - int init(std::shared_ptr media); + int init(std::shared_ptr media, const std::string &id); void addControl(uint32_t cid, const ControlInfo &v4l2info, ControlInfoMap::Map *ctrls); void imageBufferReady(FrameBuffer *buffer); @@ -62,8 +64,6 @@ public: std::optional manualExposureMode_; private: - bool generateId(); - std::string id_; }; @@ -95,7 +95,9 @@ public: int queueRequestDevice(Camera *camera, Request *request) override; - bool match(DeviceEnumerator *enumerator) override; + int survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) override; + int createCamera(const CameraDescriptor *descriptor) override; private: int processControl(const UVCCameraData *data, ControlList *controls, @@ -129,6 +131,20 @@ std::optional v4l2ToExposureMode(int32_t x) } } +/* Locate the default video entity of a UVC media device. */ +MediaEntity *defaultEntity(const MediaDevice *media) +{ + const std::vector &entities = media->entities(); + auto entity = std::find_if(entities.begin(), entities.end(), + [](MediaEntity *e) { + return e->flags() & MEDIA_ENT_FL_DEFAULT; + }); + if (entity == entities.end()) + return nullptr; + + return *entity; +} + /* * Generate the camera ID from the sysfs path of the UVC device video node. The * path is derived from the device numbers of the media entity, so the ID can be @@ -136,12 +152,15 @@ std::optional v4l2ToExposureMode(int32_t x) */ std::string generateIdFromPath(const std::string &path) { + if (path.empty()) + return {}; + /* Create a controller ID from first device described in firmware. */ std::string controllerId; std::string searchPath = path; while (true) { std::string::size_type pos = searchPath.rfind('/'); - if (pos <= 1) { + if (pos == std::string::npos || pos <= 1) { LOG(UVC, Error) << "Can not find controller ID"; return {}; } @@ -533,31 +552,72 @@ int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request) return 0; } -bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator) +int PipelineHandlerUVC::survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) { - std::shared_ptr media; DeviceMatch dm("uvcvideo"); - media = acquireMediaDevice(enumerator, dm); - if (!media) - return false; + for (std::shared_ptr &media : enumerator->searchAll(dm)) { + MediaEntity *entity = defaultEntity(media.get()); + if (!entity) + continue; - std::unique_ptr data = std::make_unique(this); + /* + * The sysfs path of the device is derived from the entity + * device numbers, so the ID is generated without accessing + * the video device. + */ + std::string id = + generateIdFromPath(sysfs::devicePath(entity->deviceMajor(), + entity->deviceMinor())); + if (id.empty()) { + LOG(UVC, Warning) + << "Failed to generate an ID for " + << media->model(); + continue; + } - if (data->init(media)) - return false; + auto data = std::make_unique(); + data->id_ = std::move(id); + data->properties_.set(properties::Model, + utils::toAscii(media->model())); + data->mediaDevices_ = { media }; + data->entityName_ = entity->name(); + + descriptors->push_back(CameraDescriptor::create(std::move(data))); + } + + return 0; +} + +int PipelineHandlerUVC::createCamera(const CameraDescriptor *descriptor) +{ + const CameraDescriptor::Private *data = descriptor->_d(); + + if (data->mediaDevices_.size() != 1) + return -EINVAL; + + std::shared_ptr media = data->mediaDevices_[0]; + if (!acquireMediaDevice(media)) + return -EBUSY; + + std::unique_ptr cameraData = std::make_unique(this); + + int ret = cameraData->init(media, descriptor->id()); + if (ret) + return ret; /* Create and register the camera. */ - std::string id = data->id(); - std::set streams{ &data->stream_ }; + std::string id = cameraData->id(); + std::set streams{ &cameraData->stream_ }; std::shared_ptr camera = - Camera::create(std::move(data), id, streams); + Camera::create(std::move(cameraData), id, streams); registerCamera(std::move(camera)); /* Enable hot-unplug notifications. */ - hotplugMediaDevice(media); + hotplugMediaDevice(std::move(media)); - return true; + return 0; } bool PipelineHandlerUVC::acquireDevice(Camera *camera) @@ -577,35 +637,27 @@ void PipelineHandlerUVC::releaseDevice(Camera *camera) data->video_->close(); } -int UVCCameraData::init(std::shared_ptr media) +int UVCCameraData::init(std::shared_ptr media, const std::string &id) { int ret; + id_ = id; + /* Locate and initialise the camera data with the default video node. */ - const std::vector &entities = media->entities(); - auto entity = std::find_if(entities.begin(), entities.end(), - [](MediaEntity *e) { - return e->flags() & MEDIA_ENT_FL_DEFAULT; - }); - if (entity == entities.end()) { + MediaEntity *entity = defaultEntity(media.get()); + if (!entity) { LOG(UVC, Error) << "Could not find a default video device"; return -ENODEV; } /* Create and open the video device. */ - video_ = std::make_unique(*entity); + video_ = std::make_unique(entity); ret = video_->open(); if (ret) return ret; video_->bufferReady.connect(this, &UVCCameraData::imageBufferReady); - /* Generate the camera ID. */ - if (!generateId()) { - LOG(UVC, Error) << "Failed to generate camera ID"; - return -EINVAL; - } - /* * Populate the map of supported formats, and infer the camera sensor * resolution from the largest size it advertises. @@ -692,13 +744,6 @@ int UVCCameraData::init(std::shared_ptr media) return 0; } -bool UVCCameraData::generateId() -{ - id_ = generateIdFromPath(video_->devicePath()); - - return !id_.empty(); -} - void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info, ControlInfoMap::Map *ctrls) { From patchwork Fri Sep 18 07:59:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28332 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 3162EC3358 for ; Fri, 18 Sep 2026 08:08:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A5C8A68743; Fri, 18 Sep 2026 10:08:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="c3XOL9Ur"; dkim-atps=neutral Received: from mail-wr2-x0f.google.com (mail-wr2-x0f.google.com [IPv6:2a00:1450:4864:30::f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6D30868734 for ; Fri, 18 Sep 2026 10:07:58 +0200 (CEST) Received: by mail-wr2-x0f.google.com with SMTP id ffacd0b85a97d-482e1b429f7so64384f8f.2 for ; Fri, 18 Sep 2026 01:07:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718878; x=1790323678; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=cgeO87m08I8rjm4UZl64Fa/+TlLLQaT8WNsNCUyuluM=; b=c3XOL9Ur+2CwxRdwmZ6ArYz5LZhqYpo1S//nZAmoOEVqZibm14QoqyogjiNgsu2rXr LLMoZ4GNzSB3vFqrqhXGF7JRACWyRsKDg7ghWwNOJG7YUhkHP9PKeVUTMSXi+OmOqi6M azFAOwSVc1efbqWw+UPQ5xdN5OJsaL91z4vge8UtjVB/6RH3BzfBSllmUn+PI/eX/6OV GxBl0z6b8mPjMv7iN0uzSvIhDkLrJOKt3zGkNGCpIhNbWpjx/i3XnhdP7FsR8dcCv8Z+ YFz4N2+OvUxG6NsKLLw0rOK1K7CVKmulArYoQjrtliNVdZxWzT9+w1IZmsiOnTdtY2Y2 g1nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718878; x=1790323678; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=cgeO87m08I8rjm4UZl64Fa/+TlLLQaT8WNsNCUyuluM=; b=LqtvnzErVYggk3hyXKbh52U/xfBtmQB4shDCCMpiXJfg7Vyu5goN6U9Hr2+2MIT9Ds /LUgsyNUmd2jzJitby6jexvhjGFCH+QeXw5slsNsLyDBGuoYYHwWneE3LGrsgmPUagPg eC7TRgzb+wBJ2BsxmmJjamLMfnzmQjcNm7ABWXuTDT4uScHn4nyz/No3XQmybyCw1tQF Qwa/LPia3x34lDrXTMZHr5uRhWp2LzfzitWA3t6b8thaIVZGsZV8vvez2222j/sI3iZo RY0IzS7nEN+Gl6+q1K5LWwW2Pj+EQk2DFU/tl3jmdUptC5RxZvKIriL/hSVCVdWeeZn9 FHtw== X-Gm-Message-State: AFuF++nhjCkTw9OJH+Y3RaUl9zZqglLOHEh/fYsuyIAAnIIpxfcNca1N VUpj4+edcUxiM8nqPT+sIeiaj2XGRo2GoVNYcbQy015SvDsBm8H0YdMsyR17Yz/ElspQt5Y0d16 j8ljK4AM= X-Gm-Gg: AYBFou1Z6Mp6CX/8hwrlLeY78eDMj5LxUocMhXenmgw8Di6SExw0SHxr6XUoAN+JfHR Hs25ZyhCSsHBANETl1sYhX8n6cR23ZqDxnmsyQydMqFm36cpJyQqqUPxzdpYcHqtMUGND8uzhq4 HQmuPgVTXHV+sb5sCK9Z1gg+zEYyjl08gOOQR8rXc2xRjbD/TunYxM/V2RpLzuTLn4855TEvmG4 DXK5Txbt1WDRWAiOFinrpKSimprFE09f2GAi7NnGb4beHQnU1+JKr0B19qYbkdCMbIfem4eG33/ wlAzsPTe7ZVkjUdzOA1ifYeIvDhRPBwdQXSR7PGPdrdPjKO4sPi+oF4nNh7zo/JIKdXqqv65lm5 Ns32dRfykf6bfFQ6EguJvmAPmgGG7JEgcQqASyk9lB0MGY65bMCpGZrmmwus1l2xJjqWIa3yLLM pMPn4ob2TTegphjZsAxiC+yhQNVhzit8/CvqhHVqda1bLoH0h7aT3rCReQ6idIBvX7du60rsz0u inqOs5s4KqvZij8ZDrGqEMZPszrZmCevlmu/XXl8zCBv4TArA87N7WBj7jO1gS72ZruQwrhdxKR Rpgw3XqSQ/FTzv8o1SRz2QW6EddxvYAY5ELdTfs47bd3BE3+aaOnp3wwbT8Ui3/aQsr7P4CbWdg vAgXyS8lH45FB2IAEHjVUHFPUtLebNXVGt/w= X-Received: by 2002:a05:6000:4819:b0:487:1a14:7f6c with SMTP id ffacd0b85a97d-4871faa25efmr1174355f8f.5.1789718877785; Fri, 18 Sep 2026 01:07:57 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:57 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 20/20] Documentation: Describe the two-phase camera enumeration API Date: Fri, 18 Sep 2026 08:59:58 +0100 Message-ID: <20260918080734.1228227-21-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Document the enumerate() and initialize() camera manager APIs in the application writer's guide. Document survey() and createCamera() in the pipeline handler writer's guide, with a vivid example of each, similar to the match() documentation. Signed-off-by: Naushir Patuck --- .../guides/application-developer.rst | 41 +++++++ Documentation/guides/pipeline-handler.rst | 101 ++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst index abc67dd010bd..17e6c97ca2b3 100644 --- a/Documentation/guides/application-developer.rst +++ b/Documentation/guides/application-developer.rst @@ -89,6 +89,47 @@ Printing the camera id lists the machine-readable unique identifiers, so for example, the output on a Linux machine with a connected USB webcam is ``\_SB_.PCI0.XHC_.RHUB.HS08-8:1.0-5986:2115``. +Enumerating cameras without initialising them +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:doxy-pub:`CameraManager::start()` initialises every camera in the system, +including loading the IPA module associated with each camera. This is often a +heavyweight operation. An application that only uses a single camera can instead +enumerate the cameras first, and initialise the one it needs: + +.. code:: cpp + + std::unique_ptr cm = std::make_unique(); + + for (const auto &descriptor : cm->enumerate()) + std::cout << descriptor->id() << std::endl; + +:doxy-pub:`CameraManager::enumerate()` starts the camera manager if it is not +running yet, and returns a :doxy-pub:`CameraDescriptor` for every camera of a +pipeline handler that supports enumeration. A descriptor reports the camera id +and its properties without the camera being initialised; no media device is +acquired, no device node is opened, and no IPA module is loaded. Cameras of +pipeline handlers that do not support enumeration are created by ``start()``, +and reported through ``cameras()`` as before. + +A camera is then initialised from its descriptor: + +.. code:: cpp + + std::shared_ptr camera = cm->initialize(descriptor); + +The resulting camera is identical to one created by start() and is reported +through :doxy-pub:`CameraManager::cameras`, ``get()`` and the ``cameraAdded`` +signal. Initialising a camera that has already been initialised returns the +existing instance. + +The list returned by ``enumerate()`` is a snapshot of the cameras present when +it is called. Cameras hotplugged afterwards are initialised automatically and +reported through the ``cameraAdded`` signal, as they are after ``start()``. + +Note that ``stop()`` invalidates the descriptors returned by ``enumerate()``: +they can no longer be initialised. + What libcamera considers a camera ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst index e630199a5fb9..135b5f18d8c3 100644 --- a/Documentation/guides/pipeline-handler.rst +++ b/Documentation/guides/pipeline-handler.rst @@ -334,6 +334,13 @@ to the search using the ``.add()`` function on the DeviceMatch. This example uses search patterns that match vivid, but when developing a new pipeline handler, you should change this value to suit your device identifier. +.. note:: + + ``match()`` finds and creates the cameras in one step. A pipeline handler + can instead let applications list the cameras before initialising them, by + implementing ``survey()`` and ``createCamera()`` as described in + `Enumerating cameras without creating them`_ below. + Replace the contents of the ``PipelineHandlerVivid::match`` function with the following: @@ -557,6 +564,100 @@ interface, and device interaction interfaces. #include "libcamera/internal/media_device.h" #include "libcamera/internal/v4l2_videodevice.h" +Enumerating cameras without creating them +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``match()`` finds and creates the cameras of a pipeline handler in one step, and +:doxy-pub:`CameraManager::start()` calls it for every pipeline handler in the +system. Creating a camera includes loading its IPA module, which is often a +heavyweight operation. An application that only uses a single camera can instead +call :doxy-pub:`CameraManager::enumerate()` to list the cameras first, and +initialise the one it needs. To support this, a pipeline handler implements +:doxy-int:`PipelineHandler::survey` and +:doxy-int:`PipelineHandler::createCamera` in place of ``match()``. + +``survey()`` reports a :doxy-pub:`CameraDescriptor` for every camera the +pipeline handler would create, using only the information available from the +``DeviceEnumerator``. It shall not acquire a media device, open a device node or +alter any hardware state. It returns 0 on success, appending one descriptor per +camera found, or ``-ENOTSUP`` if the pipeline handler cannot survey its cameras. +Returning ``-ENOTSUP`` tells the camera manager to fall back to ``match()``. + +A descriptor carries the camera id, the properties that are known without +opening the device, such as the model, and the media devices the camera needs. +The :doxy-int:`DeviceEnumerator::searchAll` function returns every media device +matching a ``DeviceMatch`` without acquiring it. For vivid, one camera is +reported per matching media device: + +.. code-block:: cpp + + int PipelineHandlerVivid::survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) + { + DeviceMatch dm("vivid"); + dm.add("vivid-000-vid-cap"); + + for (std::shared_ptr &media : enumerator->searchAll(dm)) { + auto data = std::make_unique(); + data->id_ = media->getEntityByName("vivid-000-vid-cap")->name(); + data->properties_.set(properties::Model, media->model()); + data->mediaDevices_ = { media }; + + descriptors->push_back(CameraDescriptor::create(std::move(data))); + } + + return 0; + } + +``createCamera()`` then performs, for a single descriptor, the per-camera work +that ``match()`` would have done, i.e. acquiring the media devices the camera +needs, opening the device nodes and registering the camera. The camera shall be +created with the id of its descriptor, so that applications can relate the two. +For vivid, this is the body of the ``match()`` function written above, with the +media device taken from the descriptor instead of searched for: + +.. code-block:: cpp + + int PipelineHandlerVivid::createCamera(const CameraDescriptor *descriptor) + { + std::shared_ptr media = descriptor->_d()->mediaDevices_[0]; + if (!acquireMediaDevice(media)) + return -EBUSY; + + std::unique_ptr data = std::make_unique(this); + + /* Locate and open the capture video node. */ + if (data->init(media.get())) + return -ENODEV; + + /* Create and register the camera. */ + std::set streams{ &data->stream_ }; + std::shared_ptr camera = Camera::create(std::move(data), + descriptor->id(), streams); + registerCamera(std::move(camera)); + + return 0; + } + +When several cameras share a media device, for instance sensors behind a video +mux, the camera manager routes them to the same pipeline handler instance. +``createCamera()`` shall then only acquire the media device if the instance does +not already hold it, which can be checked with +:doxy-int:`PipelineHandler::usesMediaDevice`. + +The descriptor classes need the following includes: + +.. code-block:: cpp + + #include + #include "libcamera/internal/camera_descriptor.h" + +A pipeline handler that implements ``survey()`` and ``createCamera()`` does not +need ``match()``. ``start()`` creates its cameras by surveying and initialising +all of them. A pipeline handler that implements neither is not reported by +``CameraManager::enumerate()`` and its cameras are created by ``start()`` +through ``match()`` as before. + Registering controls and properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~