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