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