From patchwork Tue Apr 7 15:33:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26441 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 527ECBDCBD for ; Tue, 7 Apr 2026 15:34:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3FF0062DAC; Tue, 7 Apr 2026 17:34:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TaPyhsoi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1066F62D95 for ; Tue, 7 Apr 2026 17:34:30 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 77E326A6 for ; Tue, 7 Apr 2026 17:33:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575982; bh=9FbfwYofHyzwmmR3Qjf9uUfZyR1GSto0PS3FDU59jvs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=TaPyhsoixfc3mLfDLzpv8oo6X/u9jYVi67WZCGD+JomfXsARElLGs0Ym5kdMbESXH 8Pfcxjwpe49NJUdwM5GxlW8aSJ8IcIoKbcocc8HHnS64amPUVbaZmGx/IYU4FoPzv2 lyzo9m6m3n7M7tHU8Kbnwv4NGCp+27q5Udpt8CLQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 01/42] libcamera: Drop unneeded usage of this pointer Date: Tue, 7 Apr 2026 18:33:46 +0300 Message-ID: <20260407153427.1825999-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" A few unneeded usages of the this pointer to qualify access to class members have been introduced over time. Drop them. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Fix typo in commit message --- include/libcamera/internal/shared_mem_object.h | 4 ++-- src/libcamera/camera_manager.cpp | 2 +- src/libcamera/shared_mem_object.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h index e9f1dacd731d..21e025513fdf 100644 --- a/include/libcamera/internal/shared_mem_object.h +++ b/include/libcamera/internal/shared_mem_object.h @@ -79,7 +79,7 @@ public: SharedMemObject(SharedMemObject &&rhs) : SharedMem(std::move(rhs)) { - this->obj_ = rhs.obj_; + obj_ = rhs.obj_; rhs.obj_ = nullptr; } @@ -92,7 +92,7 @@ public: SharedMemObject &operator=(SharedMemObject &&rhs) { SharedMem::operator=(std::move(rhs)); - this->obj_ = rhs.obj_; + obj_ = rhs.obj_; rhs.obj_ = nullptr; return *this; } diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 23185467ba02..fc6e490bc476 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera) CameraManager::Private::Private() : Thread("CameraManager"), initialized_(false) { - ipaManager_ = std::make_unique(this->configuration()); + ipaManager_ = std::make_unique(configuration()); } int CameraManager::Private::start() diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp index d9b61d37dd43..6edab045d47e 100644 --- a/src/libcamera/shared_mem_object.cpp +++ b/src/libcamera/shared_mem_object.cpp @@ -80,8 +80,8 @@ SharedMem::SharedMem(const std::string &name, std::size_t size) */ SharedMem::SharedMem(SharedMem &&rhs) { - this->fd_ = std::move(rhs.fd_); - this->mem_ = rhs.mem_; + fd_ = std::move(rhs.fd_); + mem_ = rhs.mem_; rhs.mem_ = {}; } @@ -109,8 +109,8 @@ SharedMem::~SharedMem() */ SharedMem &SharedMem::operator=(SharedMem &&rhs) { - this->fd_ = std::move(rhs.fd_); - this->mem_ = rhs.mem_; + fd_ = std::move(rhs.fd_); + mem_ = rhs.mem_; rhs.mem_ = {}; return *this; } From patchwork Tue Apr 7 15:33:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26442 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 DED5BC32BB for ; Tue, 7 Apr 2026 15:34:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4B60062DA2; Tue, 7 Apr 2026 17:34:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JcIP+k9J"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 711FA62846 for ; Tue, 7 Apr 2026 17:34:31 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id D550A596 for ; Tue, 7 Apr 2026 17:33:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575984; bh=FA+VbWXucMIF14YZpAzXF035GI4stpayD7vwmpuTWFQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=JcIP+k9Jm7moO+cPBeFh3Qa916OVqTjMOnSIsQ3MMPZUvcb9xLgQdQA8zPJwuNghy d5K+G3Ok1hVTDpJEqIannKdArrPS243jAkRE+YtisNAcZ3Ydj1kBz6DcjSrlFN7V2Y D09N9XGQdnriV3FY4XYZRlEYkCgXjz8muSR9A7RY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 02/42] libcamera: request: Move all private member variables to Private class Date: Tue, 7 Apr 2026 18:33:47 +0300 Message-ID: <20260407153427.1825999-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 Request class has a set of private member variables, with some of them stored in the Request class itself, and some in the Request::Private class. Storing the variables in the Request class itself has the advantage that accessors can be inline, at the cost of ABI breakage if variables need to be added, removed or otherwise modified. The controls_ and metadata_ variables have recently been turned from pointers to instances. This broke the ABI. To avoid further breakages, move all remaining private member variables to Request::Private. The performance impact of not inlining accessors will be negligible. While at it, drop an unneeded class forward declaration. Signed-off-by: Laurent Pinchart --- include/libcamera/internal/request.h | 12 +++- include/libcamera/request.h | 15 ++-- src/libcamera/request.cpp | 104 +++++++++++++++------------ 3 files changed, 70 insertions(+), 61 deletions(-) diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h index 693097ee9a26..7715077b3f7c 100644 --- a/include/libcamera/internal/request.h +++ b/include/libcamera/internal/request.h @@ -30,7 +30,7 @@ class Request::Private : public Extensible::Private LIBCAMERA_DECLARE_PUBLIC(Request) public: - Private(Camera *camera); + Private(Camera *camera, uint64_t cookie); ~Private(); Camera *camera() const { return camera_; } @@ -41,7 +41,7 @@ public: bool completeBuffer(FrameBuffer *buffer); void complete(); void cancel(); - void reset(); + void reset(Request::ReuseFlag flags); void prepare(std::chrono::milliseconds timeout = 0ms); Signal<> prepared; @@ -56,14 +56,20 @@ private: void timeout(); Camera *camera_; + const uint64_t cookie_; + + Status status_; bool cancelled_; uint32_t sequence_ = 0; bool prepared_ = false; + ControlList controls_; + ControlList metadata_; + BufferMap bufferMap_; + std::unordered_set pending_; std::map notifiers_; std::unique_ptr timer_; - ControlList metadata_; }; } /* namespace libcamera */ diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 290983f61352..08ac6e8daba7 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -22,7 +22,6 @@ namespace libcamera { class Camera; -class CameraControlValidator; class FrameBuffer; class Stream; @@ -49,16 +48,16 @@ public: void reuse(ReuseFlag flags = Default); - ControlList &controls() { return controls_; } + ControlList &controls(); const ControlList &metadata() const; - const BufferMap &buffers() const { return bufferMap_; } + const BufferMap &buffers() const; int addBuffer(const Stream *stream, FrameBuffer *buffer, std::unique_ptr &&fence = {}); FrameBuffer *findBuffer(const Stream *stream) const; uint32_t sequence() const; - uint64_t cookie() const { return cookie_; } - Status status() const { return status_; } + uint64_t cookie() const; + Status status() const; bool hasPendingBuffers() const; @@ -66,12 +65,6 @@ public: private: LIBCAMERA_DISABLE_COPY(Request) - - ControlList controls_; - BufferMap bufferMap_; - - const uint64_t cookie_; - Status status_; }; std::ostream &operator<<(std::ostream &out, const Request &r); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index b50a64205283..719e27aac763 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -52,12 +52,16 @@ LOG_DEFINE_CATEGORY(Request) /** * \brief Create a Request::Private - * \param camera The Camera that creates the request + * \param[in] camera The Camera that creates the request + * \param[in] cookie Opaque cookie for application use * * \todo Add a validator for metadata controls. */ -Request::Private::Private(Camera *camera) - : camera_(camera), cancelled_(false), metadata_(controls::controls) +Request::Private::Private(Camera *camera, uint64_t cookie) + : camera_(camera), cookie_(cookie), status_(RequestPending), + cancelled_(false), + controls_(camera->controls(), camera->_d()->validator()), + metadata_(controls::controls) { } @@ -132,7 +136,7 @@ void Request::Private::complete() ASSERT(request->status() == RequestPending); ASSERT(!hasPendingBuffers()); - request->status_ = cancelled_ ? RequestCancelled : RequestComplete; + status_ = cancelled_ ? RequestCancelled : RequestComplete; LOG(Request, Debug) << request->toString(); @@ -174,18 +178,37 @@ void Request::Private::cancel() /** * \brief Reset the request internal data to default values + * \param[in] flags Indicate whether or not to reuse the buffers * * After calling this function, all request internal data will have default - * values as if the Request::Private instance had just been constructed. + * values as if the Request::Private instance had just been constructed, with + * the exception of bufferMap_ if the Request::ReuseFlag::ReuseBuffers flag is + * set in \a flags. */ -void Request::Private::reset() +void Request::Private::reset(Request::ReuseFlag flags) { - sequence_ = 0; + status_ = RequestPending; cancelled_ = false; + sequence_ = 0; prepared_ = false; + + controls_.clear(); + metadata_.clear(); + pending_.clear(); notifiers_.clear(); timer_.reset(); + + if (flags & ReuseBuffers) { + Request *request = _o(); + + for (const auto &[stream, buffer] : bufferMap_) { + buffer->_d()->setRequest(request); + pending_.insert(buffer); + } + } else { + bufferMap_.clear(); + } } /* @@ -286,9 +309,8 @@ void Request::Private::notifierActivated(FrameBuffer *buffer) ASSERT(it != notifiers_.end()); notifiers_.erase(it); - Request *request = _o(); LOG(Request, Debug) - << "Request " << request->cookie() << " buffer " << buffer + << "Request " << cookie_ << " buffer " << buffer << " fence signalled"; if (!notifiers_.empty()) @@ -305,8 +327,7 @@ void Request::Private::timeout() ASSERT(!notifiers_.empty()); notifiers_.clear(); - Request *request = _o(); - LOG(Request, Debug) << "Request prepare timeout: " << request->cookie(); + LOG(Request, Debug) << "Request prepare timeout: " << cookie_; cancel(); @@ -361,13 +382,11 @@ void Request::Private::timeout() * completely opaque to libcamera. */ Request::Request(Camera *camera, uint64_t cookie) - : Extensible(std::make_unique(camera)), - controls_(camera->controls(), camera->_d()->validator()), - cookie_(cookie), status_(RequestPending) + : Extensible(std::make_unique(camera, cookie)) { LIBCAMERA_TRACEPOINT(request_construct, this); - LOG(Request, Debug) << "Created request - cookie: " << cookie_; + LOG(Request, Debug) << "Created request - cookie: " << cookie; } Request::~Request() @@ -389,25 +408,10 @@ void Request::reuse(ReuseFlag flags) { LIBCAMERA_TRACEPOINT(request_reuse, this); - _d()->reset(); - - if (flags & ReuseBuffers) { - for (const auto &[stream, buffer] : bufferMap_) { - buffer->_d()->setRequest(this); - _d()->pending_.insert(buffer); - } - } else { - bufferMap_.clear(); - } - - status_ = RequestPending; - - controls_.clear(); - _d()->metadata_.clear(); + _d()->reset(flags); } /** - * \fn Request::controls() * \brief Retrieve the request's ControlList * * Requests store a list of controls to be applied to all frames captured for @@ -421,6 +425,10 @@ void Request::reuse(ReuseFlag flags) * * \return A reference to the ControlList in this request */ +ControlList &Request::controls() +{ + return _d()->controls_; +} /** * \brief Retrieve the request's metadata @@ -432,14 +440,19 @@ const ControlList &Request::metadata() const } /** - * \fn Request::buffers() * \brief Retrieve the request's streams to buffers map * * Return a reference to the map that associates each Stream part of the - * request to the FrameBuffer the Stream output should be directed to. + * request to the FrameBuffer the Stream output should be directed to. If a + * stream is not utilised in this request there will be no buffer for that + * stream in the map. * * \return The map of Stream to FrameBuffer */ +const Request::BufferMap &Request::buffers() const +{ + return _d()->bufferMap_; +} /** * \brief Add a FrameBuffer with its associated Stream to the Request @@ -492,7 +505,7 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer, return -EEXIST; } - auto [it, inserted] = bufferMap_.try_emplace(stream, buffer); + auto [it, inserted] = _d()->bufferMap_.try_emplace(stream, buffer); if (!inserted) { LOG(Request, Error) << "FrameBuffer already set for stream"; return -EEXIST; @@ -507,15 +520,6 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer, return 0; } -/** - * \var Request::bufferMap_ - * \brief Mapping of streams to buffers for this request - * - * The bufferMap_ tracks the buffers associated with each stream. If a stream is - * not utilised in this request there will be no buffer for that stream in the - * map. - */ - /** * \brief Return the buffer associated with a stream * \param[in] stream The stream the buffer is associated to @@ -524,8 +528,8 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer, */ FrameBuffer *Request::findBuffer(const Stream *stream) const { - const auto it = bufferMap_.find(stream); - if (it == bufferMap_.end()) + const auto it = _d()->bufferMap_.find(stream); + if (it == _d()->bufferMap_.end()) return nullptr; return it->second; @@ -552,13 +556,15 @@ uint32_t Request::sequence() const } /** - * \fn Request::cookie() * \brief Retrieve the cookie set when the request was created * \return The request cookie */ +uint64_t Request::cookie() const +{ + return _d()->cookie_; +} /** - * \fn Request::status() * \brief Retrieve the request completion status * * The request status indicates whether the request has completed successfully @@ -569,6 +575,10 @@ uint32_t Request::sequence() const * * \return The request completion status */ +Request::Status Request::status() const +{ + return _d()->status_; +} /** * \brief Check if a request has buffers yet to be completed From patchwork Tue Apr 7 15:33:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26443 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 08C89C32F5 for ; Tue, 7 Apr 2026 15:34:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6DB6162DB7; Tue, 7 Apr 2026 17:34:38 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hbIs8JTF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CAAEA62D95 for ; Tue, 7 Apr 2026 17:34:32 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 38C356A6 for ; Tue, 7 Apr 2026 17:33:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575985; bh=j6aB4OjheUpdHcByXqiSNHqTNROAHjaCBDTJ3oX+2nA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=hbIs8JTFwcXk+8krriLjD/tkoyz3pQMxFS7duYMk2r9gjVHgMB5m+Itifn6W4H2QJ FHyRIFhdN9rtqqQvjfJ3hYep+7DnYbxgozisCOru3EC3mXHiSDivSW7jUfeOmCSy13 pRKx8rrrv8fiTbetPfJBFrJ5zfab54hBHgynIP+g= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 03/42] libcamera: Replace plain pointers with std::unique<> Date: Tue, 7 Apr 2026 18:33:48 +0300 Message-ID: <20260407153427.1825999-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" libcamera uses std::unique_ptr<> to simplify life time management of objects and avoid leaks. For historical reasons there are a fair number of plain pointers with manual memory management. Replace them with std::unique_ptr<> when the conversion is simple. Signed-off-by: Laurent Pinchart --- .../guides/application-developer.rst | 5 +++-- .../internal/device_enumerator_udev.h | 2 +- include/libcamera/internal/ipc_unixsocket.h | 3 ++- include/libcamera/internal/v4l2_device.h | 2 +- include/libcamera/internal/v4l2_videodevice.h | 12 +++++----- src/apps/qcam/main.cpp | 5 ++--- src/apps/qcam/main_window.cpp | 11 +++++----- src/apps/qcam/main_window.h | 2 +- src/libcamera/device_enumerator_udev.cpp | 6 ++--- src/libcamera/ipc_unixsocket.cpp | 7 +++--- src/libcamera/v4l2_device.cpp | 5 +++-- src/libcamera/v4l2_videodevice.cpp | 22 ++++++++----------- src/v4l2/v4l2_camera.cpp | 7 +++--- src/v4l2/v4l2_camera.h | 2 +- src/v4l2/v4l2_compat_manager.cpp | 18 +++++---------- src/v4l2/v4l2_compat_manager.h | 2 +- 16 files changed, 49 insertions(+), 62 deletions(-) diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst index 918077af1d22..fd91cc503568 100644 --- a/Documentation/guides/application-developer.rst +++ b/Documentation/guides/application-developer.rst @@ -279,7 +279,8 @@ as the parameter of the ``FrameBufferAllocator::buffers()`` function. .. code:: cpp - FrameBufferAllocator *allocator = new FrameBufferAllocator(camera); + std::unique_ptr allocator = + std::make_unique(camera); for (StreamConfiguration &cfg : *config) { int ret = allocator->allocate(cfg.stream()); @@ -539,7 +540,7 @@ uses, so needs to do the following: camera->stop(); allocator->free(stream); - delete allocator; + allocator.reset(); camera->release(); camera.reset(); cm->stop(); diff --git a/include/libcamera/internal/device_enumerator_udev.h b/include/libcamera/internal/device_enumerator_udev.h index 0bf78b551e75..1ce988ab84d1 100644 --- a/include/libcamera/internal/device_enumerator_udev.h +++ b/include/libcamera/internal/device_enumerator_udev.h @@ -69,7 +69,7 @@ private: struct udev *udev_; struct udev_monitor *monitor_; - EventNotifier *notifier_; + std::unique_ptr notifier_; std::set orphans_; std::unordered_set devices_; diff --git a/include/libcamera/internal/ipc_unixsocket.h b/include/libcamera/internal/ipc_unixsocket.h index 48bb7a9422b5..57614fa3f0c7 100644 --- a/include/libcamera/internal/ipc_unixsocket.h +++ b/include/libcamera/internal/ipc_unixsocket.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -53,7 +54,7 @@ private: UniqueFD fd_; bool headerReceived_; struct Header header_; - EventNotifier *notifier_; + std::unique_ptr notifier_; }; } /* namespace libcamera */ diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h index dbbd118abd00..8c4c59bf7773 100644 --- a/include/libcamera/internal/v4l2_device.h +++ b/include/libcamera/internal/v4l2_device.h @@ -89,7 +89,7 @@ private: std::string deviceNode_; UniqueFD fd_; - EventNotifier *fdEventNotifier_; + std::unique_ptr fdEventNotifier_; bool frameStartEnabled_; }; diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h index 57db0036db6b..10367e4e178a 100644 --- a/include/libcamera/internal/v4l2_videodevice.h +++ b/include/libcamera/internal/v4l2_videodevice.h @@ -280,10 +280,10 @@ private: enum v4l2_buf_type bufferType_; enum v4l2_memory memoryType_; - V4L2BufferCache *cache_; + std::unique_ptr cache_; std::map queuedBuffers_; - EventNotifier *fdBufferNotifier_; + std::unique_ptr fdBufferNotifier_; State state_; std::optional firstFrame_; @@ -301,14 +301,14 @@ public: int open(); void close(); - V4L2VideoDevice *output() { return output_; } - V4L2VideoDevice *capture() { return capture_; } + V4L2VideoDevice *output() { return output_.get(); } + V4L2VideoDevice *capture() { return capture_.get(); } private: std::string deviceNode_; - V4L2VideoDevice *output_; - V4L2VideoDevice *capture_; + std::unique_ptr output_; + std::unique_ptr capture_; }; } /* namespace libcamera */ diff --git a/src/apps/qcam/main.cpp b/src/apps/qcam/main.cpp index d0bde14130fb..8c43d43f0dca 100644 --- a/src/apps/qcam/main.cpp +++ b/src/apps/qcam/main.cpp @@ -73,7 +73,7 @@ int main(int argc, char **argv) sa.sa_handler = &signalHandler; sigaction(SIGINT, &sa, nullptr); - CameraManager *cm = new libcamera::CameraManager(); + std::unique_ptr cm = std::make_unique(); ret = cm->start(); if (ret) { @@ -82,13 +82,12 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - MainWindow *mainWindow = new MainWindow(cm, options); + MainWindow *mainWindow = new MainWindow(cm.get(), options); mainWindow->show(); ret = app.exec(); delete mainWindow; cm->stop(); - delete cm; return ret; } diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp index 5a88c5aa8457..59c2aa66cb41 100644 --- a/src/apps/qcam/main_window.cpp +++ b/src/apps/qcam/main_window.cpp @@ -87,8 +87,8 @@ private: }; MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) - : saveRaw_(nullptr), options_(options), cm_(cm), allocator_(nullptr), - isCapturing_(false), captureRaw_(false) + : saveRaw_(nullptr), options_(options), cm_(cm), isCapturing_(false), + captureRaw_(false) { int ret; @@ -450,7 +450,7 @@ int MainWindow::startCapture() saveRaw_->setEnabled(config_->size() == 2); /* Allocate and map buffers. */ - allocator_ = new FrameBufferAllocator(camera_); + allocator_ = std::make_unique(camera_); for (StreamConfiguration &config : *config_) { Stream *stream = config.stream(); @@ -531,8 +531,7 @@ error: freeBuffers_.clear(); - delete allocator_; - allocator_ = nullptr; + allocator_.reset(); return ret; } @@ -564,7 +563,7 @@ void MainWindow::stopCapture() requests_.clear(); freeQueue_.clear(); - delete allocator_; + allocator_.reset(); isCapturing_ = false; diff --git a/src/apps/qcam/main_window.h b/src/apps/qcam/main_window.h index 81fcf915ade5..20d369ad2318 100644 --- a/src/apps/qcam/main_window.h +++ b/src/apps/qcam/main_window.h @@ -109,7 +109,7 @@ private: /* Camera manager, camera, configuration and buffers */ libcamera::CameraManager *cm_; std::shared_ptr camera_; - libcamera::FrameBufferAllocator *allocator_; + std::unique_ptr allocator_; std::unique_ptr config_; std::map> mappedBuffers_; diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp index 3195dd06e2fc..8b9376ca6ac4 100644 --- a/src/libcamera/device_enumerator_udev.cpp +++ b/src/libcamera/device_enumerator_udev.cpp @@ -28,14 +28,12 @@ namespace libcamera { LOG_DECLARE_CATEGORY(DeviceEnumerator) DeviceEnumeratorUdev::DeviceEnumeratorUdev() - : udev_(nullptr), monitor_(nullptr), notifier_(nullptr) + : udev_(nullptr), monitor_(nullptr) { } DeviceEnumeratorUdev::~DeviceEnumeratorUdev() { - delete notifier_; - if (monitor_) udev_monitor_unref(monitor_); if (udev_) @@ -212,7 +210,7 @@ done: return ret; int fd = udev_monitor_get_fd(monitor_); - notifier_ = new EventNotifier(fd, EventNotifier::Read); + notifier_ = std::make_unique(fd, EventNotifier::Read); notifier_->activated.connect(this, &DeviceEnumeratorUdev::udevNotify); return 0; diff --git a/src/libcamera/ipc_unixsocket.cpp b/src/libcamera/ipc_unixsocket.cpp index 002053e35557..6ec5a5cb6595 100644 --- a/src/libcamera/ipc_unixsocket.cpp +++ b/src/libcamera/ipc_unixsocket.cpp @@ -70,7 +70,7 @@ LOG_DEFINE_CATEGORY(IPCUnixSocket) */ IPCUnixSocket::IPCUnixSocket() - : headerReceived_(false), notifier_(nullptr) + : headerReceived_(false) { } @@ -130,7 +130,7 @@ int IPCUnixSocket::bind(UniqueFD fd) return -EINVAL; fd_ = std::move(fd); - notifier_ = new EventNotifier(fd_.get(), EventNotifier::Read); + notifier_ = std::make_unique(fd_.get(), EventNotifier::Read); notifier_->activated.connect(this, &IPCUnixSocket::dataNotifier); return 0; @@ -146,8 +146,7 @@ void IPCUnixSocket::close() if (!isBound()) return; - delete notifier_; - notifier_ = nullptr; + notifier_.reset(); fd_.reset(); headerReceived_ = false; diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index b49d73b1cb01..d6893210f3d9 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -123,7 +123,8 @@ int V4L2Device::setFd(UniqueFD fd) fd_ = std::move(fd); - fdEventNotifier_ = new EventNotifier(fd_.get(), EventNotifier::Exception); + fdEventNotifier_ = std::make_unique(fd_.get(), + EventNotifier::Exception); fdEventNotifier_->activated.connect(this, &V4L2Device::eventAvailable); fdEventNotifier_->setEnabled(false); @@ -142,7 +143,7 @@ void V4L2Device::close() if (!isOpen()) return; - delete fdEventNotifier_; + fdEventNotifier_.reset(); fd_.reset(); } diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index d877df29ee6e..ca87598309de 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -535,8 +535,7 @@ std::ostream &operator<<(std::ostream &out, const V4L2DeviceFormat &f) */ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) : V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr), - fdBufferNotifier_(nullptr), state_(State::Stopped), - watchdogDuration_(0.0) + state_(State::Stopped), watchdogDuration_(0.0) { /* * We default to an MMAP based CAPTURE video device, however this will @@ -626,7 +625,7 @@ int V4L2VideoDevice::open() return -EINVAL; } - fdBufferNotifier_ = new EventNotifier(fd(), notifierType); + fdBufferNotifier_ = std::make_unique(fd(), notifierType); fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); fdBufferNotifier_->setEnabled(false); @@ -715,7 +714,7 @@ int V4L2VideoDevice::open(SharedFD handle, enum v4l2_buf_type type) return -EINVAL; } - fdBufferNotifier_ = new EventNotifier(fd(), notifierType); + fdBufferNotifier_ = std::make_unique(fd(), notifierType); fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); fdBufferNotifier_->setEnabled(false); @@ -760,7 +759,7 @@ void V4L2VideoDevice::close() return; releaseBuffers(); - delete fdBufferNotifier_; + fdBufferNotifier_.reset(); formatInfo_ = nullptr; @@ -1374,7 +1373,7 @@ int V4L2VideoDevice::allocateBuffers(unsigned int count, if (ret < 0) return ret; - cache_ = new V4L2BufferCache(*buffers); + cache_ = std::make_unique(*buffers); memoryType_ = V4L2_MEMORY_MMAP; return ret; @@ -1599,7 +1598,7 @@ int V4L2VideoDevice::importBuffers(unsigned int count) if (ret) return ret; - cache_ = new V4L2BufferCache(count); + cache_ = std::make_unique(count); LOG(V4L2, Debug) << "Prepared to import " << count << " buffers"; @@ -1621,8 +1620,7 @@ int V4L2VideoDevice::releaseBuffers() LOG(V4L2, Debug) << "Releasing buffers"; - delete cache_; - cache_ = nullptr; + cache_.reset(); return requestBuffers(0, memoryType_); } @@ -2197,14 +2195,12 @@ V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelForma V4L2M2MDevice::V4L2M2MDevice(const std::string &deviceNode) : deviceNode_(deviceNode) { - output_ = new V4L2VideoDevice(deviceNode); - capture_ = new V4L2VideoDevice(deviceNode); + output_ = std::make_unique(deviceNode); + capture_ = std::make_unique(deviceNode); } V4L2M2MDevice::~V4L2M2MDevice() { - delete capture_; - delete output_; } /** diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 94d138cd5710..412ab4ea678e 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -20,7 +20,7 @@ LOG_DECLARE_CATEGORY(V4L2Compat) V4L2Camera::V4L2Camera(std::shared_ptr camera) : camera_(camera), controls_(controls::controls), isRunning_(false), - bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0) + efd_(-1), bufferAvailableCount_(0) { camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); } @@ -43,7 +43,7 @@ int V4L2Camera::open(StreamConfiguration *streamConfig) return -EINVAL; } - bufferAllocator_ = new FrameBufferAllocator(camera_); + bufferAllocator_ = std::make_unique(camera_); *streamConfig = config_->at(0); return 0; @@ -53,8 +53,7 @@ void V4L2Camera::close() { requestPool_.clear(); - delete bufferAllocator_; - bufferAllocator_ = nullptr; + bufferAllocator_.reset(); camera_->release(); } diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 9bd161b909de..3d09ae7d6057 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -80,7 +80,7 @@ private: bool isRunning_; libcamera::Mutex bufferLock_; - libcamera::FrameBufferAllocator *bufferAllocator_; + std::unique_ptr bufferAllocator_; std::vector> requestPool_; diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp index f53fb300dde8..a92f2e2944fc 100644 --- a/src/v4l2/v4l2_compat_manager.cpp +++ b/src/v4l2/v4l2_compat_manager.cpp @@ -54,25 +54,21 @@ V4L2CompatManager::~V4L2CompatManager() { files_.clear(); mmaps_.clear(); + proxies_.clear(); - if (cm_) { - proxies_.clear(); + if (cm_) cm_->stop(); - delete cm_; - cm_ = nullptr; - } } int V4L2CompatManager::start() { - cm_ = new CameraManager(); + cm_ = std::make_unique(); int ret = cm_->start(); if (ret) { LOG(V4L2Compat, Error) << "Failed to start camera manager: " << strerror(-ret); - delete cm_; - cm_ = nullptr; + cm_.reset(); return ret; } @@ -83,10 +79,8 @@ int V4L2CompatManager::start() * created here to wrap a camera device. */ auto cameras = cm_->cameras(); - for (auto [index, camera] : utils::enumerate(cameras)) { - V4L2CameraProxy *proxy = new V4L2CameraProxy(index, camera); - proxies_.emplace_back(proxy); - } + for (auto [index, camera] : utils::enumerate(cameras)) + proxies_.emplace_back(std::make_unique(index, camera)); return 0; } diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h index f7c6f1228282..13673d604a63 100644 --- a/src/v4l2/v4l2_compat_manager.h +++ b/src/v4l2/v4l2_compat_manager.h @@ -61,7 +61,7 @@ private: FileOperations fops_; - libcamera::CameraManager *cm_; + std::unique_ptr cm_; std::vector> proxies_; std::map> files_; From patchwork Tue Apr 7 15:33:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26444 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 82E2DBDCBD for ; Tue, 7 Apr 2026 15:34:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 511B262DAB; Tue, 7 Apr 2026 17:34:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="MYKqL4Fo"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 148CA62DA5 for ; Tue, 7 Apr 2026 17:34:34 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 77BC56A6 for ; Tue, 7 Apr 2026 17:33:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575986; bh=u15Vd1kV4ZYY8y9yeM0esvzrrLktDSirH1g7l59rCZA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=MYKqL4Foz0qc0LnsCsOOxTyTNHTLwhaVYLhroi2Ts5PFkmpHC+KgJ6yWh9Ia3/sKg MLZ33NWRrUes1tzuMpdTMFdttggMetjq0NTbMcy0p7OvdtJZHky0gHZMHTV4wLNDxz qEMEPPXp50WoBZdc3z+34pICTz3GCg1n8KpJvEPo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 04/42] libcamera: pipeline_handler: Add createIPA() function Date: Tue, 7 Apr 2026 18:33:49 +0300 Message-ID: <20260407153427.1825999-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" IPA proxies are created with a call to IPAManager::createIPA(), which is a static member function. This requires a complicated dance in the createIPA() function to retrieve the IPAManager instance through the camera manager, itself retrieved from the pipeline handler. Simplify the code by turning IPAManager::createIPA() into a non-static member function, and providing a wrapper in the PipelineHandler class to keep instantiation of IPA proxies easy in pipeline handlers. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Documentation/guides/ipa.rst | 8 +++--- include/libcamera/internal/ipa_manager.h | 25 ++++++++----------- include/libcamera/internal/pipeline_handler.h | 11 +++++++- src/libcamera/ipa_manager.cpp | 1 + src/libcamera/pipeline/ipu3/ipu3.cpp | 3 +-- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 3 +-- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 3 +-- .../pipeline/rpi/common/pipeline_base.cpp | 3 +-- src/libcamera/pipeline/vimc/vimc.cpp | 3 +-- src/libcamera/pipeline_handler.cpp | 10 ++++++++ src/libcamera/software_isp/software_isp.cpp | 3 +-- test/ipa/ipa_interface_test.cpp | 3 +-- 12 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Documentation/guides/ipa.rst b/Documentation/guides/ipa.rst index 25deadefaf7c..42993924dd79 100644 --- a/Documentation/guides/ipa.rst +++ b/Documentation/guides/ipa.rst @@ -387,20 +387,20 @@ In the pipeline handler, we first need to construct a specialized IPA proxy. From the point of view of the pipeline hander, this is the object that is the IPA. -To do so, we invoke the IPAManager: +To do so, we call the PipelineHandler::createIPA() function: .. code-block:: C++ std::unique_ptr ipa_ = - IPAManager::createIPA(pipe_, 1, 1); + pipe_->createIPA(1, 1); The ipa::rpi namespace comes from the namespace that we defined in the mojo data definition file, in the "Namespacing" section. The name of the proxy, IPAProxyRPi, comes from the name given to the main IPA interface, IPARPiInterface, in the "The Main IPA interface" section. -The return value of IPAManager::createIPA shall be error-checked, to confirm -that the returned pointer is not a nullptr. +The return value of createIPA() shall be error-checked, to confirm that the +returned pointer is not a nullptr. After this, before initializing the IPA, slots should be connected to all of the IPA's signals, as defined in the Event IPA interface: diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index f8ce780169e6..ecb6ae896e0c 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -17,15 +17,16 @@ #include #include "libcamera/internal/camera_manager.h" -#include "libcamera/internal/global_configuration.h" -#include "libcamera/internal/ipa_module.h" -#include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/pub_key.h" namespace libcamera { LOG_DECLARE_CATEGORY(IPAManager) +class GlobalConfiguration; +class IPAModule; +class PipelineHandler; + class IPAManager { public: @@ -33,23 +34,18 @@ public: ~IPAManager(); template - static std::unique_ptr createIPA(PipelineHandler *pipe, - uint32_t minVersion, - uint32_t maxVersion) + std::unique_ptr createIPA(PipelineHandler *pipe, uint32_t minVersion, + uint32_t maxVersion) { - CameraManager *cm = pipe->cameraManager(); - IPAManager *self = cm->_d()->ipaManager(); - IPAModule *m = self->module(pipe, minVersion, maxVersion); + IPAModule *m = module(pipe, minVersion, maxVersion); if (!m) return nullptr; - const GlobalConfiguration &configuration = cm->_d()->configuration(); - auto proxy = [&]() -> std::unique_ptr { - if (self->isSignatureValid(m)) - return std::make_unique(m, configuration); + if (isSignatureValid(m)) + return std::make_unique(m, configuration_); else - return std::make_unique(m, configuration); + return std::make_unique(m, configuration_); }(); if (!proxy->isValid()) { @@ -77,6 +73,7 @@ private: bool isSignatureValid(IPAModule *ipa) const; + const GlobalConfiguration &configuration_; std::vector> modules_; #if HAVE_IPA_PUBKEY diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index b4f97477acec..6922ce18ec87 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -17,11 +17,13 @@ #include #include +#include "libcamera/internal/camera_manager.h" +#include "libcamera/internal/ipa_manager.h" + namespace libcamera { class Camera; class CameraConfiguration; -class CameraManager; class DeviceEnumerator; class DeviceMatch; class FrameBuffer; @@ -70,6 +72,13 @@ public: CameraManager *cameraManager() const { return manager_; } + template + std::unique_ptr createIPA(uint32_t minVersion, uint32_t maxVersion) + { + IPAManager *ipaManager = manager_->_d()->ipaManager(); + return ipaManager->createIPA(this, minVersion, maxVersion); + } + protected: void registerCamera(std::shared_ptr camera); void hotplugMediaDevice(std::shared_ptr media); diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 35171d097136..1e905e8b82e8 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -105,6 +105,7 @@ LOG_DEFINE_CATEGORY(IPAManager) * CameraManager. */ IPAManager::IPAManager(const GlobalConfiguration &configuration) + : configuration_(configuration) { #if HAVE_IPA_PUBKEY if (!pubKey_.isValid()) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index bac6f1c2ef40..bf4c2921f2e7 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -31,7 +31,6 @@ #include "libcamera/internal/delayed_controls.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" @@ -1151,7 +1150,7 @@ int PipelineHandlerIPU3::registerCameras() int IPU3CameraData::loadIPA() { - ipa_ = IPAManager::createIPA(pipe(), 1, 1); + ipa_ = pipe()->createIPA(1, 1); if (!ipa_) return -ENOENT; diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index c209b0b070b1..e39c8d5aea59 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -35,7 +35,6 @@ #include "libcamera/internal/delayed_controls.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" @@ -382,7 +381,7 @@ int MaliC55CameraData::loadIPA() if (!sensor_) return 0; - ipa_ = IPAManager::createIPA(pipe(), 1, 1); + ipa_ = pipe()->createIPA(1, 1); if (!ipa_) return -ENOENT; diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index faeeecd96692..a74ac8287199 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -40,7 +40,6 @@ #include "libcamera/internal/delayed_controls.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/media_pipeline.h" #include "libcamera/internal/pipeline_handler.h" @@ -389,7 +388,7 @@ const PipelineHandlerRkISP1 *RkISP1CameraData::pipe() const int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks) { - ipa_ = IPAManager::createIPA(pipe(), 1, 1); + ipa_ = pipe()->createIPA(1, 1); if (!ipa_) return -ENOENT; diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 867ecf1bc1e3..6d2072e84da5 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -20,7 +20,6 @@ #include #include "libcamera/internal/camera_lens.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/v4l2_subdevice.h" using namespace std::chrono_literals; @@ -1161,7 +1160,7 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result) { int ret; - ipa_ = IPAManager::createIPA(pipe(), 1, 1); + ipa_ = pipe()->createIPA(1, 1); if (!ipa_) return -ENOENT; diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index 08b1007528f2..e915d5c2df23 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -35,7 +35,6 @@ #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" @@ -488,7 +487,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) if (data->init()) return false; - data->ipa_ = IPAManager::createIPA(this, 0, 0); + data->ipa_ = createIPA(0, 0); if (!data->ipa_) { LOG(VIMC, Error) << "no matching IPA found"; return false; diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 5c469e5bad24..e7145c1d4815 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -835,6 +835,16 @@ void PipelineHandler::disconnect() * \return The CameraManager for this pipeline handler */ +/** + * \fn PipelineHandler::createIPA() + * \brief Create an IPA proxy that matches this pipeline handler + * \param[in] minVersion Minimum acceptable version of IPA module + * \param[in] maxVersion Maximum acceptable version of IPA module + * + * \return A newly created IPA proxy, or nullptr if no matching IPA module is + * found or if the IPA proxy fails to initialize + */ + /** * \class PipelineHandlerFactoryBase * \brief Base class for pipeline handler factories diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 60228369fe2a..5b225c7cff93 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -23,7 +23,6 @@ #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/software_isp/debayer_params.h" #include "debayer_cpu.h" @@ -127,7 +126,7 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady); - ipa_ = IPAManager::createIPA(pipe, 0, 0); + ipa_ = pipe->createIPA(0, 0); if (!ipa_) { LOG(SoftwareIsp, Error) << "Creating IPA for software ISP failed"; diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index b81783664977..ccff6ac106c8 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -22,7 +22,6 @@ #include "libcamera/internal/camera_manager.h" #include "libcamera/internal/device_enumerator.h" -#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/pipeline_handler.h" @@ -99,7 +98,7 @@ protected: EventDispatcher *dispatcher = thread()->eventDispatcher(); Timer timer; - ipa_ = IPAManager::createIPA(pipe_.get(), 0, 0); + ipa_ = pipe_->createIPA(0, 0); if (!ipa_) { cerr << "Failed to create VIMC IPA interface" << endl; return TestFail; From patchwork Tue Apr 7 15:33:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26445 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 E2DD1C32F6 for ; Tue, 7 Apr 2026 15:34:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6E18D62DB5; Tue, 7 Apr 2026 17:34:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RTNNa+9A"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 562A262D9D for ; Tue, 7 Apr 2026 17:34:35 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id CC8836A6 for ; Tue, 7 Apr 2026 17:33:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575987; bh=hBpMyS0YxcY9DSFeNBS4rsFhthcjbfeuWaM0OnFgi3I=; h=From:To:Subject:Date:In-Reply-To:References:From; b=RTNNa+9AwzKIjiZtzIAP08LDosojbGQlVx7W2d+I7PpXfu/OjygofIPTFnwvreo5e VkXIJx2j1apXpB7tJWtV2oUdz3QekzvpGqWYoMgsOWVmS4QWNdYdpqYch7c4xGLi7c q06dbV2cj2fWpQTlQZRpDe2ANkDUpApUHgcwIo3A= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 05/42] test: ipa: ipa_interface: Use IPAManager::createIPA() Date: Tue, 7 Apr 2026 18:33:50 +0300 Message-ID: <20260407153427.1825999-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 goal of ipa_interface is to unit test the IPA interface. It currently creates the IPA proxy through the higher level PipelineHandler class. Use the IPAManager::createIPA() function instead to keep the focus on the components that were meant to be tested. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- test/ipa/ipa_interface_test.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index ccff6ac106c8..41ef81721bfa 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -22,6 +22,8 @@ #include "libcamera/internal/camera_manager.h" #include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/pipeline_handler.h" @@ -43,6 +45,8 @@ public: { delete notifier_; ipa_.reset(); + ipaManager_.reset(); + config_.reset(); cameraManager_.reset(); } @@ -90,6 +94,10 @@ protected: notifier_ = new EventNotifier(fd_, EventNotifier::Read, this); notifier_->activated.connect(this, &IPAInterfaceTest::readTrace); + /* Create the IPA manager. */ + config_ = std::make_unique(); + ipaManager_ = std::make_unique(*config_); + return TestPass; } @@ -98,7 +106,7 @@ protected: EventDispatcher *dispatcher = thread()->eventDispatcher(); Timer timer; - ipa_ = pipe_->createIPA(0, 0); + ipa_ = ipaManager_->createIPA(pipe_.get(), 0, 0); if (!ipa_) { cerr << "Failed to create VIMC IPA interface" << endl; return TestFail; @@ -173,6 +181,8 @@ private: std::shared_ptr pipe_; std::unique_ptr ipa_; std::unique_ptr cameraManager_; + std::unique_ptr config_; + std::unique_ptr ipaManager_; enum ipa::vimc::IPAOperationCode trace_; EventNotifier *notifier_; int fd_; From patchwork Tue Apr 7 15:33:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26446 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 22767C32F7 for ; Tue, 7 Apr 2026 15:34:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B1F1C62DB2; Tue, 7 Apr 2026 17:34:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="T38MO1P3"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A393162DB4 for ; Tue, 7 Apr 2026 17:34:36 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 13D46596 for ; Tue, 7 Apr 2026 17:33:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575989; bh=qBteUXCX+a0df/fse16sghyXjj/ZtdSN3WkmOX0/2hE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=T38MO1P36BL5dVOXhPB5XSYj/LEsnXkfvH4eETjnchUFFHbYZpr5zC66yQSoec3nR Ukn0FeZuLdGtEe4ismYrN4psF8WAIHAN2OlbTT/7W3N67RfYHsxQrxgXkR05bhYH7z v9CcYC4wtZR0cjlJyoPC+1jCnnaWzsPjxBoceKk4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 06/42] test: ipa: ipa_interface: Replace FIFO with pipe Date: Tue, 7 Apr 2026 18:33:51 +0300 Message-ID: <20260407153427.1825999-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 ipa_interface unit test uses a FIFO to communicate with the vimc IPA module. FIFOs are named pipes, created in the file system. The path to the FIFO is hardcoded so that both the unit test and IPA module know where to locate the file. If the ipa_interface crashes for any reason, the FIFO will not be removed, and subsequent usage of the vimc IPA module will hang when trying to write to the FIFO in the IPA module. Fix this by replacing the FIFO with a pipe. Pipes are unidirectional data channels that are represented by a pair of file descriptors, without any presence in the file system. The write end of the pipe is passed to the vimc IPA module init() function, and then used the same way as the FIFO. While at it, use a std::unique_ptr to manage the notifier in the unit test instead of manually allocating and deleting the object. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/ipa/vimc.mojom | 3 +- src/ipa/vimc/vimc.cpp | 33 ++++--------------- src/libcamera/pipeline/vimc/vimc.cpp | 2 +- test/ipa/ipa_interface_test.cpp | 49 +++++++++++----------------- 4 files changed, 28 insertions(+), 59 deletions(-) diff --git a/include/libcamera/ipa/vimc.mojom b/include/libcamera/ipa/vimc.mojom index c5c5fe83734e..b1bee05a81a9 100644 --- a/include/libcamera/ipa/vimc.mojom +++ b/include/libcamera/ipa/vimc.mojom @@ -8,8 +8,6 @@ module ipa.vimc; import "include/libcamera/ipa/core.mojom"; -const string VimcIPAFIFOPath = "/tmp/libcamera_ipa_vimc_fifo"; - enum IPAOperationCode { IPAOperationNone, IPAOperationInit, @@ -26,6 +24,7 @@ enum IPAOperationCode { interface IPAVimcInterface { init(libcamera.IPASettings settings, + libcamera.SharedFD traceFd, IPAOperationCode code, [flags] TestFlag inFlags) => (int32 ret, [flags] TestFlag outFlags); diff --git a/src/ipa/vimc/vimc.cpp b/src/ipa/vimc/vimc.cpp index a1351a0f45ce..4162b848f7b1 100644 --- a/src/ipa/vimc/vimc.cpp +++ b/src/ipa/vimc/vimc.cpp @@ -33,6 +33,7 @@ public: ~IPAVimc(); int init(const IPASettings &settings, + const SharedFD &traceFd, const ipa::vimc::IPAOperationCode code, const Flags inFlags, Flags *outFlags) override; @@ -51,30 +52,28 @@ public: void computeParams(uint32_t frame, uint32_t bufferId) override; private: - void initTrace(); void trace(enum ipa::vimc::IPAOperationCode operation); - int fd_; + SharedFD fd_; std::map buffers_; }; IPAVimc::IPAVimc() - : fd_(-1) { - initTrace(); } IPAVimc::~IPAVimc() { - if (fd_ != -1) - ::close(fd_); } int IPAVimc::init(const IPASettings &settings, + const SharedFD &traceFd, const ipa::vimc::IPAOperationCode code, const Flags inFlags, Flags *outFlags) { + fd_ = traceFd; + trace(ipa::vimc::IPAOperationInit); LOG(IPAVimc, Debug) @@ -162,30 +161,12 @@ void IPAVimc::computeParams([[maybe_unused]] uint32_t frame, uint32_t bufferId) paramsComputed.emit(bufferId, flags); } -void IPAVimc::initTrace() -{ - struct stat fifoStat; - int ret = stat(ipa::vimc::VimcIPAFIFOPath.c_str(), &fifoStat); - if (ret) - return; - - ret = ::open(ipa::vimc::VimcIPAFIFOPath.c_str(), O_WRONLY | O_CLOEXEC); - if (ret < 0) { - ret = errno; - LOG(IPAVimc, Error) << "Failed to open vimc IPA test FIFO: " - << strerror(ret); - return; - } - - fd_ = ret; -} - void IPAVimc::trace(enum ipa::vimc::IPAOperationCode operation) { - if (fd_ < 0) + if (!fd_.isValid()) return; - int ret = ::write(fd_, &operation, sizeof(operation)); + int ret = ::write(fd_.get(), &operation, sizeof(operation)); if (ret < 0) { ret = errno; LOG(IPAVimc, Error) << "Failed to write to vimc IPA test FIFO: " diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index e915d5c2df23..6681c74ee564 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -498,7 +498,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) std::string conf = data->ipa_->configurationFile("vimc.conf"); Flags inFlags = ipa::vimc::TestFlag::Flag2; Flags outFlags; - data->ipa_->init(IPASettings{ conf, data->sensor_->model() }, + data->ipa_->init(IPASettings{ conf, data->sensor_->model() }, SharedFD{}, ipa::vimc::IPAOperationInit, inFlags, &outFlags); LOG(VIMC, Debug) diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index 41ef81721bfa..c1fe2267cc6e 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -7,9 +7,8 @@ #include #include +#include #include -#include -#include #include #include @@ -17,8 +16,10 @@ #include #include #include +#include #include #include +#include #include "libcamera/internal/camera_manager.h" #include "libcamera/internal/device_enumerator.h" @@ -37,13 +38,13 @@ class IPAInterfaceTest : public Test, public Object { public: IPAInterfaceTest() - : trace_(ipa::vimc::IPAOperationNone), notifier_(nullptr), fd_(-1) + : trace_(ipa::vimc::IPAOperationNone) { } ~IPAInterfaceTest() { - delete notifier_; + notifier_.reset(); ipa_.reset(); ipaManager_.reset(); config_.reset(); @@ -70,28 +71,22 @@ protected: return TestPass; } - /* Create and open the communication FIFO. */ - int ret = mkfifo(ipa::vimc::VimcIPAFIFOPath.c_str(), S_IRUSR | S_IWUSR); + /* Create the communication pipe. */ + int pipefds[2]; + int ret = pipe2(pipefds, O_NONBLOCK); if (ret) { ret = errno; - cerr << "Failed to create IPA test FIFO at '" - << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret) + cerr << "Failed to create IPA test pipe: " << strerror(ret) << endl; return TestFail; } - ret = open(ipa::vimc::VimcIPAFIFOPath.c_str(), O_RDONLY | O_NONBLOCK); - if (ret < 0) { - ret = errno; - cerr << "Failed to open IPA test FIFO at '" - << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret) - << endl; - unlink(ipa::vimc::VimcIPAFIFOPath.c_str()); - return TestFail; - } - fd_ = ret; + pipeReadFd_ = UniqueFD(pipefds[0]); + pipeWriteFd_ = SharedFD(pipefds[1]); - notifier_ = new EventNotifier(fd_, EventNotifier::Read, this); + notifier_ = std::make_unique(pipeReadFd_.get(), + EventNotifier::Read, + this); notifier_->activated.connect(this, &IPAInterfaceTest::readTrace); /* Create the IPA manager. */ @@ -116,7 +111,7 @@ protected: std::string conf = ipa_->configurationFile("vimc.conf"); Flags inFlags; Flags outFlags; - int ret = ipa_->init(IPASettings{ conf, "vimc" }, + int ret = ipa_->init(IPASettings{ conf, "vimc" }, pipeWriteFd_, ipa::vimc::IPAOperationInit, inFlags, &outFlags); if (ret < 0) { @@ -159,20 +154,13 @@ protected: return TestPass; } - void cleanup() override - { - close(fd_); - unlink(ipa::vimc::VimcIPAFIFOPath.c_str()); - } - private: void readTrace() { ssize_t s = read(notifier_->fd(), &trace_, sizeof(trace_)); if (s < 0) { int ret = errno; - cerr << "Failed to read from IPA test FIFO at '" - << ipa::vimc::VimcIPAFIFOPath << "': " << strerror(ret) + cerr << "Failed to read from IPA test pipe: " << strerror(ret) << endl; trace_ = ipa::vimc::IPAOperationNone; } @@ -184,8 +172,9 @@ private: std::unique_ptr config_; std::unique_ptr ipaManager_; enum ipa::vimc::IPAOperationCode trace_; - EventNotifier *notifier_; - int fd_; + std::unique_ptr notifier_; + UniqueFD pipeReadFd_; + SharedFD pipeWriteFd_; }; TEST_REGISTER(IPAInterfaceTest) From patchwork Tue Apr 7 15:33:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26447 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 E589DC32BB for ; Tue, 7 Apr 2026 15:34:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5106C62DC0; Tue, 7 Apr 2026 17:34:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ulM6izHm"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EF72362DB2 for ; Tue, 7 Apr 2026 17:34:37 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 593806A6 for ; Tue, 7 Apr 2026 17:33:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575990; bh=7RIEwgWtmLm34iS8i5jpipaLiTEQXiL5iqU+gJhTbhk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ulM6izHmsKQ08lW2YAkVwbXqQFazPD9Qu25vv4Ec/VoMbQCZNGKpYTa/l0d2dh0mQ aKW9WfObV27vLFZy4s1KCpF7Jmjqbaj8gRqdSmHLyoVevsFGfn9nq0sEtefC6tPMqM SZuXHFxH7jyKxGcYNTu7X9oP8NvSqBw8pj9ECIqI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 07/42] libcamera: camera_manager: Move IPAManager creation to start() time Date: Tue, 7 Apr 2026 18:33:52 +0300 Message-ID: <20260407153427.1825999-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 is currently instantiated when constructing the CameraManager constructor, in the CameraManager::Private constructor. This is the only sizeable object constructed with the CameraManager, all other components are constructed when starting the manager. Early construction of the IPAManager isn't wrong per-se, but prevents accessing the CameraManager from the IPAManager constructor (as the former isn't fully constructed). It also results in internal objects constructed in different threads, which isn't an issue at the moment as none of the objects constructed by the IPAManager are thread-bound, but could cause issues later. Finally, it prevents influencing the IPAManager creation with parameters passed to the CameraManager::start() function once we implement this, which would be useful for applications to override configuration parameters related to IPA modules. Move the instantiation of the IPAManager to start time to fix all those issues. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze Reviewed-by: Stefan Klug --- src/libcamera/camera_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index fc6e490bc476..5762c210ffc2 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -41,7 +41,6 @@ LOG_DEFINE_CATEGORY(Camera) CameraManager::Private::Private() : Thread("CameraManager"), initialized_(false) { - ipaManager_ = std::make_unique(configuration()); } int CameraManager::Private::start() @@ -94,6 +93,8 @@ void CameraManager::Private::run() int CameraManager::Private::init() { + ipaManager_ = std::make_unique(configuration()); + enumerator_ = DeviceEnumerator::create(); if (!enumerator_ || enumerator_->enumerate()) return -ENODEV; From patchwork Tue Apr 7 15:33:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26448 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 1921EC32F8 for ; Tue, 7 Apr 2026 15:34:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 648FB62DB8; Tue, 7 Apr 2026 17:34:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="gTkC+RWq"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6C89862DA2 for ; Tue, 7 Apr 2026 17:34:39 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id CE1276A6 for ; Tue, 7 Apr 2026 17:33:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575992; bh=vqb+V5KiW+iQzA+wKfrOttyEJip+8YUkGI2wXdO0r/4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=gTkC+RWqpocrlsg8tcKtf6FFX+HCJSpg38v+R0WYRmTNIvdJjBvVn7XlG60zfl8CC BLIm3ivQJxbM78nK9S5kSvKjcxxwafXXpOM2KGWgBzGuHphCAzzP057A4NQG2wYekQ 7MpR2oAyd5OJbQwZpiT8gtTBxGiicUBLwASWccC8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 08/42] libcamera: yaml_parser: Use std::make_unique<> Date: Tue, 7 Apr 2026 18:33:53 +0300 Message-ID: <20260407153427.1825999-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlParser::parse() function constructs a std::unique_ptr<> instance with a manual call to operator new. Replace it with std::make_unique<>. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze Reviewed-by: Stefan Klug --- src/libcamera/yaml_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index e13b5faedd81..791fb6eeafa8 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -769,7 +769,7 @@ std::unique_ptr YamlParser::parse(File &file) if (context.init(file)) return nullptr; - std::unique_ptr root(new YamlObject()); + std::unique_ptr root = std::make_unique(); if (context.parseContent(*root)) { LOG(YamlParser, Error) From patchwork Tue Apr 7 15:33:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26449 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 12F7DC32F9 for ; Tue, 7 Apr 2026 15:34:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8B7A262DB4; Tue, 7 Apr 2026 17:34:45 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="p8zleIA6"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AEF8162DB8 for ; Tue, 7 Apr 2026 17:34:40 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 1CF786A6 for ; Tue, 7 Apr 2026 17:33:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575993; bh=Jvu+34nKMt/j7LRF2DnIfAkc2QDWjJUmgIR8EITtpkA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=p8zleIA6EzlKk5P3uxTYozU1EHDWtw6LrjWiUuybubUBpKoLPHvLruM0qZbvhiq5x O+SbAYDZaEANvYE9QgsQafo2sa/PD3e5n+YCFDnda9Up3HXIvL1UV2HAHHT9egHlu9 VOVL/JfWgYBjwveBe+rBLh5D8B15tq1L5O7+Ko0o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 09/42] libcamera: yaml_parser: Rename Container to ValueContainer Date: Tue, 7 Apr 2026 18:33:54 +0300 Message-ID: <20260407153427.1825999-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject class defines two private types, Container and ListContainer. The format is an alias to std::vector, and is used to store child elements. The latter hasn't been used since commit 38987e165c28 ("libcamera: yaml_parser: Preserve order of items in dictionary"). To prepare for upcoming reworks that will use the name 'Container' as a template parameter, rename Container to ValueContainer for clarity, and drop the unused ListContainer type. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/yaml_parser.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 8c7916565946..03d6a05e2d0f 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -36,8 +36,7 @@ private: std::unique_ptr value; }; - using Container = std::vector; - using ListContainer = std::vector>; + using ValueContainer = std::vector; public: #ifndef __DOXYGEN__ @@ -48,7 +47,7 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; - Iterator(typename Container::const_iterator it) + Iterator(typename ValueContainer::const_iterator it) : it_(it) { } @@ -77,14 +76,14 @@ public: } protected: - Container::const_iterator it_; + ValueContainer::const_iterator it_; }; template class Adapter { public: - Adapter(const Container &container) + Adapter(const ValueContainer &container) : container_(container) { } @@ -100,7 +99,7 @@ public: } protected: - const Container &container_; + const ValueContainer &container_; }; class ListIterator : public Iterator @@ -232,7 +231,7 @@ private: Type type_; std::string value_; - Container list_; + ValueContainer list_; std::map> dictionary_; }; From patchwork Tue Apr 7 15:33:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26450 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 EA383C32FA for ; Tue, 7 Apr 2026 15:34:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4ED5762DD4; Tue, 7 Apr 2026 17:34:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kzCoxnVg"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0229862DAE for ; Tue, 7 Apr 2026 17:34:42 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 69899596 for ; Tue, 7 Apr 2026 17:33:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575994; bh=A/hZw4NPZr+MjatRMWXgxfVWlYIqKdh6FIzjaaHLDC4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kzCoxnVglRs97uIEYz074L9ZB6Pm8FdrS+ng3jnot4oHOVXFc7BWgH+F106wCL8P5 Fn3RLu+ZCen/EccaqE6EbwLLS3/+yfKQzZjEwEDLFh6RhlDxIRevlrt6CS6q9KWLT6 HKtO26TIWtCaSHTXXDBrZ2W7wFsYVTcwQ5nfg7tY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 10/42] libcamera: yaml_parser: Rename Getter to Accessor Date: Tue, 7 Apr 2026 18:33:55 +0300 Message-ID: <20260407153427.1825999-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" In preparation for support to set the value of a YamlObject, rename the Getter structure to Accessor. The structure will be extended with a set() function. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/matrix.h | 2 +- include/libcamera/internal/vector.h | 2 +- include/libcamera/internal/yaml_parser.h | 6 +++--- src/ipa/libipa/lsc_polynomial.h | 2 +- src/ipa/libipa/pwl.cpp | 2 +- src/libcamera/yaml_parser.cpp | 24 ++++++++++++------------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h index 1842389f2b22..67761cb6037d 100644 --- a/include/libcamera/internal/matrix.h +++ b/include/libcamera/internal/matrix.h @@ -200,7 +200,7 @@ std::ostream &operator<<(std::ostream &out, const Matrix &m) } template -struct YamlObject::Getter> { +struct YamlObject::Accessor> { std::optional> get(const YamlObject &obj) const { if (!matrixValidateYaml(obj, Rows * Cols)) diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h index 16b6aef0b38f..cfd8882ce0e6 100644 --- a/include/libcamera/internal/vector.h +++ b/include/libcamera/internal/vector.h @@ -347,7 +347,7 @@ std::ostream &operator<<(std::ostream &out, const Vector &v) } template -struct YamlObject::Getter> { +struct YamlObject::Accessor> { std::optional> get(const YamlObject &obj) const { if (!vectorValidateYaml(obj, Rows)) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 03d6a05e2d0f..0ff026706682 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -173,7 +173,7 @@ public: template std::optional get() const { - return Getter{}.get(*this); + return Accessor{}.get(*this); } template @@ -213,7 +213,7 @@ private: LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) template - friend struct Getter; + friend struct Accessor; friend class YamlParserContext; enum class Type { @@ -224,7 +224,7 @@ private: }; template - struct Getter { + struct Accessor { std::optional get(const YamlObject &obj) const; }; diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index c898faeb13db..df3a0d4b39c4 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -81,7 +81,7 @@ private: #ifndef __DOXYGEN__ template<> -struct YamlObject::Getter { +struct YamlObject::Accessor { std::optional get(const YamlObject &obj) const { std::optional cx = obj["cx"].get(); diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp index 72b63c4d068d..f38573e69c13 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -435,7 +435,7 @@ std::string Pwl::toString() const */ template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { /* Treat a single value as single point PWL. */ if (obj.isValue()) { diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 791fb6eeafa8..806a2743e0de 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -134,7 +134,7 @@ std::size_t YamlObject::size() const template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { if (obj.type_ != Type::Value) return std::nullopt; @@ -148,7 +148,7 @@ YamlObject::Getter::get(const YamlObject &obj) const } template -struct YamlObject::Getter || std::is_same_v || std::is_same_v || @@ -173,23 +173,23 @@ struct YamlObject::Getter; -template struct YamlObject::Getter; -template struct YamlObject::Getter; -template struct YamlObject::Getter; -template struct YamlObject::Getter; -template struct YamlObject::Getter; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { return obj.get(); } template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { if (obj.type_ != Type::Value) return std::nullopt; @@ -210,7 +210,7 @@ YamlObject::Getter::get(const YamlObject &obj) const template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { if (obj.type_ != Type::Value) return std::nullopt; @@ -220,7 +220,7 @@ YamlObject::Getter::get(const YamlObject &obj) const template<> std::optional -YamlObject::Getter::get(const YamlObject &obj) const +YamlObject::Accessor::get(const YamlObject &obj) const { if (obj.type_ != Type::List) return std::nullopt; From patchwork Tue Apr 7 15:33:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26451 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 D5472C32FB for ; Tue, 7 Apr 2026 15:34:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0A2AF62DC6; Tue, 7 Apr 2026 17:34:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="apzWc8Ca"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 612B062DB7 for ; Tue, 7 Apr 2026 17:34:43 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id BB9FF596 for ; Tue, 7 Apr 2026 17:33:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575996; bh=Fv5lXa2uyi52u+JBUw+knaNaYbxMUuzoIlNz77GPAKo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=apzWc8CaJd34tMX9n6cf9bGiOqSIOSxngUvZUGubDiZrh7RWXhxc9P2cqxPA5FWrF 5AAbGuc0u5axSJVZrt8NCqG1dgo/ZCeIwPMCD7udDBdTeYaWFH46dqRXqjt0HEt0Kr nlL0Tkm/gZcyUNSkNorceCGsvWKBtecxP5bEk1FE= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 11/42] libcamera: yaml_parser: Replace getList() with get() specializations Date: Tue, 7 Apr 2026 18:33:56 +0300 Message-ID: <20260407153427.1825999-12-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject class has two member function templates to get values: the get() function gets a scalar value, while the getList() function gets a vector of scalar values. As get() is a function template, we can provide specializations for vector types. This makes the code more systematic, and therefore more readable. Replace all getList() occurrences, and drop the getList() function. Signed-off-by: Laurent Pinchart --- Changes since v1: - Drop SFINAE for Adapter> specialization - Improve performance by moving value --- include/libcamera/internal/yaml_parser.h | 19 ---- src/ipa/libipa/agc_mean_luminance.cpp | 4 +- src/ipa/libipa/awb_bayes.cpp | 4 +- src/ipa/mali-c55/algorithms/lsc.cpp | 6 +- src/ipa/rkisp1/algorithms/agc.cpp | 2 +- src/ipa/rkisp1/algorithms/dpf.cpp | 6 +- src/ipa/rkisp1/algorithms/gsl.cpp | 8 +- src/ipa/rkisp1/algorithms/lsc.cpp | 4 +- src/ipa/rpi/controller/rpi/agc_channel.cpp | 4 +- src/ipa/rpi/controller/rpi/hdr.cpp | 8 +- src/libcamera/converter/converter_dw100.cpp | 2 +- src/libcamera/global_configuration.cpp | 2 +- .../pipeline/virtual/config_parser.cpp | 2 +- src/libcamera/yaml_parser.cpp | 92 ++++++++----------- test/yaml-parser.cpp | 4 +- 15 files changed, 64 insertions(+), 103 deletions(-) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 0ff026706682..7953befe11e2 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -182,25 +182,6 @@ public: return get().value_or(std::forward(defaultValue)); } -#ifndef __DOXYGEN__ - template || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v> * = nullptr> -#else - template -#endif - std::optional> getList() const; - DictAdapter asDict() const { return DictAdapter{ list_ }; } ListAdapter asList() const { return ListAdapter{ list_ }; } diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 564d4143d363..72988096d384 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -288,9 +288,9 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData) } std::vector exposureTimes = - modeValues["exposureTime"].getList().value_or(std::vector{}); + modeValues["exposureTime"].get>().value_or(std::vector{}); std::vector gains = - modeValues["gain"].getList().value_or(std::vector{}); + modeValues["gain"].get>().value_or(std::vector{}); if (exposureTimes.size() != gains.size()) { LOG(AgcMeanLuminance, Error) diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp index d2bcbd83d7f8..595bd0705732 100644 --- a/src/ipa/libipa/awb_bayes.cpp +++ b/src/ipa/libipa/awb_bayes.cpp @@ -211,9 +211,9 @@ int AwbBayes::readPriors(const YamlObject &tuningData) } std::vector temperatures = - p["ct"].getList().value_or(std::vector{}); + p["ct"].get>().value_or(std::vector{}); std::vector probabilities = - p["probability"].getList().value_or(std::vector{}); + p["probability"].get>().value_or(std::vector{}); if (temperatures.size() != probabilities.size()) { LOG(Awb, Error) diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index 5b042c757bc7..f75b9cd7b7b8 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -48,11 +48,11 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData } std::vector rTable = - yamlSet["r"].getList().value_or(std::vector{}); + yamlSet["r"].get>().value_or(std::vector{}); std::vector gTable = - yamlSet["g"].getList().value_or(std::vector{}); + yamlSet["g"].get>().value_or(std::vector{}); std::vector bTable = - yamlSet["b"].getList().value_or(std::vector{}); + yamlSet["b"].get>().value_or(std::vector{}); /* * Some validation to do; only 16x16 and 32x32 tables of diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 1ecaff680978..7cc06f91ac2b 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -55,7 +55,7 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData) } std::vector weights = - value.getList().value_or(std::vector{}); + value.get>().value_or(std::vector{}); if (weights.size() != context.hw.numHistogramWeights) { LOG(RkISP1Agc, Warning) << "Failed to read metering mode'" << key << "'"; diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp index 83c1e4b7b355..9961992b339b 100644 --- a/src/ipa/rkisp1/algorithms/dpf.cpp +++ b/src/ipa/rkisp1/algorithms/dpf.cpp @@ -72,7 +72,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * +---------|--------> X * -4....-1 0 1 2 3 4 */ - values = dFObject["g"].getList().value_or(std::vector{}); + values = dFObject["g"].get>().value_or(std::vector{}); if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) { LOG(RkISP1Dpf, Error) << "Invalid 'DomainFilter:g': expected " @@ -108,7 +108,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * For a 9x9 kernel, columns -6 and 6 are dropped, so coefficient * number 6 is not used. */ - values = dFObject["rb"].getList().value_or(std::vector{}); + values = dFObject["rb"].get>().value_or(std::vector{}); if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS && values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1) { LOG(RkISP1Dpf, Error) @@ -137,7 +137,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, const YamlObject &rFObject = tuningData["NoiseLevelFunction"]; std::vector nllValues; - nllValues = rFObject["coeff"].getList().value_or(std::vector{}); + nllValues = rFObject["coeff"].get>().value_or(std::vector{}); if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) { LOG(RkISP1Dpf, Error) << "Invalid 'RangeFilter:coeff': expected " diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp index 9604c0ac001a..7ac5dc215850 100644 --- a/src/ipa/rkisp1/algorithms/gsl.cpp +++ b/src/ipa/rkisp1/algorithms/gsl.cpp @@ -59,7 +59,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) { std::vector xIntervals = - tuningData["x-intervals"].getList().value_or(std::vector{}); + tuningData["x-intervals"].get>().value_or(std::vector{}); if (xIntervals.size() != kDegammaXIntervals) { LOG(RkISP1Gsl, Error) << "Invalid 'x' coordinates: expected " @@ -83,7 +83,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - curveYr_ = yObject["red"].getList().value_or(std::vector{}); + curveYr_ = yObject["red"].get>().value_or(std::vector{}); if (curveYr_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) { LOG(RkISP1Gsl, Error) << "Invalid 'y:red' coordinates: expected " @@ -92,7 +92,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - curveYg_ = yObject["green"].getList().value_or(std::vector{}); + curveYg_ = yObject["green"].get>().value_or(std::vector{}); if (curveYg_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) { LOG(RkISP1Gsl, Error) << "Invalid 'y:green' coordinates: expected " @@ -101,7 +101,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - curveYb_ = yObject["blue"].getList().value_or(std::vector{}); + curveYb_ = yObject["blue"].get>().value_or(std::vector{}); if (curveYb_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) { LOG(RkISP1Gsl, Error) << "Invalid 'y:blue' coordinates: expected " diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index c6982e6b4b10..3f456210a2da 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -317,7 +317,7 @@ std::vector LscTableLoader::parseTable(const YamlObject &tuningData, RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX; std::vector table = - tuningData[prop].getList().value_or(std::vector{}); + tuningData[prop].get>().value_or(std::vector{}); if (table.size() != kLscNumSamples) { LOG(RkISP1Lsc, Error) << "Invalid '" << prop << "' values: expected " @@ -333,7 +333,7 @@ std::vector parseSizes(const YamlObject &tuningData, const char *prop) { std::vector sizes = - tuningData[prop].getList().value_or(std::vector{}); + tuningData[prop].get>().value_or(std::vector{}); if (sizes.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) { LOG(RkISP1Lsc, Error) << "Invalid '" << prop << "' values: expected " diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp index 010de81a0e35..ebef560be84e 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp @@ -66,13 +66,13 @@ readMeteringModes(std::map &metering_modes, int AgcExposureMode::read(const libcamera::YamlObject ¶ms) { - auto value = params["shutter"].getList(); + auto value = params["shutter"].get>(); if (!value) return -EINVAL; std::transform(value->begin(), value->end(), std::back_inserter(exposureTime), [](double v) { return v * 1us; }); - value = params["gain"].getList(); + value = params["gain"].get>(); if (!value) return -EINVAL; gain = std::move(*value); diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp index f3da8291bf5d..06400ea79a95 100644 --- a/src/ipa/rpi/controller/rpi/hdr.cpp +++ b/src/ipa/rpi/controller/rpi/hdr.cpp @@ -29,7 +29,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod if (!params.contains("cadence")) LOG(RPiHdr, Fatal) << "No cadence for HDR mode " << name; - cadence = params["cadence"].getList().value(); + cadence = params["cadence"].get>().value(); if (cadence.empty()) LOG(RPiHdr, Fatal) << "Empty cadence in HDR mode " << name; @@ -69,14 +69,14 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod tonemap = params["tonemap"].get(ipa::Pwl{}); speed = params["speed"].get(1.0); if (params.contains("hi_quantile_targets")) { - hiQuantileTargets = params["hi_quantile_targets"].getList().value(); + hiQuantileTargets = params["hi_quantile_targets"].get>().value(); if (hiQuantileTargets.empty() || hiQuantileTargets.size() % 2) LOG(RPiHdr, Fatal) << "hi_quantile_targets much be even and non-empty"; } else hiQuantileTargets = { 0.95, 0.65, 0.5, 0.28, 0.3, 0.25 }; hiQuantileMaxGain = params["hi_quantile_max_gain"].get(1.6); if (params.contains("quantile_targets")) { - quantileTargets = params["quantile_targets"].getList().value(); + quantileTargets = params["quantile_targets"].get>().value(); if (quantileTargets.empty() || quantileTargets.size() % 2) LOG(RPiHdr, Fatal) << "quantile_targets much be even and non-empty"; } else @@ -84,7 +84,7 @@ void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &mod powerMin = params["power_min"].get(0.65); powerMax = params["power_max"].get(1.0); if (params.contains("contrast_adjustments")) { - contrastAdjustments = params["contrast_adjustments"].getList().value(); + contrastAdjustments = params["contrast_adjustments"].get>().value(); } else contrastAdjustments = { 0.5, 0.75 }; diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp index 7899fe8c25d1..19aa44bdeb32 100644 --- a/src/libcamera/converter/converter_dw100.cpp +++ b/src/libcamera/converter/converter_dw100.cpp @@ -126,7 +126,7 @@ int ConverterDW100Module::init(const YamlObject ¶ms) return -EINVAL; } - const auto coeffs = coefficients.getList(); + const auto coeffs = coefficients.get>(); if (!coeffs) { LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list"; return -EINVAL; diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index e086246d6856..99d16e7c38c6 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -152,7 +152,7 @@ std::optional> GlobalConfiguration::listOption( if (!*c) return {}; } - return c->getList(); + return c->get>(); } /** diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp index 1d3d9ba87ec0..fdc729509371 100644 --- a/src/libcamera/pipeline/virtual/config_parser.cpp +++ b/src/libcamera/pipeline/virtual/config_parser.cpp @@ -115,7 +115,7 @@ int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData, std::vector frameRates; if (supportedResolution.contains("frame_rates")) { auto frameRatesList = - supportedResolution["frame_rates"].getList(); + supportedResolution["frame_rates"].get>(); if (!frameRatesList || (frameRatesList->size() != 1 && frameRatesList->size() != 2)) { LOG(Virtual, Error) << "Invalid frame_rates: either one or two values"; diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 806a2743e0de..399e7529385f 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -109,12 +109,16 @@ std::size_t YamlObject::size() const /** * \fn template YamlObject::get() const + * \tparam T Type of the value * \brief Parse the YamlObject as a \a T value * * This function parses the value of the YamlObject as a \a T object, and * returns the value. If parsing fails (usually because the YamlObject doesn't * store a \a T value), std::nullopt is returned. * + * If the type \a T is an std::vector, the YamlObject will be parsed as a list + * of values. + * * \return The YamlObject value, or std::nullopt if parsing failed */ @@ -127,6 +131,9 @@ std::size_t YamlObject::size() const * returns the value. If parsing fails (usually because the YamlObject doesn't * store a \a T value), the \a defaultValue is returned. * + * Unlike the get() function, this overload does not support std::vector for the + * type \a T. + * * \return The YamlObject value, or \a defaultValue if parsing failed */ @@ -239,65 +246,38 @@ YamlObject::Accessor::get(const YamlObject &obj) const return Size(*width, *height); } -#endif /* __DOXYGEN__ */ - -/** - * \fn template YamlObject::getList() const - * \brief Parse the YamlObject as a list of \a T - * - * This function parses the value of the YamlObject as a list of \a T objects, - * and returns the value as a \a std::vector. If parsing fails, std::nullopt - * is returned. - * - * \return The YamlObject value as a std::vector, or std::nullopt if parsing - * failed - */ - -#ifndef __DOXYGEN__ - -template || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v> *> -std::optional> YamlObject::getList() const -{ - if (type_ != Type::List) - return std::nullopt; - - std::vector values; - values.reserve(list_.size()); - - for (const YamlObject &entry : asList()) { - const auto value = entry.get(); - if (!value) +template +struct YamlObject::Accessor> { + std::optional> get(const YamlObject &obj) const + { + if (obj.type_ != Type::List) return std::nullopt; - values.emplace_back(*value); + + std::vector values; + values.reserve(obj.list_.size()); + + for (const YamlObject &entry : obj.asList()) { + auto value = entry.get(); + if (!value) + return std::nullopt; + values.emplace_back(std::move(*value)); + } + + return values; } +}; - return values; -} - -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; -template std::optional> YamlObject::getList() const; - +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; #endif /* __DOXYGEN__ */ /** diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index 1b22c87b72f1..566401afcab6 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -587,9 +587,9 @@ protected: return TestFail; } - const auto &values = firstElement.getList(); + const auto &values = firstElement.get>(); if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) { - cerr << "getList() failed to return correct vector" << std::endl; + cerr << "get() failed to return correct vector" << std::endl; return TestFail; } From patchwork Tue Apr 7 15:33:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26452 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 9BC35C32FC for ; Tue, 7 Apr 2026 15:34:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BBC1D62DE0; Tue, 7 Apr 2026 17:34:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HZMsb4XP"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D3C4262DA2 for ; Tue, 7 Apr 2026 17:34:44 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 243C4596 for ; Tue, 7 Apr 2026 17:33:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575997; bh=7v9XADkIcESoBajheMtbeQniMG8/jRV6V+0ZQ5dVybk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HZMsb4XPhih9F0lKgfGYwtVzQwH7JMXknu1cIFakhzfjB3cfa9N+TCRkoKsgO25gi WM54SNB1YaZ02qqPuzBA+WK5Isd9GPzht21nTgCkFkjAFGYJhYjGgRGeb/fKPRVC3t dGz7tfkuj4e8yotyQj2mVVtdqvOhxUH4wCZqyhao= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 12/42] libcamera: yaml_parser: Add function to set a YamlObject value Date: Tue, 7 Apr 2026 18:33:57 +0300 Message-ID: <20260407153427.1825999-13-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject::set() function to set the value of an object, with specializations for scalar types. Signed-off-by: Laurent Pinchart --- Changes since v1: - Remove cv in addition to reference in YamlObject::set() --- include/libcamera/internal/yaml_parser.h | 8 ++++ src/libcamera/yaml_parser.cpp | 59 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 7953befe11e2..0666762308ac 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -182,6 +182,13 @@ public: return get().value_or(std::forward(defaultValue)); } + template + void set(T &&value) + { + return Accessor>>{} + .set(*this, std::forward(value)); + } + DictAdapter asDict() const { return DictAdapter{ list_ }; } ListAdapter asList() const { return ListAdapter{ list_ }; } @@ -207,6 +214,7 @@ private: template struct Accessor { std::optional get(const YamlObject &obj) const; + void set(YamlObject &obj, T value); }; Type type_; diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 399e7529385f..3119ab41d89e 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -137,6 +137,20 @@ std::size_t YamlObject::size() const * \return The YamlObject value, or \a defaultValue if parsing failed */ +/** + * \fn template YamlObject::set(T &&value) + * \brief Set the value of a YamlObject + * \param[in] value The value + * + * This function sets the value stored in a YamlObject to \a value. The value is + * converted to a string in an implementation-specific way that guarantees that + * subsequent calls to get() will return the same value. + * + * Values can only be set on YamlObject of Type::Value type or empty YamlObject. + * Attempting to set a value on an object of type Type::Dict or Type::List does + * not modify the YamlObject. + */ + #ifndef __DOXYGEN__ template<> @@ -154,6 +168,16 @@ YamlObject::Accessor::get(const YamlObject &obj) const return std::nullopt; } +template<> +void YamlObject::Accessor::set(YamlObject &obj, bool value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = value ? "true" : "false"; +} + template struct YamlObject::Accessor || @@ -178,6 +202,15 @@ struct YamlObject::Accessor; @@ -194,6 +227,12 @@ YamlObject::Accessor::get(const YamlObject &obj) const return obj.get(); } +template<> +void YamlObject::Accessor::set(YamlObject &obj, float value) +{ + obj.set(std::forward(value)); +} + template<> std::optional YamlObject::Accessor::get(const YamlObject &obj) const @@ -215,6 +254,16 @@ YamlObject::Accessor::get(const YamlObject &obj) const return value; } +template<> +void YamlObject::Accessor::set(YamlObject &obj, double value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); +} + template<> std::optional YamlObject::Accessor::get(const YamlObject &obj) const @@ -225,6 +274,16 @@ YamlObject::Accessor::get(const YamlObject &obj) const return obj.value_; } +template<> +void YamlObject::Accessor::set(YamlObject &obj, std::string value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::move(value); +} + template<> std::optional YamlObject::Accessor::get(const YamlObject &obj) const From patchwork Tue Apr 7 15:33:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26453 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 51E46C32F5 for ; Tue, 7 Apr 2026 15:34:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D590962DD3; Tue, 7 Apr 2026 17:34:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bmZSvL0Y"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FFFD62DC4 for ; Tue, 7 Apr 2026 17:34:46 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 80F5B98A for ; Tue, 7 Apr 2026 17:33:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575998; bh=ELMgsAtzXAignrpDM/vf18ndkoQBVXdeUmlrI9Ss+Gk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=bmZSvL0YqGZMBLRA5NIT30rJOvN1P+OQANdE+Ym4OCL7/AHNHLZENhEfclt19AWvi 1Ipz8boz6NjeIY3b2LkScwV6g5BFpRns06HNfVk2mIQrx90Mk8dDiZ/RIBdAmOqGbH 2aMNc/fhd5VbbfV6EXPjVxTyvRM/dNTtSc6o7BaI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 13/42] libcamera: yaml_parser: Add functions to add children Date: Tue, 7 Apr 2026 18:33:58 +0300 Message-ID: <20260407153427.1825999-14-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject::add() functions to add children to a list or dictionary object. This will be used by the YamlParserContext to replace direct access to YamlObject member variables to decouple the two classes. Signed-off-by: Laurent Pinchart --- Changes since v1: - Avoid std::map lookup before insertion in YamlObject::add() - Don't move child node if add fails --- include/libcamera/internal/yaml_parser.h | 5 ++- src/libcamera/yaml_parser.cpp | 55 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 0666762308ac..3be61c503c88 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -28,7 +28,7 @@ class YamlObject { private: struct Value { - Value(std::string &&k, std::unique_ptr &&v) + Value(std::string k, std::unique_ptr &&v) : key(std::move(k)), value(std::move(v)) { } @@ -197,6 +197,9 @@ public: bool contains(std::string_view key) const; const YamlObject &operator[](std::string_view key) const; + YamlObject *add(std::unique_ptr &&child); + YamlObject *add(std::string key, std::unique_ptr &&child); + private: LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 3119ab41d89e..61088c7aed4f 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -436,6 +436,61 @@ const YamlObject &YamlObject::operator[](std::string_view key) const return *iter->second; } +/** + * \brief Add a child object to a list + * \param[in] child The child object + * + * Append the \a child object as the last element of this object's children + * list. This object must be empty, in which case it is converted to the + * Type::List type, or be a list. Otherwise, the function returns a nullptr and + * the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::List; + + if (type_ != Type::List) + return nullptr; + + Value &elem = list_.emplace_back(std::string{}, std::move(child)); + return elem.value.get(); +} + +/** + * \brief Add a child object to a dictionary + * \param[in] key The dictionary key + * \param[in] child The child object + * + * Add the \a child object with the given \a key to this object's children. This + * object must be empty, in which case it is converted to the Type::Dictionary + * type, or be a dictionary. Otherwise, the function returns a nullptr and the + * \a child is not modified. + * + * Keys are unique. If a child with the same \a key already exist, the function + * returns a nullptr and the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::Dictionary; + + if (type_ != Type::Dictionary) + return nullptr; + + auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); + if (!inserted) + return nullptr; + + return list_.emplace_back(it->first, std::move(child)).value.get(); +} + #ifndef __DOXYGEN__ class YamlParserContext From patchwork Tue Apr 7 15:33:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26454 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 40708C32FE for ; Tue, 7 Apr 2026 15:34:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D288662DCB; Tue, 7 Apr 2026 17:34:50 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GCjb/DxV"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 588EB62DD7 for ; Tue, 7 Apr 2026 17:34:47 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id C8DE998A for ; Tue, 7 Apr 2026 17:33:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775575999; bh=xSPIi+oHJsO/IDbh+rOdR9WCgBicw9wEP2PnCgzOQu4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=GCjb/DxVO3zrNL5syJY1ohs14qwjz/Q2p7UHZbGkhHnbmWvUv+8tK1YO4cOjV9nsT iY8eHtQ313QRq+JeT4+3fCmRC5NUCi7QGAAC8YqawCrGxuu6u2tDC2IBKvEQTvcTXH lpTeJXO3M972bgYuMqpw6ZWxNghC48sAAh0gzpWM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 14/42] libcamera: yaml_parser: Un-friend YamlParserContext from YamlObject Date: Tue, 7 Apr 2026 18:33:59 +0300 Message-ID: <20260407153427.1825999-15-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" YamlParserContext is a friend of the YamlObject class to access private member variables. Now that YamlObject exposes functions to set a value and add children, this is not needed anymore. Decouple the two classes. The YamlParserContext::readValue() function now takes a const reference to the EventPtr as the YamlParserContext::parseNextYamlObject() function needs to access it in the error handler. Signed-off-by: Laurent Pinchart --- Changes since v1: - Add error checking for add() failures - Pass const reference to readValue() function --- include/libcamera/internal/yaml_parser.h | 1 - src/libcamera/yaml_parser.cpp | 63 +++++++++++------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 3be61c503c88..c41397df67ab 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -205,7 +205,6 @@ private: template friend struct Accessor; - friend class YamlParserContext; enum class Type { Dictionary, diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 61088c7aed4f..6caacc3111c4 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -517,8 +517,8 @@ private: EventPtr nextEvent(); - void readValue(std::string &value, EventPtr event); - int parseDictionaryOrList(YamlObject::Type type, + std::string readValue(const EventPtr &event); + int parseDictionaryOrList(yaml_event_type_t endEventType, const std::function &parseItem); int parseNextYamlObject(YamlObject &yamlObject, EventPtr event); @@ -660,22 +660,22 @@ int YamlParserContext::parseContent(YamlObject &yamlObject) /** * \fn YamlParserContext::readValue() * \brief Parse event scalar and fill its content into a string - * \param[in] value The string reference to fill value * * A helper function to parse a scalar event as string. The caller needs to - * guarantee the event is of scaler type. + * guarantee the event is of scalar type. + * + * \return The scalar event value as a string */ -void YamlParserContext::readValue(std::string &value, EventPtr event) +std::string YamlParserContext::readValue(const EventPtr &event) { - value.assign(reinterpret_cast(event->data.scalar.value), - event->data.scalar.length); + return std::string{ reinterpret_cast(event->data.scalar.value), + event->data.scalar.length }; } /** * \fn YamlParserContext::parseDictionaryOrList() * \brief A helper function to abstract the common part of parsing dictionary or list - * - * \param[in] isDictionary True for parsing a dictionary, and false for a list + * \param[in] endEventType The YAML end event type (sequence or mapping) * \param[in] parseItem The callback to handle an item * * A helper function to abstract parsing an item from a dictionary or a list. @@ -690,13 +690,9 @@ void YamlParserContext::readValue(std::string &value, EventPtr event) * \return 0 on success or a negative error code otherwise * \retval -EINVAL The parser is failed to initialize */ -int YamlParserContext::parseDictionaryOrList(YamlObject::Type type, +int YamlParserContext::parseDictionaryOrList(yaml_event_type_t endEventType, const std::function &parseItem) { - yaml_event_type_t endEventType = YAML_SEQUENCE_END_EVENT; - if (type == YamlObject::Type::Dictionary) - endEventType = YAML_MAPPING_END_EVENT; - /* * Add a safety counter to make sure we don't loop indefinitely in case * the YAML file is malformed. @@ -738,24 +734,19 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even switch (event->type) { case YAML_SCALAR_EVENT: - yamlObject.type_ = YamlObject::Type::Value; - readValue(yamlObject.value_, std::move(event)); + yamlObject.set(readValue(event)); return 0; case YAML_SEQUENCE_START_EVENT: { - yamlObject.type_ = YamlObject::Type::List; - auto &list = yamlObject.list_; - auto handler = [this, &list](EventPtr evt) { - list.emplace_back(std::string{}, std::make_unique()); - return parseNextYamlObject(*list.back().value, std::move(evt)); + auto handler = [this, &yamlObject](EventPtr evt) { + YamlObject *child = yamlObject.add(std::make_unique()); + return parseNextYamlObject(*child, std::move(evt)); }; - return parseDictionaryOrList(YamlObject::Type::List, handler); + return parseDictionaryOrList(YAML_SEQUENCE_END_EVENT, handler); } case YAML_MAPPING_START_EVENT: { - yamlObject.type_ = YamlObject::Type::Dictionary; - auto &list = yamlObject.list_; - auto handler = [this, &list](EventPtr evtKey) { + auto handler = [this, &yamlObject](EventPtr evtKey) { /* Parse key */ if (evtKey->type != YAML_SCALAR_EVENT) { LOG(YamlParser, Error) << "Expect key at line: " @@ -765,26 +756,28 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even return -EINVAL; } - std::string key; - readValue(key, std::move(evtKey)); + std::string key = readValue(evtKey); /* Parse value */ EventPtr evtValue = nextEvent(); if (!evtValue) return -EINVAL; - auto &elem = list.emplace_back(std::move(key), - std::make_unique()); - return parseNextYamlObject(*elem.value, std::move(evtValue)); + YamlObject *child = yamlObject.add(std::move(key), + std::make_unique()); + if (!child) { + LOG(YamlParser, Error) + << "Duplicated key at line " + << evtKey->start_mark.line; + return -EINVAL; + } + + return parseNextYamlObject(*child, std::move(evtValue)); }; - int ret = parseDictionaryOrList(YamlObject::Type::Dictionary, handler); + int ret = parseDictionaryOrList(YAML_MAPPING_END_EVENT, handler); if (ret) return ret; - auto &dictionary = yamlObject.dictionary_; - for (const auto &elem : list) - dictionary.emplace(elem.key, elem.value.get()); - return 0; } From patchwork Tue Apr 7 15:34:00 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26455 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 42C92C3300 for ; Tue, 7 Apr 2026 15:34:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8545162DD4; Tue, 7 Apr 2026 17:34:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="M4438fYb"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CA59B62DE5 for ; Tue, 7 Apr 2026 17:34:48 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 109C498A for ; Tue, 7 Apr 2026 17:33:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576001; bh=KgSje6QVWRqdn0N+xzAWeZ1L8NrPB2FXHp79Rqrq7mg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=M4438fYbDV82H/q4IpMhRdbFmD4rW8uAkI1cgfjz0CgaegxaBsK0Puy1plCWpq2gT y8SfWKzXTC1ER9I83eU5v6Pghd/2kWVCbGho4JgJEWiFWWmCOzf/q6el5uty0mfQcf NcpMsGVxaMSL4swZgPflEc7f0/A/kk92p58yZghc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 15/42] libcamera: yaml_parser: Move Size handling to geometry.cpp Date: Tue, 7 Apr 2026 18:34:00 +0300 Message-ID: <20260407153427.1825999-16-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject::Accessor structure is designed to extend the YamlObject class with new getter and setter types without modifying yaml_parser.cpp. This feature is used for various libcamera classes, while standard C++ types are handled in yaml_parser.cpp. The Size class is an outlier: it is a libcamera class, but is handled in yaml_parser.cpp. Move it to geometry.cpp. Drop the std::vector specialization as it is currently unused. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/yaml_parser.h | 2 -- src/ipa/libipa/lsc_polynomial.h | 2 ++ src/libcamera/geometry.cpp | 29 ++++++++++++++++++++++++ src/libcamera/yaml_parser.cpp | 22 ------------------ test/yaml-parser.cpp | 2 ++ 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index c41397df67ab..7707e7469aca 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -17,8 +17,6 @@ #include -#include - namespace libcamera { class File; diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index df3a0d4b39c4..c71f215de3fc 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -14,6 +14,8 @@ #include #include +#include + #include "libcamera/internal/yaml_parser.h" namespace libcamera { diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index 2763967a6e57..2e8e9e33ea78 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -12,6 +12,8 @@ #include +#include "libcamera/internal/yaml_parser.h" + /** * \file geometry.h * \brief Data structures related to geometric objects @@ -924,4 +926,31 @@ std::ostream &operator<<(std::ostream &out, const Rectangle &r) return out; } +#ifndef __DOXYGEN__ +/* + * The YAML data shall be a list of two numerical values containing the x and y + * coordinates, in that order. + */ +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::List) + return std::nullopt; + + if (obj.list_.size() != 2) + return std::nullopt; + + auto width = obj.list_[0].value->get(); + if (!width) + return std::nullopt; + + auto height = obj.list_[1].value->get(); + if (!height) + return std::nullopt; + + return Size(*width, *height); +} +#endif /* __DOXYGEN__ */ + } /* namespace libcamera */ diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 6caacc3111c4..bf098360191b 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -284,27 +284,6 @@ void YamlObject::Accessor::set(YamlObject &obj, std::string value) obj.value_ = std::move(value); } -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::List) - return std::nullopt; - - if (obj.list_.size() != 2) - return std::nullopt; - - auto width = obj.list_[0].value->get(); - if (!width) - return std::nullopt; - - auto height = obj.list_[1].value->get(); - if (!height) - return std::nullopt; - - return Size(*width, *height); -} - template struct YamlObject::Accessor> { std::optional> get(const YamlObject &obj) const @@ -336,7 +315,6 @@ template struct YamlObject::Accessor>; template struct YamlObject::Accessor>; template struct YamlObject::Accessor>; template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; #endif /* __DOXYGEN__ */ /** diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index 566401afcab6..01e734d23059 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "libcamera/internal/yaml_parser.h" #include "test.h" From patchwork Tue Apr 7 15:34:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26456 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 4C02CC3301 for ; Tue, 7 Apr 2026 15:34:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B07B162DFA; Tue, 7 Apr 2026 17:34:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="cOvXD/CY"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EC9CE62DD7 for ; Tue, 7 Apr 2026 17:34:49 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6C3AB98A for ; Tue, 7 Apr 2026 17:33:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576002; bh=l3tl9rSkQdjIHsfOTq97YOolveY8DWFxRMX3gRoKTYY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=cOvXD/CY604HVBzdHDqKqMZ98+JQr5QkEUmmIxj/D0SVvN4NClkjKbOMHzvHj/EEE UoJEgDCE6RdLvcKMcWw0Aaw2Ye561Y5Y/fV01oyTWD5J1qXTuxMw5i1VP8FOwoHK5D dpvd9fXhBnDwHpDSuSMZZkVa0jKZdqBFTl44UqIo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 16/42] libcamera: yaml_parser: Drop unneeded \fn Doxygen commands Date: Tue, 7 Apr 2026 18:34:01 +0300 Message-ID: <20260407153427.1825999-17-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" There's no need to specify the function name in Doxygen comment blocks with \fn if the documentation directly precedes the function definition. Drop the unneeded \fn. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- src/libcamera/yaml_parser.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index bf098360191b..46369c6bc6a3 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -86,7 +86,6 @@ YamlObject::~YamlObject() = default; */ /** - * \fn YamlObject::size() * \brief Retrieve the number of elements in a dictionary or list YamlObject * * This function retrieves the size of the YamlObject, defined as the number of @@ -529,7 +528,6 @@ YamlParserContext::~YamlParserContext() } /** - * \fn YamlParserContext::init() * \brief Initialize a parser with an opened file for parsing * \param[in] fh The YAML file to parse * @@ -568,7 +566,6 @@ int YamlParserContext::yamlRead(void *data, unsigned char *buffer, size_t size, } /** - * \fn YamlParserContext::nextEvent() * \brief Get the next event * * Get the next event in the current YAML event stream, and return nullptr when @@ -597,7 +594,6 @@ YamlParserContext::EventPtr YamlParserContext::nextEvent() } /** - * \fn YamlParserContext::parseContent() * \brief Parse the content of a YAML document * \param[in] yamlObject The result of YamlObject * @@ -636,7 +632,6 @@ int YamlParserContext::parseContent(YamlObject &yamlObject) } /** - * \fn YamlParserContext::readValue() * \brief Parse event scalar and fill its content into a string * * A helper function to parse a scalar event as string. The caller needs to @@ -651,7 +646,6 @@ std::string YamlParserContext::readValue(const EventPtr &event) } /** - * \fn YamlParserContext::parseDictionaryOrList() * \brief A helper function to abstract the common part of parsing dictionary or list * \param[in] endEventType The YAML end event type (sequence or mapping) * \param[in] parseItem The callback to handle an item @@ -695,7 +689,6 @@ int YamlParserContext::parseDictionaryOrList(yaml_event_type_t endEventType, } /** - * \fn YamlParserContext::parseNextYamlObject() * \brief Parse next YAML event and read it as a YamlObject * \param[in] yamlObject The result of YamlObject * \param[in] event The leading event of the object From patchwork Tue Apr 7 15:34:02 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26457 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 C5AC9C3302 for ; Tue, 7 Apr 2026 15:34:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0707462DD3; Tue, 7 Apr 2026 17:34:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Gib9uf3i"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B9BC262DF4 for ; Tue, 7 Apr 2026 17:34:51 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B650DC58 for ; Tue, 7 Apr 2026 17:33:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576004; bh=zvKaRpEzAXrjtuy8jZo82JcLNWtPePLURUHEMiXAf3Q=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Gib9uf3ixAj0FWkanOa2Wg0aOw5eoQcLJ1GT8Swl1zc0bP37aGng87gmojrzge3wG pNah6Ssld2GJWolr6MJHUgu+7sfhFS6JzOmcSt4UfYtSuHlM615b8CEXoPsYAGpuME cMoVL+o/xT5MLcnA3k3ZFpd7ZwoQydWzdJ4xFyvI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 17/42] libcamera: yaml_parser: Split YamlObject from YamlParser Date: Tue, 7 Apr 2026 18:34:02 +0300 Message-ID: <20260407153427.1825999-18-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject class was designed to represent data parsed from YAML files. It is outgrowing the initial needs. Move it to a separate file to prepare for a rename. Most files that include yaml_parser.h only need access to a YamlObject. Switch them to yaml_object.h. pipeline_base.cpp was including yaml_parser.h indirectly through pipeline_base.h, include it directly now. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- .../libcamera/internal/global_configuration.h | 2 +- include/libcamera/internal/matrix.h | 2 +- include/libcamera/internal/meson.build | 1 + include/libcamera/internal/vector.h | 2 +- include/libcamera/internal/yaml_object.h | 227 +++++++++ include/libcamera/internal/yaml_parser.h | 213 +------- src/ipa/libipa/agc_mean_luminance.h | 2 +- src/ipa/libipa/awb.h | 2 +- src/ipa/libipa/awb_bayes.h | 2 +- src/ipa/libipa/interpolator.h | 2 +- src/ipa/libipa/lsc_polynomial.h | 2 +- src/ipa/libipa/lux.cpp | 2 +- src/ipa/libipa/module.h | 2 +- src/ipa/mali-c55/algorithms/blc.cpp | 2 +- src/ipa/mali-c55/algorithms/lsc.cpp | 2 +- src/ipa/rkisp1/algorithms/agc.cpp | 2 +- src/ipa/rkisp1/algorithms/blc.cpp | 2 +- src/ipa/rkisp1/algorithms/ccm.cpp | 2 +- src/ipa/rkisp1/algorithms/dpcc.cpp | 2 +- src/ipa/rkisp1/algorithms/goc.cpp | 2 +- src/ipa/rkisp1/algorithms/gsl.cpp | 2 +- src/ipa/rkisp1/algorithms/lsc.cpp | 2 +- src/ipa/rkisp1/algorithms/wdr.cpp | 2 +- src/ipa/rpi/controller/algorithm.h | 2 +- src/ipa/rpi/controller/controller.h | 2 +- src/libcamera/geometry.cpp | 2 +- src/libcamera/meson.build | 1 + .../pipeline/rpi/common/pipeline_base.cpp | 1 + .../pipeline/rpi/common/pipeline_base.h | 2 +- .../pipeline/virtual/config_parser.h | 2 +- src/libcamera/pipeline/virtual/virtual.cpp | 2 +- src/libcamera/yaml_object.cpp | 467 ++++++++++++++++++ src/libcamera/yaml_parser.cpp | 446 +---------------- 33 files changed, 727 insertions(+), 681 deletions(-) create mode 100644 include/libcamera/internal/yaml_object.h create mode 100644 src/libcamera/yaml_object.cpp diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 8d09517edca1..16c6a21f2a8a 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -14,7 +14,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h index 67761cb6037d..11fccd27547e 100644 --- a/include/libcamera/internal/matrix.h +++ b/include/libcamera/internal/matrix.h @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 4d2a09bd7041..21ce8b391ba2 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -49,6 +49,7 @@ libcamera_internal_headers = files([ 'v4l2_subdevice.h', 'v4l2_videodevice.h', 'vector.h', + 'yaml_object.h', 'yaml_parser.h', ]) diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h index cfd8882ce0e6..af24485f3bb1 100644 --- a/include/libcamera/internal/vector.h +++ b/include/libcamera/internal/vector.h @@ -19,7 +19,7 @@ #include #include "libcamera/internal/matrix.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/yaml_object.h new file mode 100644 index 000000000000..5a937e3d55e0 --- /dev/null +++ b/include/libcamera/internal/yaml_object.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * Copyright (C) 2025, Ideas on Board + * + * libcamera YAML object + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace libcamera { + +class YamlObject +{ +private: + struct Value { + Value(std::string k, std::unique_ptr &&v) + : key(std::move(k)), value(std::move(v)) + { + } + std::string key; + std::unique_ptr value; + }; + + using ValueContainer = std::vector; + +public: +#ifndef __DOXYGEN__ + template + class Iterator + { + public: + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + Iterator(typename ValueContainer::const_iterator it) + : it_(it) + { + } + + Derived &operator++() + { + ++it_; + return *static_cast(this); + } + + Derived operator++(int) + { + Derived it = *static_cast(this); + it_++; + return it; + } + + friend bool operator==(const Iterator &a, const Iterator &b) + { + return a.it_ == b.it_; + } + + friend bool operator!=(const Iterator &a, const Iterator &b) + { + return a.it_ != b.it_; + } + + protected: + ValueContainer::const_iterator it_; + }; + + template + class Adapter + { + public: + Adapter(const ValueContainer &container) + : container_(container) + { + } + + Iterator begin() const + { + return Iterator{ container_.begin() }; + } + + Iterator end() const + { + return Iterator{ container_.end() }; + } + + protected: + const ValueContainer &container_; + }; + + class ListIterator : public Iterator + { + public: + using value_type = const YamlObject &; + using pointer = const YamlObject *; + using reference = value_type; + + value_type operator*() const + { + return *it_->value.get(); + } + + pointer operator->() const + { + return it_->value.get(); + } + }; + + class DictIterator : public Iterator + { + public: + using value_type = std::pair; + using pointer = value_type *; + using reference = value_type &; + + value_type operator*() const + { + return { it_->key, *it_->value.get() }; + } + }; + + class DictAdapter : public Adapter + { + public: + using key_type = std::string; + }; + + class ListAdapter : public Adapter + { + }; +#endif /* __DOXYGEN__ */ + + YamlObject(); + ~YamlObject(); + + bool isValue() const + { + return type_ == Type::Value; + } + bool isList() const + { + return type_ == Type::List; + } + bool isDictionary() const + { + return type_ == Type::Dictionary; + } + bool isEmpty() const + { + return type_ == Type::Empty; + } + explicit operator bool() const + { + return type_ != Type::Empty; + } + + std::size_t size() const; + + template + std::optional get() const + { + return Accessor{}.get(*this); + } + + template + T get(U &&defaultValue) const + { + return get().value_or(std::forward(defaultValue)); + } + + template + void set(T &&value) + { + return Accessor>>{} + .set(*this, std::forward(value)); + } + + DictAdapter asDict() const { return DictAdapter{ list_ }; } + ListAdapter asList() const { return ListAdapter{ list_ }; } + + const YamlObject &operator[](std::size_t index) const; + + bool contains(std::string_view key) const; + const YamlObject &operator[](std::string_view key) const; + + YamlObject *add(std::unique_ptr &&child); + YamlObject *add(std::string key, std::unique_ptr &&child); + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) + + template + friend struct Accessor; + + enum class Type { + Dictionary, + List, + Value, + Empty, + }; + + template + struct Accessor { + std::optional get(const YamlObject &obj) const; + void set(YamlObject &obj, T value); + }; + + Type type_; + + std::string value_; + ValueContainer list_; + std::map> dictionary_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 7707e7469aca..e503e83a80da 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -7,222 +7,13 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include -#include +#include "libcamera/internal/yaml_object.h" namespace libcamera { class File; -class YamlParserContext; - -class YamlObject -{ -private: - struct Value { - Value(std::string k, std::unique_ptr &&v) - : key(std::move(k)), value(std::move(v)) - { - } - std::string key; - std::unique_ptr value; - }; - - using ValueContainer = std::vector; - -public: -#ifndef __DOXYGEN__ - template - class Iterator - { - public: - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; - - Iterator(typename ValueContainer::const_iterator it) - : it_(it) - { - } - - Derived &operator++() - { - ++it_; - return *static_cast(this); - } - - Derived operator++(int) - { - Derived it = *static_cast(this); - it_++; - return it; - } - - friend bool operator==(const Iterator &a, const Iterator &b) - { - return a.it_ == b.it_; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) - { - return a.it_ != b.it_; - } - - protected: - ValueContainer::const_iterator it_; - }; - - template - class Adapter - { - public: - Adapter(const ValueContainer &container) - : container_(container) - { - } - - Iterator begin() const - { - return Iterator{ container_.begin() }; - } - - Iterator end() const - { - return Iterator{ container_.end() }; - } - - protected: - const ValueContainer &container_; - }; - - class ListIterator : public Iterator - { - public: - using value_type = const YamlObject &; - using pointer = const YamlObject *; - using reference = value_type; - - value_type operator*() const - { - return *it_->value.get(); - } - - pointer operator->() const - { - return it_->value.get(); - } - }; - - class DictIterator : public Iterator - { - public: - using value_type = std::pair; - using pointer = value_type *; - using reference = value_type &; - - value_type operator*() const - { - return { it_->key, *it_->value.get() }; - } - }; - - class DictAdapter : public Adapter - { - public: - using key_type = std::string; - }; - - class ListAdapter : public Adapter - { - }; -#endif /* __DOXYGEN__ */ - - YamlObject(); - ~YamlObject(); - - bool isValue() const - { - return type_ == Type::Value; - } - bool isList() const - { - return type_ == Type::List; - } - bool isDictionary() const - { - return type_ == Type::Dictionary; - } - bool isEmpty() const - { - return type_ == Type::Empty; - } - explicit operator bool() const - { - return type_ != Type::Empty; - } - - std::size_t size() const; - - template - std::optional get() const - { - return Accessor{}.get(*this); - } - - template - T get(U &&defaultValue) const - { - return get().value_or(std::forward(defaultValue)); - } - - template - void set(T &&value) - { - return Accessor>>{} - .set(*this, std::forward(value)); - } - - DictAdapter asDict() const { return DictAdapter{ list_ }; } - ListAdapter asList() const { return ListAdapter{ list_ }; } - - const YamlObject &operator[](std::size_t index) const; - - bool contains(std::string_view key) const; - const YamlObject &operator[](std::string_view key) const; - - YamlObject *add(std::unique_ptr &&child); - YamlObject *add(std::string key, std::unique_ptr &&child); - -private: - LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) - - template - friend struct Accessor; - - enum class Type { - Dictionary, - List, - Value, - Empty, - }; - - template - struct Accessor { - std::optional get(const YamlObject &obj) const; - void set(YamlObject &obj, T value); - }; - - Type type_; - - std::string value_; - ValueContainer list_; - std::map> dictionary_; -}; class YamlParser final { diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 0df97b27332d..dfe1ccbe948b 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "exposure_mode_helper.h" #include "histogram.h" diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index f4a86038635f..3f25d13feaa8 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -14,7 +14,7 @@ #include #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h index 47ef3cce4d58..79334ad3e7a3 100644 --- a/src/ipa/libipa/awb_bayes.h +++ b/src/ipa/libipa/awb_bayes.h @@ -10,7 +10,7 @@ #include #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "awb.h" #include "interpolator.h" diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h index fb2c611d186e..af35fc556a9b 100644 --- a/src/ipa/libipa/interpolator.h +++ b/src/ipa/libipa/interpolator.h @@ -15,7 +15,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index c71f215de3fc..19da46860849 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp index 899e88248f04..d79b116a3339 100644 --- a/src/ipa/libipa/lux.cpp +++ b/src/ipa/libipa/lux.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "histogram.h" diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h index c27af7718feb..8e6c658a6b63 100644 --- a/src/ipa/libipa/module.h +++ b/src/ipa/libipa/module.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "algorithm.h" diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp index d099219c3e43..3bdf19141e1d 100644 --- a/src/ipa/mali-c55/algorithms/blc.cpp +++ b/src/ipa/mali-c55/algorithms/blc.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file blc.h diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index f75b9cd7b7b8..e32f5a83485e 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -7,7 +7,7 @@ #include "lsc.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 7cc06f91ac2b..ef16a3775056 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -19,7 +19,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/histogram.h" diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp index 32fc44ffff92..19c262fffa73 100644 --- a/src/ipa/rkisp1/algorithms/blc.cpp +++ b/src/ipa/rkisp1/algorithms/blc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file blc.h diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index 466d7a116eea..567891d115be 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/fixedpoint.h" #include "libipa/interpolator.h" diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp index 7894628144f3..c195334750e1 100644 --- a/src/ipa/rkisp1/algorithms/dpcc.cpp +++ b/src/ipa/rkisp1/algorithms/dpcc.cpp @@ -9,7 +9,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp index a0e7030fe5db..6c07bd8c71e5 100644 --- a/src/ipa/rkisp1/algorithms/goc.cpp +++ b/src/ipa/rkisp1/algorithms/goc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp index 7ac5dc215850..292d0e80dc57 100644 --- a/src/ipa/rkisp1/algorithms/gsl.cpp +++ b/src/ipa/rkisp1/algorithms/gsl.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 3f456210a2da..7302eb58570b 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/lsc_polynomial.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp index ed81628c032c..9e2688a05179 100644 --- a/src/ipa/rkisp1/algorithms/wdr.cpp +++ b/src/ipa/rkisp1/algorithms/wdr.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include #include diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h index 1971bfdcc8ad..8839daa90916 100644 --- a/src/ipa/rpi/controller/algorithm.h +++ b/src/ipa/rpi/controller/algorithm.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "controller.h" diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h index fdb46557de9c..573942886bc0 100644 --- a/src/ipa/rpi/controller/controller.h +++ b/src/ipa/rpi/controller/controller.h @@ -16,7 +16,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "camera_mode.h" #include "device_status.h" diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index 2e8e9e33ea78..621008844c74 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file geometry.h diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 575408b2c733..1aafc84b8802 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -58,6 +58,7 @@ libcamera_internal_sources = files([ 'v4l2_subdevice.cpp', 'v4l2_videodevice.cpp', 'vector.cpp', + 'yaml_object.cpp', 'yaml_parser.cpp', ]) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 6d2072e84da5..08a2b32dc30f 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -21,6 +21,7 @@ #include "libcamera/internal/camera_lens.h" #include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/yaml_parser.h" using namespace std::chrono_literals; diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index 597eb5878dbf..8b5d78d2d2dc 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -26,7 +26,7 @@ #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" #include "libcamera/internal/v4l2_videodevice.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include #include diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h index d2000de9c12f..f696d8862897 100644 --- a/src/libcamera/pipeline/virtual/config_parser.h +++ b/src/libcamera/pipeline/virtual/config_parser.h @@ -13,7 +13,7 @@ #include #include "libcamera/internal/pipeline_handler.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "virtual.h" diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index efd800ebe3d6..d8fc64d22448 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -36,7 +36,7 @@ #include "libcamera/internal/framebuffer.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "pipeline/virtual/config_parser.h" diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp new file mode 100644 index 000000000000..90c1eaa85234 --- /dev/null +++ b/src/libcamera/yaml_object.cpp @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * Copyright (C) 2025, Ideas on Board. + * + * libcamera YAML object + */ + +#include "libcamera/internal/yaml_object.h" + +#include +#include +#include +#include + +#include + +/** + * \file yaml_object.h + * \brief YAML objects + */ + +namespace libcamera { + +namespace { + +/* Empty static YamlObject as a safe result for invalid operations */ +static const YamlObject empty; + +} /* namespace */ + +/** + * \class YamlObject + * \brief A class representing the tree structure of the YAML content + * + * The YamlObject class represents the tree structure of YAML content. A + * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a + * tree leaf. + */ + +YamlObject::YamlObject() + : type_(Type::Empty) +{ +} + +YamlObject::~YamlObject() = default; + +/** + * \fn YamlObject::isValue() + * \brief Return whether the YamlObject is a value + * + * \return True if the YamlObject is a value, false otherwise + */ + +/** + * \fn YamlObject::isList() + * \brief Return whether the YamlObject is a list + * + * \return True if the YamlObject is a list, false otherwise + */ + +/** + * \fn YamlObject::isDictionary() + * \brief Return whether the YamlObject is a dictionary + * + * \return True if the YamlObject is a dictionary, false otherwise + */ + +/** + * \fn YamlObject::isEmpty() + * \brief Return whether the YamlObject is an empty + * + * \return True if the YamlObject is empty, false otherwise + */ + +/** + * \fn YamlObject::operator bool() + * \brief Return whether the YamlObject is a non-empty + * + * \return False if the YamlObject is empty, true otherwise + */ + +/** + * \fn YamlObject::size() + * \brief Retrieve the number of elements in a dictionary or list YamlObject + * + * This function retrieves the size of the YamlObject, defined as the number of + * child elements it contains. Only YamlObject instances of Dictionary or List + * types have a size, calling this function on other types of instances is + * invalid and results in undefined behaviour. + * + * \return The size of the YamlObject + */ +std::size_t YamlObject::size() const +{ + switch (type_) { + case Type::Dictionary: + case Type::List: + return list_.size(); + default: + return 0; + } +} + +/** + * \fn template YamlObject::get() const + * \tparam T Type of the value + * \brief Parse the YamlObject as a \a T value + * + * This function parses the value of the YamlObject as a \a T object, and + * returns the value. If parsing fails (usually because the YamlObject doesn't + * store a \a T value), std::nullopt is returned. + * + * If the type \a T is an std::vector, the YamlObject will be parsed as a list + * of values. + * + * \return The YamlObject value, or std::nullopt if parsing failed + */ + +/** + * \fn template YamlObject::get(U &&defaultValue) const + * \brief Parse the YamlObject as a \a T value + * \param[in] defaultValue The default value when failing to parse + * + * This function parses the value of the YamlObject as a \a T object, and + * returns the value. If parsing fails (usually because the YamlObject doesn't + * store a \a T value), the \a defaultValue is returned. + * + * Unlike the get() function, this overload does not support std::vector for the + * type \a T. + * + * \return The YamlObject value, or \a defaultValue if parsing failed + */ + +/** + * \fn template YamlObject::set(T &&value) + * \brief Set the value of a YamlObject + * \param[in] value The value + * + * This function sets the value stored in a YamlObject to \a value. The value is + * converted to a string in an implementation-specific way that guarantees that + * subsequent calls to get() will return the same value. + * + * Values can only be set on YamlObject of Type::Value type or empty YamlObject. + * Attempting to set a value on an object of type Type::Dict or Type::List does + * not modify the YamlObject. + */ + +#ifndef __DOXYGEN__ + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_ == "true") + return true; + else if (obj.value_ == "false") + return false; + + return std::nullopt; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, bool value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = value ? "true" : "false"; +} + +template +struct YamlObject::Accessor || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v>> +{ + std::optional get(const YamlObject &obj) const + { + if (obj.type_ != Type::Value) + return std::nullopt; + + const std::string &str = obj.value_; + T value = {}; + + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), + value); + if (ptr != str.data() + str.size() || ec != std::errc()) + return std::nullopt; + + return value; + } + + void set(YamlObject &obj, T value) + { + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); + } +}; + +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + return obj.get(); +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, float value) +{ + obj.set(std::forward(value)); +} + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_.empty()) + return std::nullopt; + + char *end; + + errno = 0; + double value = utils::strtod(obj.value_.c_str(), &end); + + if ('\0' != *end || errno == ERANGE) + return std::nullopt; + + return value; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, double value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); +} + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + return obj.value_; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, std::string value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::move(value); +} + +template +struct YamlObject::Accessor> { + std::optional> get(const YamlObject &obj) const + { + if (obj.type_ != Type::List) + return std::nullopt; + + std::vector values; + values.reserve(obj.list_.size()); + + for (const YamlObject &entry : obj.asList()) { + auto value = entry.get(); + if (!value) + return std::nullopt; + values.emplace_back(std::move(*value)); + } + + return values; + } +}; + +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +#endif /* __DOXYGEN__ */ + +/** + * \fn YamlObject::asDict() const + * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators + * + * The YamlObject class doesn't directly implement iterators, as the iterator + * type depends on whether the object is a Dictionary or List. This function + * wraps a YamlObject of Dictionary type into an adapter that exposes + * iterators, as well as begin() and end() functions, allowing usage of + * range-based for loops with YamlObject. As YAML mappings are not ordered, the + * iteration order is not specified. + * + * The iterator's value_type is a + * std::pair. + * + * If the YamlObject is not of Dictionary type, the returned adapter operates + * as an empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \fn YamlObject::asList() const + * \brief Wrap a list YamlObject in an adapter that exposes iterators + * + * The YamlObject class doesn't directly implement iterators, as the iterator + * type depends on whether the object is a Dictionary or List. This function + * wraps a YamlObject of List type into an adapter that exposes iterators, as + * well as begin() and end() functions, allowing usage of range-based for loops + * with YamlObject. As YAML lists are ordered, the iteration order is identical + * to the list order in the YAML data. + * + * The iterator's value_type is a const YamlObject &. + * + * If the YamlObject is not of List type, the returned adapter operates as an + * empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \fn YamlObject::operator[](std::size_t index) const + * \brief Retrieve the element from list YamlObject by index + * + * This function retrieves an element of the YamlObject. Only YamlObject + * instances of List type associate elements with index, calling this function + * on other types of instances or with an invalid index results in an empty + * object. + * + * \return The YamlObject as an element of the list + */ +const YamlObject &YamlObject::operator[](std::size_t index) const +{ + if (type_ != Type::List || index >= size()) + return empty; + + return *list_[index].value; +} + +/** + * \fn YamlObject::contains() + * \brief Check if an element of a dictionary exists + * + * This function check if the YamlObject contains an element. Only YamlObject + * instances of Dictionary type associate elements with names, calling this + * function on other types of instances is invalid and results in undefined + * behaviour. + * + * \return True if an element exists, false otherwise + */ +bool YamlObject::contains(std::string_view key) const +{ + return dictionary_.find(key) != dictionary_.end(); +} + +/** + * \fn YamlObject::operator[](std::string_view key) const + * \brief Retrieve a member by name from the dictionary + * + * This function retrieve a member of a YamlObject by name. Only YamlObject + * instances of Dictionary type associate elements with names, calling this + * function on other types of instances or with a nonexistent key results in an + * empty object. + * + * \return The YamlObject corresponding to the \a key member + */ +const YamlObject &YamlObject::operator[](std::string_view key) const +{ + if (type_ != Type::Dictionary) + return empty; + + auto iter = dictionary_.find(key); + if (iter == dictionary_.end()) + return empty; + + return *iter->second; +} + +/** + * \brief Add a child object to a list + * \param[in] child The child object + * + * Append the \a child object as the last element of this object's children + * list. This object must be empty, in which case it is converted to the + * Type::List type, or be a list. Otherwise, the function returns a nullptr and + * the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::List; + + if (type_ != Type::List) + return nullptr; + + Value &elem = list_.emplace_back(std::string{}, std::move(child)); + return elem.value.get(); +} + +/** + * \brief Add a child object to a dictionary + * \param[in] key The dictionary key + * \param[in] child The child object + * + * Add the \a child object with the given \a key to this object's children. This + * object must be empty, in which case it is converted to the Type::Dictionary + * type, or be a dictionary. Otherwise, the function returns a nullptr and the + * \a child is not modified. + * + * Keys are unique. If a child with the same \a key already exist, the function + * returns a nullptr and the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::Dictionary; + + if (type_ != Type::Dictionary) + return nullptr; + + auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); + if (!inserted) + return nullptr; + + return list_.emplace_back(it->first, std::move(child)).value.get(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 46369c6bc6a3..91c8f3e5b84f 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -7,11 +7,10 @@ #include "libcamera/internal/yaml_parser.h" -#include #include #include -#include -#include +#include +#include #include #include @@ -27,447 +26,6 @@ namespace libcamera { LOG_DEFINE_CATEGORY(YamlParser) -namespace { - -/* Empty static YamlObject as a safe result for invalid operations */ -static const YamlObject empty; - -} /* namespace */ - -/** - * \class YamlObject - * \brief A class representing the tree structure of the YAML content - * - * The YamlObject class represents the tree structure of YAML content. A - * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a - * tree leaf. - */ - -YamlObject::YamlObject() - : type_(Type::Empty) -{ -} - -YamlObject::~YamlObject() = default; - -/** - * \fn YamlObject::isValue() - * \brief Return whether the YamlObject is a value - * - * \return True if the YamlObject is a value, false otherwise - */ - -/** - * \fn YamlObject::isList() - * \brief Return whether the YamlObject is a list - * - * \return True if the YamlObject is a list, false otherwise - */ - -/** - * \fn YamlObject::isDictionary() - * \brief Return whether the YamlObject is a dictionary - * - * \return True if the YamlObject is a dictionary, false otherwise - */ - -/** - * \fn YamlObject::isEmpty() - * \brief Return whether the YamlObject is an empty - * - * \return True if the YamlObject is empty, false otherwise - */ - -/** - * \fn YamlObject::operator bool() - * \brief Return whether the YamlObject is a non-empty - * - * \return False if the YamlObject is empty, true otherwise - */ - -/** - * \brief Retrieve the number of elements in a dictionary or list YamlObject - * - * This function retrieves the size of the YamlObject, defined as the number of - * child elements it contains. Only YamlObject instances of Dictionary or List - * types have a size, calling this function on other types of instances is - * invalid and results in undefined behaviour. - * - * \return The size of the YamlObject - */ -std::size_t YamlObject::size() const -{ - switch (type_) { - case Type::Dictionary: - case Type::List: - return list_.size(); - default: - return 0; - } -} - -/** - * \fn template YamlObject::get() const - * \tparam T Type of the value - * \brief Parse the YamlObject as a \a T value - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), std::nullopt is returned. - * - * If the type \a T is an std::vector, the YamlObject will be parsed as a list - * of values. - * - * \return The YamlObject value, or std::nullopt if parsing failed - */ - -/** - * \fn template YamlObject::get(U &&defaultValue) const - * \brief Parse the YamlObject as a \a T value - * \param[in] defaultValue The default value when failing to parse - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), the \a defaultValue is returned. - * - * Unlike the get() function, this overload does not support std::vector for the - * type \a T. - * - * \return The YamlObject value, or \a defaultValue if parsing failed - */ - -/** - * \fn template YamlObject::set(T &&value) - * \brief Set the value of a YamlObject - * \param[in] value The value - * - * This function sets the value stored in a YamlObject to \a value. The value is - * converted to a string in an implementation-specific way that guarantees that - * subsequent calls to get() will return the same value. - * - * Values can only be set on YamlObject of Type::Value type or empty YamlObject. - * Attempting to set a value on an object of type Type::Dict or Type::List does - * not modify the YamlObject. - */ - -#ifndef __DOXYGEN__ - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_ == "true") - return true; - else if (obj.value_ == "false") - return false; - - return std::nullopt; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, bool value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = value ? "true" : "false"; -} - -template -struct YamlObject::Accessor || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v>> -{ - std::optional get(const YamlObject &obj) const - { - if (obj.type_ != Type::Value) - return std::nullopt; - - const std::string &str = obj.value_; - T value = {}; - - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), - value); - if (ptr != str.data() + str.size() || ec != std::errc()) - return std::nullopt; - - return value; - } - - void set(YamlObject &obj, T value) - { - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); - } -}; - -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - return obj.get(); -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, float value) -{ - obj.set(std::forward(value)); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_.empty()) - return std::nullopt; - - char *end; - - errno = 0; - double value = utils::strtod(obj.value_.c_str(), &end); - - if ('\0' != *end || errno == ERANGE) - return std::nullopt; - - return value; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, double value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - return obj.value_; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, std::string value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::move(value); -} - -template -struct YamlObject::Accessor> { - std::optional> get(const YamlObject &obj) const - { - if (obj.type_ != Type::List) - return std::nullopt; - - std::vector values; - values.reserve(obj.list_.size()); - - for (const YamlObject &entry : obj.asList()) { - auto value = entry.get(); - if (!value) - return std::nullopt; - values.emplace_back(std::move(*value)); - } - - return values; - } -}; - -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -#endif /* __DOXYGEN__ */ - -/** - * \fn YamlObject::asDict() const - * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of Dictionary type into an adapter that exposes - * iterators, as well as begin() and end() functions, allowing usage of - * range-based for loops with YamlObject. As YAML mappings are not ordered, the - * iteration order is not specified. - * - * The iterator's value_type is a - * std::pair. - * - * If the YamlObject is not of Dictionary type, the returned adapter operates - * as an empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \fn YamlObject::asList() const - * \brief Wrap a list YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of List type into an adapter that exposes iterators, as - * well as begin() and end() functions, allowing usage of range-based for loops - * with YamlObject. As YAML lists are ordered, the iteration order is identical - * to the list order in the YAML data. - * - * The iterator's value_type is a const YamlObject &. - * - * If the YamlObject is not of List type, the returned adapter operates as an - * empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \fn YamlObject::operator[](std::size_t index) const - * \brief Retrieve the element from list YamlObject by index - * - * This function retrieves an element of the YamlObject. Only YamlObject - * instances of List type associate elements with index, calling this function - * on other types of instances or with an invalid index results in an empty - * object. - * - * \return The YamlObject as an element of the list - */ -const YamlObject &YamlObject::operator[](std::size_t index) const -{ - if (type_ != Type::List || index >= size()) - return empty; - - return *list_[index].value; -} - -/** - * \fn YamlObject::contains() - * \brief Check if an element of a dictionary exists - * - * This function check if the YamlObject contains an element. Only YamlObject - * instances of Dictionary type associate elements with names, calling this - * function on other types of instances is invalid and results in undefined - * behaviour. - * - * \return True if an element exists, false otherwise - */ -bool YamlObject::contains(std::string_view key) const -{ - return dictionary_.find(key) != dictionary_.end(); -} - -/** - * \fn YamlObject::operator[](std::string_view key) const - * \brief Retrieve a member by name from the dictionary - * - * This function retrieve a member of a YamlObject by name. Only YamlObject - * instances of Dictionary type associate elements with names, calling this - * function on other types of instances or with a nonexistent key results in an - * empty object. - * - * \return The YamlObject corresponding to the \a key member - */ -const YamlObject &YamlObject::operator[](std::string_view key) const -{ - if (type_ != Type::Dictionary) - return empty; - - auto iter = dictionary_.find(key); - if (iter == dictionary_.end()) - return empty; - - return *iter->second; -} - -/** - * \brief Add a child object to a list - * \param[in] child The child object - * - * Append the \a child object as the last element of this object's children - * list. This object must be empty, in which case it is converted to the - * Type::List type, or be a list. Otherwise, the function returns a nullptr and - * the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::List; - - if (type_ != Type::List) - return nullptr; - - Value &elem = list_.emplace_back(std::string{}, std::move(child)); - return elem.value.get(); -} - -/** - * \brief Add a child object to a dictionary - * \param[in] key The dictionary key - * \param[in] child The child object - * - * Add the \a child object with the given \a key to this object's children. This - * object must be empty, in which case it is converted to the Type::Dictionary - * type, or be a dictionary. Otherwise, the function returns a nullptr and the - * \a child is not modified. - * - * Keys are unique. If a child with the same \a key already exist, the function - * returns a nullptr and the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::Dictionary; - - if (type_ != Type::Dictionary) - return nullptr; - - auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); - if (!inserted) - return nullptr; - - return list_.emplace_back(it->first, std::move(child)).value.get(); -} - #ifndef __DOXYGEN__ class YamlParserContext From patchwork Tue Apr 7 15:34:03 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26458 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 0B094C3303 for ; Tue, 7 Apr 2026 15:34:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3637B62DF3; Tue, 7 Apr 2026 17:34:57 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PpxmMl9l"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 18BC462DD7 for ; Tue, 7 Apr 2026 17:34:53 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6D0C1596 for ; Tue, 7 Apr 2026 17:33:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576005; bh=3iIiFRu/9YK7+zop6Gn5Uy7nj1DOkASglxvZpaFzH0k=; h=From:To:Subject:Date:In-Reply-To:References:From; b=PpxmMl9lx+t+rd+6sWVARcbPKn3UAHwUpMbDKZU8eErcrYAaw/5H17deORNE+jcEq kH43eXYU3L3fog61/Imuwo9d1UFDxhHp1qn+2MRZgGxG/oljESCbFyMyDxl9dDGIIO g/OzN1RmDY6nZ6qhqjTxk42EeFp0C/rWdKiRJfyQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 18/42] libcamera: yaml_object: Miscellaneous documentation improvements Date: Tue, 7 Apr 2026 18:34:03 +0300 Message-ID: <20260407153427.1825999-19-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Fix a few documentation issues: - Drop unneeded \fn - Sort \brief and \tparam correctly - Add missing \tparam and \param - Explain the T and U template parameters for get() - Standardize the naming of dictionary keys as "key" - Fix typo Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Fix typo --- src/libcamera/yaml_object.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp index 90c1eaa85234..5bb78b66c7e7 100644 --- a/src/libcamera/yaml_object.cpp +++ b/src/libcamera/yaml_object.cpp @@ -81,7 +81,6 @@ YamlObject::~YamlObject() = default; */ /** - * \fn YamlObject::size() * \brief Retrieve the number of elements in a dictionary or list YamlObject * * This function retrieves the size of the YamlObject, defined as the number of @@ -104,8 +103,8 @@ std::size_t YamlObject::size() const /** * \fn template YamlObject::get() const - * \tparam T Type of the value * \brief Parse the YamlObject as a \a T value + * \tparam T Type of the value * * This function parses the value of the YamlObject as a \a T object, and * returns the value. If parsing fails (usually because the YamlObject doesn't @@ -120,11 +119,14 @@ std::size_t YamlObject::size() const /** * \fn template YamlObject::get(U &&defaultValue) const * \brief Parse the YamlObject as a \a T value + * \tparam T Type of the value + * \tparam U Type of the default value * \param[in] defaultValue The default value when failing to parse * * This function parses the value of the YamlObject as a \a T object, and * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), the \a defaultValue is returned. + * store a \a T value), the \a defaultValue is returned. Type \a U must be + * convertible to type \a T. * * Unlike the get() function, this overload does not support std::vector for the * type \a T. @@ -135,6 +137,7 @@ std::size_t YamlObject::size() const /** * \fn template YamlObject::set(T &&value) * \brief Set the value of a YamlObject + * \tparam T Type of the value * \param[in] value The value * * This function sets the value stored in a YamlObject to \a value. The value is @@ -352,8 +355,8 @@ template struct YamlObject::Accessor>; */ /** - * \fn YamlObject::operator[](std::size_t index) const * \brief Retrieve the element from list YamlObject by index + * \param[in] index The element index * * This function retrieves an element of the YamlObject. Only YamlObject * instances of List type associate elements with index, calling this function @@ -371,13 +374,13 @@ const YamlObject &YamlObject::operator[](std::size_t index) const } /** - * \fn YamlObject::contains() * \brief Check if an element of a dictionary exists + * \param[in] key The element key * - * This function check if the YamlObject contains an element. Only YamlObject - * instances of Dictionary type associate elements with names, calling this - * function on other types of instances is invalid and results in undefined - * behaviour. + * This function checks if the YamlObject contains an element for the given + * \a key. Only YamlObject instances of Dictionary type associate elements with + * keys, calling this function on other types of instances is invalid and + * results in undefined behaviour. * * \return True if an element exists, false otherwise */ @@ -387,11 +390,11 @@ bool YamlObject::contains(std::string_view key) const } /** - * \fn YamlObject::operator[](std::string_view key) const - * \brief Retrieve a member by name from the dictionary + * \brief Retrieve a member by key from the dictionary + * \param[in] key The element key * - * This function retrieve a member of a YamlObject by name. Only YamlObject - * instances of Dictionary type associate elements with names, calling this + * This function retrieves a member of a YamlObject by \a key. Only YamlObject + * instances of Dictionary type associate elements with keys, calling this * function on other types of instances or with a nonexistent key results in an * empty object. * From patchwork Tue Apr 7 15:34:04 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26459 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 13BF8BDCBD for ; Tue, 7 Apr 2026 15:35:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 77CBC62E04; Tue, 7 Apr 2026 17:35:00 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WY4lQHn4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EF7D962DE0 for ; Tue, 7 Apr 2026 17:34:54 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id D4D95596 for ; Tue, 7 Apr 2026 17:33:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576007; bh=mmoLOo/Giw+ifZHPXcB7yGh6ojWFqngA8oBEkPX7trc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=WY4lQHn4yIQKoMPOphh/XysI8OI5g9Z8F9b1LcOnzcHNWR+y9xMhmhRaJopMDALIV w2Wn8bjmYAYYK9a8WUbmBv9EE3ShHxQcciI6dUler8K2kWPhSML6oh1occAIGm5xIv nNdwsSFNidWCRGAbes0OSfZCeG5h8/OpdUnk3rIY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 19/42] libcamera: Rename YamlObject to ValueNode Date: Tue, 7 Apr 2026 18:34:04 +0300 Message-ID: <20260407153427.1825999-20-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 YamlObject class is now a generic data container to model trees of values. Rename it to ValueNode and expand the class documentation. While at it, drop the unneeded libcamera:: namespace prefix when using the ValueNode class. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- .../internal/converter/converter_dw100.h | 2 +- .../libcamera/internal/global_configuration.h | 10 +- include/libcamera/internal/matrix.h | 10 +- include/libcamera/internal/meson.build | 2 +- .../internal/{yaml_object.h => value_node.h} | 34 +- include/libcamera/internal/vector.h | 10 +- include/libcamera/internal/yaml_parser.h | 4 +- src/android/camera_hal_config.cpp | 16 +- src/ipa/ipu3/algorithms/agc.cpp | 4 +- src/ipa/ipu3/algorithms/agc.h | 2 +- src/ipa/ipu3/ipu3.cpp | 2 +- src/ipa/libipa/agc_mean_luminance.cpp | 16 +- src/ipa/libipa/agc_mean_luminance.h | 12 +- src/ipa/libipa/algorithm.cpp | 2 +- src/ipa/libipa/algorithm.h | 4 +- src/ipa/libipa/awb.cpp | 6 +- src/ipa/libipa/awb.h | 6 +- src/ipa/libipa/awb_bayes.cpp | 4 +- src/ipa/libipa/awb_bayes.h | 6 +- src/ipa/libipa/awb_grey.cpp | 2 +- src/ipa/libipa/awb_grey.h | 2 +- src/ipa/libipa/interpolator.cpp | 2 +- src/ipa/libipa/interpolator.h | 4 +- src/ipa/libipa/lsc_polynomial.h | 6 +- src/ipa/libipa/lux.cpp | 6 +- src/ipa/libipa/lux.h | 4 +- src/ipa/libipa/module.cpp | 2 +- src/ipa/libipa/module.h | 6 +- src/ipa/libipa/pwl.cpp | 2 +- src/ipa/mali-c55/algorithms/agc.cpp | 2 +- src/ipa/mali-c55/algorithms/agc.h | 2 +- src/ipa/mali-c55/algorithms/blc.cpp | 4 +- src/ipa/mali-c55/algorithms/blc.h | 2 +- src/ipa/mali-c55/algorithms/lsc.cpp | 6 +- src/ipa/mali-c55/algorithms/lsc.h | 2 +- src/ipa/mali-c55/mali-c55.cpp | 2 +- src/ipa/rkisp1/algorithms/agc.cpp | 10 +- src/ipa/rkisp1/algorithms/agc.h | 4 +- src/ipa/rkisp1/algorithms/awb.cpp | 2 +- src/ipa/rkisp1/algorithms/awb.h | 2 +- src/ipa/rkisp1/algorithms/blc.cpp | 4 +- src/ipa/rkisp1/algorithms/blc.h | 2 +- src/ipa/rkisp1/algorithms/ccm.cpp | 4 +- src/ipa/rkisp1/algorithms/ccm.h | 4 +- src/ipa/rkisp1/algorithms/cproc.cpp | 2 +- src/ipa/rkisp1/algorithms/cproc.h | 2 +- src/ipa/rkisp1/algorithms/dpcc.cpp | 22 +- src/ipa/rkisp1/algorithms/dpcc.h | 2 +- src/ipa/rkisp1/algorithms/dpf.cpp | 8 +- src/ipa/rkisp1/algorithms/dpf.h | 2 +- src/ipa/rkisp1/algorithms/filter.cpp | 2 +- src/ipa/rkisp1/algorithms/filter.h | 2 +- src/ipa/rkisp1/algorithms/goc.cpp | 4 +- src/ipa/rkisp1/algorithms/goc.h | 2 +- src/ipa/rkisp1/algorithms/gsl.cpp | 6 +- src/ipa/rkisp1/algorithms/gsl.h | 2 +- src/ipa/rkisp1/algorithms/lsc.cpp | 20 +- src/ipa/rkisp1/algorithms/lsc.h | 2 +- src/ipa/rkisp1/algorithms/lux.cpp | 2 +- src/ipa/rkisp1/algorithms/lux.h | 2 +- src/ipa/rkisp1/algorithms/wdr.cpp | 4 +- src/ipa/rkisp1/algorithms/wdr.h | 2 +- src/ipa/rkisp1/rkisp1.cpp | 2 +- src/ipa/rpi/controller/algorithm.cpp | 2 +- src/ipa/rpi/controller/algorithm.h | 4 +- src/ipa/rpi/controller/controller.cpp | 4 +- src/ipa/rpi/controller/controller.h | 4 +- src/ipa/rpi/controller/rpi/af.cpp | 10 +- src/ipa/rpi/controller/rpi/af.h | 8 +- src/ipa/rpi/controller/rpi/agc.cpp | 2 +- src/ipa/rpi/controller/rpi/agc.h | 2 +- src/ipa/rpi/controller/rpi/agc_channel.cpp | 24 +- src/ipa/rpi/controller/rpi/agc_channel.h | 12 +- src/ipa/rpi/controller/rpi/alsc.cpp | 10 +- src/ipa/rpi/controller/rpi/alsc.h | 2 +- src/ipa/rpi/controller/rpi/awb.cpp | 6 +- src/ipa/rpi/controller/rpi/awb.h | 4 +- src/ipa/rpi/controller/rpi/awb_bayes.cpp | 12 +- src/ipa/rpi/controller/rpi/black_level.cpp | 2 +- src/ipa/rpi/controller/rpi/black_level.h | 2 +- src/ipa/rpi/controller/rpi/cac.cpp | 4 +- src/ipa/rpi/controller/rpi/cac.h | 2 +- src/ipa/rpi/controller/rpi/ccm.cpp | 2 +- src/ipa/rpi/controller/rpi/ccm.h | 2 +- src/ipa/rpi/controller/rpi/contrast.cpp | 2 +- src/ipa/rpi/controller/rpi/contrast.h | 2 +- src/ipa/rpi/controller/rpi/decompand.cpp | 2 +- src/ipa/rpi/controller/rpi/decompand.h | 2 +- src/ipa/rpi/controller/rpi/denoise.cpp | 4 +- src/ipa/rpi/controller/rpi/denoise.h | 4 +- src/ipa/rpi/controller/rpi/dpc.cpp | 2 +- src/ipa/rpi/controller/rpi/dpc.h | 2 +- src/ipa/rpi/controller/rpi/geq.cpp | 2 +- src/ipa/rpi/controller/rpi/geq.h | 2 +- src/ipa/rpi/controller/rpi/hdr.cpp | 4 +- src/ipa/rpi/controller/rpi/hdr.h | 4 +- src/ipa/rpi/controller/rpi/lux.cpp | 2 +- src/ipa/rpi/controller/rpi/lux.h | 2 +- src/ipa/rpi/controller/rpi/noise.cpp | 2 +- src/ipa/rpi/controller/rpi/noise.h | 2 +- src/ipa/rpi/controller/rpi/saturation.cpp | 2 +- src/ipa/rpi/controller/rpi/saturation.h | 2 +- src/ipa/rpi/controller/rpi/sdn.cpp | 2 +- src/ipa/rpi/controller/rpi/sdn.h | 2 +- src/ipa/rpi/controller/rpi/sharpen.cpp | 2 +- src/ipa/rpi/controller/rpi/sharpen.h | 2 +- src/ipa/rpi/controller/rpi/tonemap.cpp | 2 +- src/ipa/rpi/controller/rpi/tonemap.h | 2 +- src/ipa/simple/algorithms/adjust.cpp | 2 +- src/ipa/simple/algorithms/adjust.h | 2 +- src/ipa/simple/algorithms/blc.cpp | 2 +- src/ipa/simple/algorithms/blc.h | 2 +- src/ipa/simple/algorithms/ccm.cpp | 2 +- src/ipa/simple/algorithms/ccm.h | 2 +- src/ipa/simple/soft_simple.cpp | 2 +- src/libcamera/converter/converter_dw100.cpp | 2 +- src/libcamera/geometry.cpp | 4 +- src/libcamera/global_configuration.cpp | 8 +- src/libcamera/matrix.cpp | 2 +- src/libcamera/meson.build | 2 +- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 2 +- .../pipeline/rpi/common/pipeline_base.cpp | 4 +- .../pipeline/rpi/common/pipeline_base.h | 4 +- src/libcamera/pipeline/rpi/pisp/pisp.cpp | 6 +- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 6 +- src/libcamera/pipeline/virtual/README.md | 2 +- .../pipeline/virtual/config_parser.cpp | 18 +- .../pipeline/virtual/config_parser.h | 12 +- src/libcamera/pipeline/virtual/virtual.cpp | 2 +- src/libcamera/value_node.cpp | 483 ++++++++++++++++++ src/libcamera/vector.cpp | 2 +- src/libcamera/yaml_object.cpp | 470 ----------------- src/libcamera/yaml_parser.cpp | 54 +- test/yaml-parser.cpp | 6 +- 134 files changed, 811 insertions(+), 798 deletions(-) rename include/libcamera/internal/{yaml_object.h => value_node.h} (81%) create mode 100644 src/libcamera/value_node.cpp delete mode 100644 src/libcamera/yaml_object.cpp diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h index 0a41b180a75f..1db1aadcb06b 100644 --- a/include/libcamera/internal/converter/converter_dw100.h +++ b/include/libcamera/internal/converter/converter_dw100.h @@ -31,7 +31,7 @@ public: static std::unique_ptr createModule(DeviceEnumerator *enumerator); - int init(const YamlObject ¶ms); + int init(const ValueNode ¶ms); int configure(const StreamConfiguration &inputCfg, const std::vector> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 16c6a21f2a8a..7ae923977aa6 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -14,14 +14,14 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { class GlobalConfiguration { public: - using Configuration = const YamlObject &; + using Configuration = const ValueNode &; GlobalConfiguration(); @@ -32,7 +32,7 @@ public: std::optional option( const std::initializer_list confPath) const { - const YamlObject *c = &configuration(); + const ValueNode *c = &configuration(); for (auto part : confPath) { c = &(*c)[part]; if (!*c) @@ -55,8 +55,8 @@ private: bool loadFile(const std::filesystem::path &fileName); void load(); - std::unique_ptr yamlConfiguration_ = - std::make_unique(); + std::unique_ptr yamlConfiguration_ = + std::make_unique(); }; } /* namespace libcamera */ diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h index 11fccd27547e..0f72263f2536 100644 --- a/include/libcamera/internal/matrix.h +++ b/include/libcamera/internal/matrix.h @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -188,7 +188,7 @@ constexpr Matrix operator+(const Matrix &m1, const } #ifndef __DOXYGEN__ -bool matrixValidateYaml(const YamlObject &obj, unsigned int size); +bool matrixValidateYaml(const ValueNode &obj, unsigned int size); #endif /* __DOXYGEN__ */ #ifndef __DOXYGEN__ @@ -200,8 +200,8 @@ std::ostream &operator<<(std::ostream &out, const Matrix &m) } template -struct YamlObject::Accessor> { - std::optional> get(const YamlObject &obj) const +struct ValueNode::Accessor> { + std::optional> get(const ValueNode &obj) const { if (!matrixValidateYaml(obj, Rows * Cols)) return std::nullopt; @@ -210,7 +210,7 @@ struct YamlObject::Accessor> { T *data = &matrix[0][0]; unsigned int i = 0; - for (const YamlObject &entry : obj.asList()) { + for (const ValueNode &entry : obj.asList()) { const auto value = entry.get(); if (!value) return std::nullopt; diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 21ce8b391ba2..fd375134a5c4 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -48,8 +48,8 @@ libcamera_internal_headers = files([ 'v4l2_request.h', 'v4l2_subdevice.h', 'v4l2_videodevice.h', + 'value_node.h', 'vector.h', - 'yaml_object.h', 'yaml_parser.h', ]) diff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/value_node.h similarity index 81% rename from include/libcamera/internal/yaml_object.h rename to include/libcamera/internal/value_node.h index 5a937e3d55e0..e42aa9ba0ce0 100644 --- a/include/libcamera/internal/yaml_object.h +++ b/include/libcamera/internal/value_node.h @@ -3,7 +3,7 @@ * Copyright (C) 2022, Google Inc. * Copyright (C) 2025, Ideas on Board * - * libcamera YAML object + * Data structure to manage tree of values */ #pragma once @@ -22,16 +22,16 @@ namespace libcamera { -class YamlObject +class ValueNode { private: struct Value { - Value(std::string k, std::unique_ptr &&v) + Value(std::string k, std::unique_ptr &&v) : key(std::move(k)), value(std::move(v)) { } std::string key; - std::unique_ptr value; + std::unique_ptr value; }; using ValueContainer = std::vector; @@ -103,8 +103,8 @@ public: class ListIterator : public Iterator { public: - using value_type = const YamlObject &; - using pointer = const YamlObject *; + using value_type = const ValueNode &; + using pointer = const ValueNode *; using reference = value_type; value_type operator*() const @@ -121,7 +121,7 @@ public: class DictIterator : public Iterator { public: - using value_type = std::pair; + using value_type = std::pair; using pointer = value_type *; using reference = value_type &; @@ -142,8 +142,8 @@ public: }; #endif /* __DOXYGEN__ */ - YamlObject(); - ~YamlObject(); + ValueNode(); + ~ValueNode(); bool isValue() const { @@ -190,16 +190,16 @@ public: DictAdapter asDict() const { return DictAdapter{ list_ }; } ListAdapter asList() const { return ListAdapter{ list_ }; } - const YamlObject &operator[](std::size_t index) const; + const ValueNode &operator[](std::size_t index) const; bool contains(std::string_view key) const; - const YamlObject &operator[](std::string_view key) const; + const ValueNode &operator[](std::string_view key) const; - YamlObject *add(std::unique_ptr &&child); - YamlObject *add(std::string key, std::unique_ptr &&child); + ValueNode *add(std::unique_ptr &&child); + ValueNode *add(std::string key, std::unique_ptr &&child); private: - LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) + LIBCAMERA_DISABLE_COPY_AND_MOVE(ValueNode) template friend struct Accessor; @@ -213,15 +213,15 @@ private: template struct Accessor { - std::optional get(const YamlObject &obj) const; - void set(YamlObject &obj, T value); + std::optional get(const ValueNode &obj) const; + void set(ValueNode &obj, T value); }; Type type_; std::string value_; ValueContainer list_; - std::map> dictionary_; + std::map> dictionary_; }; } /* namespace libcamera */ diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h index af24485f3bb1..ed7490e16e9e 100644 --- a/include/libcamera/internal/vector.h +++ b/include/libcamera/internal/vector.h @@ -19,7 +19,7 @@ #include #include "libcamera/internal/matrix.h" -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -329,7 +329,7 @@ bool operator!=(const Vector &lhs, const Vector &rhs) } #ifndef __DOXYGEN__ -bool vectorValidateYaml(const YamlObject &obj, unsigned int size); +bool vectorValidateYaml(const ValueNode &obj, unsigned int size); #endif /* __DOXYGEN__ */ #ifndef __DOXYGEN__ @@ -347,8 +347,8 @@ std::ostream &operator<<(std::ostream &out, const Vector &v) } template -struct YamlObject::Accessor> { - std::optional> get(const YamlObject &obj) const +struct ValueNode::Accessor> { + std::optional> get(const ValueNode &obj) const { if (!vectorValidateYaml(obj, Rows)) return std::nullopt; @@ -356,7 +356,7 @@ struct YamlObject::Accessor> { Vector vector; unsigned int i = 0; - for (const YamlObject &entry : obj.asList()) { + for (const ValueNode &entry : obj.asList()) { const auto value = entry.get(); if (!value) return std::nullopt; diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index e503e83a80da..9d4ba0a1d0e1 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -9,7 +9,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -18,7 +18,7 @@ class File; class YamlParser final { public: - static std::unique_ptr parse(File &file); + static std::unique_ptr parse(File &file); }; } /* namespace libcamera */ diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp index 7ef451ef8ab9..4a9c0577a0be 100644 --- a/src/android/camera_hal_config.cpp +++ b/src/android/camera_hal_config.cpp @@ -30,9 +30,9 @@ public: int parseConfigFile(File &file, std::map *cameras); private: - int parseCameraConfigData(const std::string &cameraId, const YamlObject &); - int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData); - int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData); + int parseCameraConfigData(const std::string &cameraId, const ValueNode &); + int parseLocation(const ValueNode &, CameraConfigData &cameraConfigData); + int parseRotation(const ValueNode &, CameraConfigData &cameraConfigData); std::map *cameras_; }; @@ -65,7 +65,7 @@ int CameraHalConfig::Private::parseConfigFile(File &file, cameras_ = cameras; - std::unique_ptr root = YamlParser::parse(file); + std::unique_ptr root = YamlParser::parse(file); if (!root) return -EINVAL; @@ -76,7 +76,7 @@ int CameraHalConfig::Private::parseConfigFile(File &file, if (!root->contains("cameras")) return -EINVAL; - const YamlObject &yamlObjectCameras = (*root)["cameras"]; + const ValueNode &yamlObjectCameras = (*root)["cameras"]; if (!yamlObjectCameras.isDictionary()) return -EINVAL; @@ -90,7 +90,7 @@ int CameraHalConfig::Private::parseConfigFile(File &file, } int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId, - const YamlObject &cameraObject) + const ValueNode &cameraObject) { if (!cameraObject.isDictionary()) @@ -109,7 +109,7 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId, return 0; } -int CameraHalConfig::Private::parseLocation(const YamlObject &cameraObject, +int CameraHalConfig::Private::parseLocation(const ValueNode &cameraObject, CameraConfigData &cameraConfigData) { if (!cameraObject.contains("location")) @@ -127,7 +127,7 @@ int CameraHalConfig::Private::parseLocation(const YamlObject &cameraObject, return 0; } -int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject, +int CameraHalConfig::Private::parseRotation(const ValueNode &cameraObject, CameraConfigData &cameraConfigData) { if (!cameraObject.contains("rotation")) diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index b0d89541da85..d6a7036c6504 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -65,14 +65,14 @@ Agc::Agc() /** * \brief Initialise the AGC algorithm from tuning files * \param[in] context The shared IPA context - * \param[in] tuningData The YamlObject containing Agc tuning data + * \param[in] tuningData The ValueNode containing Agc tuning data * * This function calls the base class' tuningData parsers to discover which * control values are supported. * * \return 0 on success or errors from the base class */ -int Agc::init(IPAContext &context, const YamlObject &tuningData) +int Agc::init(IPAContext &context, const ValueNode &tuningData) { int ret; diff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h index 890c271b4462..d274a2350485 100644 --- a/src/ipa/ipu3/algorithms/agc.h +++ b/src/ipa/ipu3/algorithms/agc.h @@ -30,7 +30,7 @@ public: Agc(); ~Agc() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; void process(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 92f5bd072134..4bdc4b7677fe 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -324,7 +324,7 @@ int IPAIPU3::init(const IPASettings &settings, return ret; } - std::unique_ptr data = YamlParser::parse(file); + std::unique_ptr data = YamlParser::parse(file); if (!data) return -EINVAL; diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 72988096d384..1d385551adc6 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -159,7 +159,7 @@ AgcMeanLuminance::AgcMeanLuminance() AgcMeanLuminance::~AgcMeanLuminance() = default; -int AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) +int AgcMeanLuminance::parseRelativeLuminanceTarget(const ValueNode &tuningData) { auto &target = tuningData["relativeLuminanceTarget"]; if (!target) { @@ -178,7 +178,7 @@ int AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) return 0; } -int AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) +int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id) { for (const auto &[boundName, content] : modeDict.asDict()) { if (boundName != "upper" && boundName != "lower") { @@ -212,11 +212,11 @@ int AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) return 0; } -int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData) +int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData) { std::vector availableConstraintModes; - const YamlObject &yamlConstraintModes = tuningData[controls::AeConstraintMode.name()]; + const ValueNode &yamlConstraintModes = tuningData[controls::AeConstraintMode.name()]; if (yamlConstraintModes.isDictionary()) { for (const auto &[modeName, modeDict] : yamlConstraintModes.asDict()) { if (AeConstraintModeNameValueMap.find(modeName) == @@ -267,11 +267,11 @@ int AgcMeanLuminance::parseConstraintModes(const YamlObject &tuningData) return 0; } -int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData) +int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData) { std::vector availableExposureModes; - const YamlObject &yamlExposureModes = tuningData[controls::AeExposureMode.name()]; + const ValueNode &yamlExposureModes = tuningData[controls::AeExposureMode.name()]; if (yamlExposureModes.isDictionary()) { for (const auto &[modeName, modeValues] : yamlExposureModes.asDict()) { if (AeExposureModeNameValueMap.find(modeName) == @@ -361,7 +361,7 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, /** * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls - * \param[in] tuningData the YamlObject representing the tuning data + * \param[in] tuningData the ValueNode representing the tuning data * * This function parses tuning data to build the list of allowed values for the * AeConstraintMode and AeExposureMode controls. Those tuning data must provide @@ -414,7 +414,7 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration, * * \return 0 on success or a negative error code */ -int AgcMeanLuminance::parseTuningData(const YamlObject &tuningData) +int AgcMeanLuminance::parseTuningData(const ValueNode &tuningData) { int ret; diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index dfe1ccbe948b..27e92b6fce7a 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "exposure_mode_helper.h" #include "histogram.h" @@ -44,7 +44,7 @@ public: }; void configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper); - int parseTuningData(const YamlObject &tuningData); + int parseTuningData(const ValueNode &tuningData); void setExposureCompensation(double gain) { @@ -88,10 +88,10 @@ public: private: virtual double estimateLuminance(const double gain) const = 0; - int parseRelativeLuminanceTarget(const YamlObject &tuningData); - int parseConstraint(const YamlObject &modeDict, int32_t id); - int parseConstraintModes(const YamlObject &tuningData); - int parseExposureModes(const YamlObject &tuningData); + int parseRelativeLuminanceTarget(const ValueNode &tuningData); + int parseConstraint(const ValueNode &modeDict, int32_t id); + int parseConstraintModes(const ValueNode &tuningData); + int parseExposureModes(const ValueNode &tuningData); double estimateInitialGain() const; double constraintClampGain(uint32_t constraintModeIndex, const Histogram &hist, diff --git a/src/ipa/libipa/algorithm.cpp b/src/ipa/libipa/algorithm.cpp index 201efdfdba25..757ce3519652 100644 --- a/src/ipa/libipa/algorithm.cpp +++ b/src/ipa/libipa/algorithm.cpp @@ -44,7 +44,7 @@ namespace ipa { * \param[in] tuningData The tuning data for the algorithm * * This function is called once, when the IPA module is initialized, to - * initialize the algorithm. The \a tuningData YamlObject contains the tuning + * initialize the algorithm. The \a tuningData ValueNode contains the tuning * data for algorithm. * * \return 0 if successful, an error code otherwise diff --git a/src/ipa/libipa/algorithm.h b/src/ipa/libipa/algorithm.h index 9a19dbd61b31..4ddb16ef3920 100644 --- a/src/ipa/libipa/algorithm.h +++ b/src/ipa/libipa/algorithm.h @@ -14,7 +14,7 @@ namespace libcamera { -class YamlObject; +class ValueNode; namespace ipa { @@ -27,7 +27,7 @@ public: virtual ~Algorithm() {} virtual int init([[maybe_unused]] typename Module::Context &context, - [[maybe_unused]] const YamlObject &tuningData) + [[maybe_unused]] const ValueNode &tuningData) { return 0; } diff --git a/src/ipa/libipa/awb.cpp b/src/ipa/libipa/awb.cpp index 214bac8b56a6..af966f1e007c 100644 --- a/src/ipa/libipa/awb.cpp +++ b/src/ipa/libipa/awb.cpp @@ -132,7 +132,7 @@ namespace ipa { /** * \brief Parse the mode configurations from the tuning data - * \param[in] tuningData the YamlObject representing the tuning data + * \param[in] tuningData the ValueNode representing the tuning data * \param[in] def The default value for the AwbMode control * * Utility function to parse the tuning data for an AwbMode entry and read all @@ -162,12 +162,12 @@ namespace ipa { * \sa controls::AwbModeEnum * \return Zero on success, negative error code otherwise */ -int AwbAlgorithm::parseModeConfigs(const YamlObject &tuningData, +int AwbAlgorithm::parseModeConfigs(const ValueNode &tuningData, const ControlValue &def) { std::vector availableModes; - const YamlObject &yamlModes = tuningData[controls::AwbMode.name()]; + const ValueNode &yamlModes = tuningData[controls::AwbMode.name()]; if (!yamlModes.isDictionary()) { LOG(Awb, Error) << "AwbModes must be a dictionary."; diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index 3f25d13feaa8..09c00e47d604 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -13,8 +13,8 @@ #include #include +#include "libcamera/internal/value_node.h" #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_object.h" namespace libcamera { @@ -38,7 +38,7 @@ class AwbAlgorithm public: virtual ~AwbAlgorithm() = default; - virtual int init(const YamlObject &tuningData) = 0; + virtual int init(const ValueNode &tuningData) = 0; virtual AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) = 0; virtual std::optional> gainsFromColourTemperature(double colourTemperature) = 0; @@ -50,7 +50,7 @@ public: virtual void handleControls([[maybe_unused]] const ControlList &controls) {} protected: - int parseModeConfigs(const YamlObject &tuningData, + int parseModeConfigs(const ValueNode &tuningData, const ControlValue &def = {}); struct ModeConfig { diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp index 595bd0705732..a1412c8bd2b5 100644 --- a/src/ipa/libipa/awb_bayes.cpp +++ b/src/ipa/libipa/awb_bayes.cpp @@ -143,7 +143,7 @@ void Interpolator::interpolate(const Pwl &a, const Pwl &b, Pwl &dest, doubl * \brief The currently selected mode */ -int AwbBayes::init(const YamlObject &tuningData) +int AwbBayes::init(const ValueNode &tuningData) { int ret = colourGainCurve_.readYaml(tuningData["colourGains"], "ct", "gains"); if (ret) { @@ -188,7 +188,7 @@ int AwbBayes::init(const YamlObject &tuningData) return 0; } -int AwbBayes::readPriors(const YamlObject &tuningData) +int AwbBayes::readPriors(const ValueNode &tuningData) { const auto &priorsList = tuningData["priors"]; std::map priors; diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h index 79334ad3e7a3..1e3373676bc0 100644 --- a/src/ipa/libipa/awb_bayes.h +++ b/src/ipa/libipa/awb_bayes.h @@ -9,8 +9,8 @@ #include +#include "libcamera/internal/value_node.h" #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_object.h" #include "awb.h" #include "interpolator.h" @@ -25,13 +25,13 @@ class AwbBayes : public AwbAlgorithm public: AwbBayes() = default; - int init(const YamlObject &tuningData) override; + int init(const ValueNode &tuningData) override; AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override; std::optional> gainsFromColourTemperature(double temperatureK) override; void handleControls(const ControlList &controls) override; private: - int readPriors(const YamlObject &tuningData); + int readPriors(const ValueNode &tuningData); void fineSearch(double &t, double &r, double &b, ipa::Pwl const &prior, const AwbStats &stats) const; diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp index d252edb2b6c6..b14b096810ae 100644 --- a/src/ipa/libipa/awb_grey.cpp +++ b/src/ipa/libipa/awb_grey.cpp @@ -41,7 +41,7 @@ namespace ipa { * * \return 0 on success, a negative error code otherwise */ -int AwbGrey::init(const YamlObject &tuningData) +int AwbGrey::init(const ValueNode &tuningData) { Interpolator> gains; int ret = gains.readYaml(tuningData["colourGains"], "ct", "gains"); diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h index f82a368d11cc..154a2af97f15 100644 --- a/src/ipa/libipa/awb_grey.h +++ b/src/ipa/libipa/awb_grey.h @@ -23,7 +23,7 @@ class AwbGrey : public AwbAlgorithm public: AwbGrey() = default; - int init(const YamlObject &tuningData) override; + int init(const ValueNode &tuningData) override; AwbResult calculateAwb(const AwbStats &stats, unsigned int lux) override; std::optional> gainsFromColourTemperature(double colourTemperature) override; diff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp index 00bf66b6c2af..d5ab932bc269 100644 --- a/src/ipa/libipa/interpolator.cpp +++ b/src/ipa/libipa/interpolator.cpp @@ -48,7 +48,7 @@ namespace ipa { */ /** - * \fn int Interpolator::readYaml(const libcamera::YamlObject &yaml, + * \fn int Interpolator::readYaml(const ValueNode &yaml, const std::string &key_name, const std::string &value_name) * \brief Initialize an Interpolator instance from yaml diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h index af35fc556a9b..cc4b27b52cfa 100644 --- a/src/ipa/libipa/interpolator.h +++ b/src/ipa/libipa/interpolator.h @@ -15,7 +15,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -39,7 +39,7 @@ public: ~Interpolator() = default; - int readYaml(const libcamera::YamlObject &yaml, + int readYaml(const ValueNode &yaml, const std::string &key_name, const std::string &value_name) { diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index 19da46860849..d7d9ae42e360 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -83,8 +83,8 @@ private: #ifndef __DOXYGEN__ template<> -struct YamlObject::Accessor { - std::optional get(const YamlObject &obj) const +struct ValueNode::Accessor { + std::optional get(const ValueNode &obj) const { std::optional cx = obj["cx"].get(); std::optional cy = obj["cy"].get(); diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp index d79b116a3339..46cc63a115b5 100644 --- a/src/ipa/libipa/lux.cpp +++ b/src/ipa/libipa/lux.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "histogram.h" @@ -78,7 +78,7 @@ Lux::Lux() /** * \brief Parse tuning data - * \param[in] tuningData The YamlObject representing the tuning data + * \param[in] tuningData The ValueNode representing the tuning data * * This function parses yaml tuning data for the common Lux module. It requires * reference exposure time, analogue gain, digital gain, and lux values. @@ -95,7 +95,7 @@ Lux::Lux() * * \return 0 on success or a negative error code */ -int Lux::parseTuningData(const YamlObject &tuningData) +int Lux::parseTuningData(const ValueNode &tuningData) { auto value = tuningData["referenceExposureTime"].get(); if (!value) { diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h index d95bcdafcfcd..b6837ab729de 100644 --- a/src/ipa/libipa/lux.h +++ b/src/ipa/libipa/lux.h @@ -12,7 +12,7 @@ namespace libcamera { -class YamlObject; +class ValueNode; namespace ipa { @@ -23,7 +23,7 @@ class Lux public: Lux(); - int parseTuningData(const YamlObject &tuningData); + int parseTuningData(const ValueNode &tuningData); double estimateLux(utils::Duration exposureTime, double aGain, double dGain, const Histogram &yHist) const; diff --git a/src/ipa/libipa/module.cpp b/src/ipa/libipa/module.cpp index a95dca696adf..76e10e250b73 100644 --- a/src/ipa/libipa/module.cpp +++ b/src/ipa/libipa/module.cpp @@ -87,7 +87,7 @@ namespace ipa { * \fn Module::createAlgorithms() * \brief Create algorithms from YAML configuration data * \param[in] context The IPA context - * \param[in] algorithms Algorithms configuration data as a parsed YamlObject + * \param[in] algorithms Algorithms configuration data as a parsed ValueNode * * This function iterates over the list of \a algorithms parsed from the YAML * configuration file, and instantiates and initializes the corresponding diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h index 8e6c658a6b63..3e2408ca30b6 100644 --- a/src/ipa/libipa/module.h +++ b/src/ipa/libipa/module.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "algorithm.h" @@ -43,7 +43,7 @@ public: return algorithms_; } - int createAlgorithms(Context &context, const YamlObject &algorithms) + int createAlgorithms(Context &context, const ValueNode &algorithms) { const auto &list = algorithms.asList(); @@ -71,7 +71,7 @@ public: } private: - int createAlgorithm(Context &context, const YamlObject &data) + int createAlgorithm(Context &context, const ValueNode &data) { const auto &[name, algoData] = *data.asDict().begin(); diff --git a/src/ipa/libipa/pwl.cpp b/src/ipa/libipa/pwl.cpp index f38573e69c13..9fddd5bf70e2 100644 --- a/src/ipa/libipa/pwl.cpp +++ b/src/ipa/libipa/pwl.cpp @@ -435,7 +435,7 @@ std::string Pwl::toString() const */ template<> std::optional -YamlObject::Accessor::get(const YamlObject &obj) const +ValueNode::Accessor::get(const ValueNode &obj) const { /* Treat a single value as single point PWL. */ if (obj.isValue()) { diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 34d676677efa..83bbf693857e 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -131,7 +131,7 @@ Agc::Agc() { } -int Agc::init(IPAContext &context, const YamlObject &tuningData) +int Agc::init(IPAContext &context, const ValueNode &tuningData) { int ret = parseTuningData(tuningData); if (ret) diff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h index 9684fff664bc..ee913de2b2e2 100644 --- a/src/ipa/mali-c55/algorithms/agc.h +++ b/src/ipa/mali-c55/algorithms/agc.h @@ -49,7 +49,7 @@ public: Agc(); ~Agc() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp index 3bdf19141e1d..591a5eaf2dc8 100644 --- a/src/ipa/mali-c55/algorithms/blc.cpp +++ b/src/ipa/mali-c55/algorithms/blc.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" /** * \file blc.h @@ -36,7 +36,7 @@ BlackLevelCorrection::BlackLevelCorrection() * \copydoc libcamera::ipa::Algorithm::init */ int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { offset00 = tuningData["offset00"].get(0); offset01 = tuningData["offset01"].get(0); diff --git a/src/ipa/mali-c55/algorithms/blc.h b/src/ipa/mali-c55/algorithms/blc.h index fc5a7ea310cb..bce343e20c55 100644 --- a/src/ipa/mali-c55/algorithms/blc.h +++ b/src/ipa/mali-c55/algorithms/blc.h @@ -18,7 +18,7 @@ public: BlackLevelCorrection(); ~BlackLevelCorrection() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void prepare(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index e32f5a83485e..fe230fdff418 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -7,7 +7,7 @@ #include "lsc.h" -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" namespace libcamera { @@ -15,7 +15,7 @@ namespace ipa::mali_c55::algorithms { LOG_DEFINE_CATEGORY(MaliC55Lsc) -int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) +int Lsc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) { if (!tuningData.contains("meshScale")) { LOG(MaliC55Lsc, Error) << "meshScale missing from tuningData"; @@ -24,7 +24,7 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData meshScale_ = tuningData["meshScale"].get(0); - const YamlObject &yamlSets = tuningData["sets"]; + const ValueNode &yamlSets = tuningData["sets"]; if (!yamlSets.isList()) { LOG(MaliC55Lsc, Error) << "LSC tables missing or invalid"; return -EINVAL; diff --git a/src/ipa/mali-c55/algorithms/lsc.h b/src/ipa/mali-c55/algorithms/lsc.h index e7092bc74a0b..3d35fd72bfa8 100644 --- a/src/ipa/mali-c55/algorithms/lsc.h +++ b/src/ipa/mali-c55/algorithms/lsc.h @@ -20,7 +20,7 @@ public: Lsc() = default; ~Lsc() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, MaliC55Params *params) override; diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp index fd5c9563d6c3..3315d9a99488 100644 --- a/src/ipa/mali-c55/mali-c55.cpp +++ b/src/ipa/mali-c55/mali-c55.cpp @@ -118,7 +118,7 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig return ret; } - std::unique_ptr data = YamlParser::parse(file); + std::unique_ptr data = YamlParser::parse(file); if (!data) return -EINVAL; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index ef16a3775056..523930488a3b 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -19,7 +19,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "libipa/histogram.h" @@ -40,7 +40,7 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Agc) -int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData) +int Agc::parseMeteringModes(IPAContext &context, const ValueNode &tuningData) { if (!tuningData.isDictionary()) LOG(RkISP1Agc, Warning) @@ -127,14 +127,14 @@ Agc::Agc() /** * \brief Initialise the AGC algorithm from tuning files * \param[in] context The shared IPA context - * \param[in] tuningData The YamlObject containing Agc tuning data + * \param[in] tuningData The ValueNode containing Agc tuning data * * This function calls the base class' tuningData parsers to discover which * control values are supported. * * \return 0 on success or errors from the base class */ -int Agc::init(IPAContext &context, const YamlObject &tuningData) +int Agc::init(IPAContext &context, const ValueNode &tuningData) { int ret; @@ -142,7 +142,7 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) if (ret) return ret; - const YamlObject &yamlMeteringModes = tuningData["AeMeteringMode"]; + const ValueNode &yamlMeteringModes = tuningData["AeMeteringMode"]; ret = parseMeteringModes(context, yamlMeteringModes); if (ret) return ret; diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h index 7867eed9c4e3..dec79f2f399c 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -28,7 +28,7 @@ public: Agc(); ~Agc() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, @@ -43,7 +43,7 @@ public: ControlList &metadata) override; private: - int parseMeteringModes(IPAContext &context, const YamlObject &tuningData); + int parseMeteringModes(IPAContext &context, const ValueNode &tuningData); uint8_t computeHistogramPredivider(const Size &size, enum rkisp1_cif_isp_histogram_mode mode); diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp index f83da545be85..5ae5b6471643 100644 --- a/src/ipa/rkisp1/algorithms/awb.cpp +++ b/src/ipa/rkisp1/algorithms/awb.cpp @@ -84,7 +84,7 @@ Awb::Awb() /** * \copydoc libcamera::ipa::Algorithm::init */ -int Awb::init(IPAContext &context, const YamlObject &tuningData) +int Awb::init(IPAContext &context, const ValueNode &tuningData) { auto &cmap = context.ctrlMap; cmap[&controls::ColourTemperature] = ControlInfo(kMinColourTemperature, diff --git a/src/ipa/rkisp1/algorithms/awb.h b/src/ipa/rkisp1/algorithms/awb.h index 02651cc732bf..60d9ef111495 100644 --- a/src/ipa/rkisp1/algorithms/awb.h +++ b/src/ipa/rkisp1/algorithms/awb.h @@ -24,7 +24,7 @@ public: Awb(); ~Awb() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp index 19c262fffa73..1ed700a205c8 100644 --- a/src/ipa/rkisp1/algorithms/blc.cpp +++ b/src/ipa/rkisp1/algorithms/blc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" /** * \file blc.h @@ -53,7 +53,7 @@ BlackLevelCorrection::BlackLevelCorrection() /** * \copydoc libcamera::ipa::Algorithm::init */ -int BlackLevelCorrection::init(IPAContext &context, const YamlObject &tuningData) +int BlackLevelCorrection::init(IPAContext &context, const ValueNode &tuningData) { std::optional levelRed = tuningData["R"].get(); std::optional levelGreenR = tuningData["Gr"].get(); diff --git a/src/ipa/rkisp1/algorithms/blc.h b/src/ipa/rkisp1/algorithms/blc.h index f797ae44d639..3b2b0ce6e2a8 100644 --- a/src/ipa/rkisp1/algorithms/blc.h +++ b/src/ipa/rkisp1/algorithms/blc.h @@ -19,7 +19,7 @@ public: BlackLevelCorrection(); ~BlackLevelCorrection() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void prepare(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index 567891d115be..6bb9c7bcfcb3 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "libipa/fixedpoint.h" #include "libipa/interpolator.h" @@ -41,7 +41,7 @@ constexpr Matrix kIdentity3x3 = Matrix::identity(); /** * \copydoc libcamera::ipa::Algorithm::init */ -int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) +int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) { auto &cmap = context.ctrlMap; cmap[&controls::ColourCorrectionMatrix] = ControlInfo( diff --git a/src/ipa/rkisp1/algorithms/ccm.h b/src/ipa/rkisp1/algorithms/ccm.h index c301e6e531c8..9ac537426d16 100644 --- a/src/ipa/rkisp1/algorithms/ccm.h +++ b/src/ipa/rkisp1/algorithms/ccm.h @@ -25,7 +25,7 @@ public: Ccm() {} ~Ccm() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, @@ -41,7 +41,7 @@ public: ControlList &metadata) override; private: - void parseYaml(const YamlObject &tuningData); + void parseYaml(const ValueNode &tuningData); void setParameters(struct rkisp1_cif_isp_ctk_config &config, const Matrix &matrix, const Matrix &offsets); diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index 7484e4780094..35ad2d7e0d9d 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -52,7 +52,7 @@ constexpr float kHueScale = -90.0f; * \copydoc libcamera::ipa::Algorithm::init */ int ColorProcessing::init(IPAContext &context, - [[maybe_unused]] const YamlObject &tuningData) + [[maybe_unused]] const ValueNode &tuningData) { auto &cmap = context.ctrlMap; diff --git a/src/ipa/rkisp1/algorithms/cproc.h b/src/ipa/rkisp1/algorithms/cproc.h index 9b589ebd4ad7..1387d4565d3f 100644 --- a/src/ipa/rkisp1/algorithms/cproc.h +++ b/src/ipa/rkisp1/algorithms/cproc.h @@ -21,7 +21,7 @@ public: ColorProcessing() = default; ~ColorProcessing() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp index c195334750e1..eb8cbf2049c5 100644 --- a/src/ipa/rkisp1/algorithms/dpcc.cpp +++ b/src/ipa/rkisp1/algorithms/dpcc.cpp @@ -9,7 +9,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "linux/rkisp1-config.h" @@ -45,7 +45,7 @@ DefectPixelClusterCorrection::DefectPixelClusterCorrection() * \copydoc libcamera::ipa::Algorithm::init */ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { config_.mode = RKISP1_CIF_ISP_DPCC_MODE_STAGE1_ENABLE; config_.output_mode = RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_STAGE1_INCL_G_CENTER @@ -55,7 +55,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, ? RKISP1_CIF_ISP_DPCC_SET_USE_STAGE1_USE_FIX_SET : 0; /* Get all defined sets to apply (up to 3). */ - const YamlObject &setsObject = tuningData["sets"]; + const ValueNode &setsObject = tuningData["sets"]; if (!setsObject.isList()) { LOG(RkISP1Dpcc, Error) << "'sets' parameter not found in tuning file"; @@ -71,14 +71,14 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, for (std::size_t i = 0; i < setsObject.size(); ++i) { struct rkisp1_cif_isp_dpcc_methods_config &method = config_.methods[i]; - const YamlObject &set = setsObject[i]; + const ValueNode &set = setsObject[i]; uint16_t value; /* Enable set if described in YAML tuning file. */ config_.set_use |= 1 << i; /* PG Method */ - const YamlObject &pgObject = set["pg-factor"]; + const ValueNode &pgObject = set["pg-factor"]; if (pgObject.contains("green")) { method.method |= @@ -97,7 +97,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, } /* RO Method */ - const YamlObject &roObject = set["ro-limits"]; + const ValueNode &roObject = set["ro-limits"]; if (roObject.contains("green")) { method.method |= @@ -118,7 +118,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, } /* RG Method */ - const YamlObject &rgObject = set["rg-factor"]; + const ValueNode &rgObject = set["rg-factor"]; method.rg_fac = 0; if (rgObject.contains("green")) { @@ -138,7 +138,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, } /* RND Method */ - const YamlObject &rndOffsetsObject = set["rnd-offsets"]; + const ValueNode &rndOffsetsObject = set["rnd-offsets"]; if (rndOffsetsObject.contains("green")) { method.method |= @@ -158,7 +158,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, RKISP1_CIF_ISP_DPCC_RND_OFFS_n_RB(i, value); } - const YamlObject &rndThresholdObject = set["rnd-threshold"]; + const ValueNode &rndThresholdObject = set["rnd-threshold"]; method.rnd_thresh = 0; if (rndThresholdObject.contains("green")) { @@ -180,7 +180,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, } /* LC Method */ - const YamlObject &lcThresholdObject = set["line-threshold"]; + const ValueNode &lcThresholdObject = set["line-threshold"]; method.line_thresh = 0; if (lcThresholdObject.contains("green")) { @@ -201,7 +201,7 @@ int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context, RKISP1_CIF_ISP_DPCC_LINE_THRESH_RB(value); } - const YamlObject &lcTMadFactorObject = set["line-mad-factor"]; + const ValueNode &lcTMadFactorObject = set["line-mad-factor"]; method.line_mad_fac = 0; if (lcTMadFactorObject.contains("green")) { diff --git a/src/ipa/rkisp1/algorithms/dpcc.h b/src/ipa/rkisp1/algorithms/dpcc.h index b77766c300fb..50b62e9bab3f 100644 --- a/src/ipa/rkisp1/algorithms/dpcc.h +++ b/src/ipa/rkisp1/algorithms/dpcc.h @@ -19,7 +19,7 @@ public: DefectPixelClusterCorrection(); ~DefectPixelClusterCorrection() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) override; diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp index 9961992b339b..5c5b539f1ee8 100644 --- a/src/ipa/rkisp1/algorithms/dpf.cpp +++ b/src/ipa/rkisp1/algorithms/dpf.cpp @@ -45,7 +45,7 @@ Dpf::Dpf() * \copydoc libcamera::ipa::Algorithm::init */ int Dpf::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { std::vector values; @@ -53,7 +53,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * The domain kernel is configured with a 9x9 kernel for the green * pixels, and a 13x9 or 9x9 kernel for red and blue pixels. */ - const YamlObject &dFObject = tuningData["DomainFilter"]; + const ValueNode &dFObject = tuningData["DomainFilter"]; /* * For the green component, we have the 9x9 kernel specified @@ -134,7 +134,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, * which stores a piecewise linear function that characterizes the * sensor noise profile as a noise level function curve (NLF). */ - const YamlObject &rFObject = tuningData["NoiseLevelFunction"]; + const ValueNode &rFObject = tuningData["NoiseLevelFunction"]; std::vector nllValues; nllValues = rFObject["coeff"].get>().value_or(std::vector{}); @@ -162,7 +162,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context, return -EINVAL; } - const YamlObject &fSObject = tuningData["FilterStrength"]; + const ValueNode &fSObject = tuningData["FilterStrength"]; strengthConfig_.r = fSObject["r"].get(64); strengthConfig_.g = fSObject["g"].get(64); diff --git a/src/ipa/rkisp1/algorithms/dpf.h b/src/ipa/rkisp1/algorithms/dpf.h index 2dd8cd362624..b07067cec0a5 100644 --- a/src/ipa/rkisp1/algorithms/dpf.h +++ b/src/ipa/rkisp1/algorithms/dpf.h @@ -21,7 +21,7 @@ public: Dpf(); ~Dpf() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void queueRequest(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls) override; diff --git a/src/ipa/rkisp1/algorithms/filter.cpp b/src/ipa/rkisp1/algorithms/filter.cpp index 8ad79801792f..2e9b4e285503 100644 --- a/src/ipa/rkisp1/algorithms/filter.cpp +++ b/src/ipa/rkisp1/algorithms/filter.cpp @@ -43,7 +43,7 @@ static constexpr uint32_t kFiltModeDefault = 0x000004f2; * \copydoc libcamera::ipa::Algorithm::init */ int Filter::init(IPAContext &context, - [[maybe_unused]] const YamlObject &tuningData) + [[maybe_unused]] const ValueNode &tuningData) { auto &cmap = context.ctrlMap; cmap[&controls::Sharpness] = ControlInfo(0.0f, 10.0f, 1.0f); diff --git a/src/ipa/rkisp1/algorithms/filter.h b/src/ipa/rkisp1/algorithms/filter.h index 37d8938d37bd..9f0188da7880 100644 --- a/src/ipa/rkisp1/algorithms/filter.h +++ b/src/ipa/rkisp1/algorithms/filter.h @@ -21,7 +21,7 @@ public: Filter() = default; ~Filter() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void queueRequest(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls) override; diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp index 6c07bd8c71e5..e8f64bf3d5e0 100644 --- a/src/ipa/rkisp1/algorithms/goc.cpp +++ b/src/ipa/rkisp1/algorithms/goc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "linux/rkisp1-config.h" @@ -48,7 +48,7 @@ const float kDefaultGamma = 2.2f; /** * \copydoc libcamera::ipa::Algorithm::init */ -int GammaOutCorrection::init(IPAContext &context, const YamlObject &tuningData) +int GammaOutCorrection::init(IPAContext &context, const ValueNode &tuningData) { if (context.hw.numGammaOutSamples != RKISP1_CIF_ISP_GAMMA_OUT_MAX_SAMPLES_V10) { diff --git a/src/ipa/rkisp1/algorithms/goc.h b/src/ipa/rkisp1/algorithms/goc.h index bb2ddfc92375..bd79fe19cc86 100644 --- a/src/ipa/rkisp1/algorithms/goc.h +++ b/src/ipa/rkisp1/algorithms/goc.h @@ -19,7 +19,7 @@ public: GammaOutCorrection() = default; ~GammaOutCorrection() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp index 292d0e80dc57..d6272f3a39ef 100644 --- a/src/ipa/rkisp1/algorithms/gsl.cpp +++ b/src/ipa/rkisp1/algorithms/gsl.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "linux/rkisp1-config.h" @@ -56,7 +56,7 @@ GammaSensorLinearization::GammaSensorLinearization() * \copydoc libcamera::ipa::Algorithm::init */ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { std::vector xIntervals = tuningData["x-intervals"].get>().value_or(std::vector{}); @@ -75,7 +75,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context, for (unsigned int i = 0; i < kDegammaXIntervals; ++i) gammaDx_[i / 8] |= (xIntervals[i] & 0x07) << ((i % 8) * 4); - const YamlObject &yObject = tuningData["y"]; + const ValueNode &yObject = tuningData["y"]; if (!yObject.isDictionary()) { LOG(RkISP1Gsl, Error) << "Issue while parsing 'y' in tuning file: " diff --git a/src/ipa/rkisp1/algorithms/gsl.h b/src/ipa/rkisp1/algorithms/gsl.h index 91cf6efa7925..3ef5630713ab 100644 --- a/src/ipa/rkisp1/algorithms/gsl.h +++ b/src/ipa/rkisp1/algorithms/gsl.h @@ -19,7 +19,7 @@ public: GammaSensorLinearization(); ~GammaSensorLinearization() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) override; diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 7302eb58570b..54e374bd9c3c 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "libipa/lsc_polynomial.h" #include "linux/rkisp1-config.h" @@ -184,14 +184,14 @@ public: { } - int parseLscData(const YamlObject &yamlSets, + int parseLscData(const ValueNode &yamlSets, LensShadingCorrection::ShadingDescriptorMap &lscData); private: Size sensorSize_; }; -int LscPolynomialLoader::parseLscData(const YamlObject &yamlSets, +int LscPolynomialLoader::parseLscData(const ValueNode &yamlSets, LensShadingCorrection::ShadingDescriptorMap &lscData) { const auto &sets = yamlSets.asList(); @@ -261,15 +261,15 @@ private: class LscTableLoader { public: - int parseLscData(const YamlObject &yamlSets, + int parseLscData(const ValueNode &yamlSets, LensShadingCorrection::ShadingDescriptorMap &lscData); private: - std::vector parseTable(const YamlObject &tuningData, + std::vector parseTable(const ValueNode &tuningData, const char *prop); }; -int LscTableLoader::parseLscData(const YamlObject &yamlSets, +int LscTableLoader::parseLscData(const ValueNode &yamlSets, LensShadingCorrection::ShadingDescriptorMap &lscData) { const auto &sets = yamlSets.asList(); @@ -310,7 +310,7 @@ int LscTableLoader::parseLscData(const YamlObject &yamlSets, return 0; } -std::vector LscTableLoader::parseTable(const YamlObject &tuningData, +std::vector LscTableLoader::parseTable(const ValueNode &tuningData, const char *prop) { static constexpr unsigned int kLscNumSamples = @@ -329,7 +329,7 @@ std::vector LscTableLoader::parseTable(const YamlObject &tuningData, return table; } -std::vector parseSizes(const YamlObject &tuningData, +std::vector parseSizes(const ValueNode &tuningData, const char *prop) { std::vector sizes = @@ -375,7 +375,7 @@ LensShadingCorrection::LensShadingCorrection() * \copydoc libcamera::ipa::Algorithm::init */ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { xSize_ = parseSizes(tuningData, "x-size"); ySize_ = parseSizes(tuningData, "y-size"); @@ -384,7 +384,7 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context, return -EINVAL; /* Get all defined sets to apply. */ - const YamlObject &yamlSets = tuningData["sets"]; + const ValueNode &yamlSets = tuningData["sets"]; if (!yamlSets.isList()) { LOG(RkISP1Lsc, Error) << "'sets' parameter not found in tuning file"; diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h index fb9dcc1a52af..0a256e225327 100644 --- a/src/ipa/rkisp1/algorithms/lsc.h +++ b/src/ipa/rkisp1/algorithms/lsc.h @@ -24,7 +24,7 @@ public: LensShadingCorrection(); ~LensShadingCorrection() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/rkisp1/algorithms/lux.cpp b/src/ipa/rkisp1/algorithms/lux.cpp index e9717bb3bf66..86e46c492f04 100644 --- a/src/ipa/rkisp1/algorithms/lux.cpp +++ b/src/ipa/rkisp1/algorithms/lux.cpp @@ -41,7 +41,7 @@ Lux::Lux() /** * \copydoc libcamera::ipa::Algorithm::init */ -int Lux::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) +int Lux::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) { return lux_.parseTuningData(tuningData); } diff --git a/src/ipa/rkisp1/algorithms/lux.h b/src/ipa/rkisp1/algorithms/lux.h index e0239848e252..8cb35cbae20d 100644 --- a/src/ipa/rkisp1/algorithms/lux.h +++ b/src/ipa/rkisp1/algorithms/lux.h @@ -22,7 +22,7 @@ class Lux : public Algorithm public: Lux(); - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, RkISP1Params *params) override; diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp index 9e2688a05179..c3d73da2c5b2 100644 --- a/src/ipa/rkisp1/algorithms/wdr.cpp +++ b/src/ipa/rkisp1/algorithms/wdr.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include #include @@ -110,7 +110,7 @@ WideDynamicRange::WideDynamicRange() * \copydoc libcamera::ipa::Algorithm::init */ int WideDynamicRange::init([[maybe_unused]] IPAContext &context, - [[maybe_unused]] const YamlObject &tuningData) + [[maybe_unused]] const ValueNode &tuningData) { if (!(context.hw.supportedBlocks & 1 << RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR)) { LOG(RkISP1Wdr, Error) diff --git a/src/ipa/rkisp1/algorithms/wdr.h b/src/ipa/rkisp1/algorithms/wdr.h index 46f7cdeea69d..f79de66fe73b 100644 --- a/src/ipa/rkisp1/algorithms/wdr.h +++ b/src/ipa/rkisp1/algorithms/wdr.h @@ -23,7 +23,7 @@ public: WideDynamicRange(); ~WideDynamicRange() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; void queueRequest(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 373a343bddd0..58ef163d85ad 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -185,7 +185,7 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, return ret; } - std::unique_ptr data = YamlParser::parse(file); + std::unique_ptr data = YamlParser::parse(file); if (!data) return -EINVAL; diff --git a/src/ipa/rpi/controller/algorithm.cpp b/src/ipa/rpi/controller/algorithm.cpp index beed47a1e1c4..82bc0302fd12 100644 --- a/src/ipa/rpi/controller/algorithm.cpp +++ b/src/ipa/rpi/controller/algorithm.cpp @@ -9,7 +9,7 @@ using namespace RPiController; -int Algorithm::read([[maybe_unused]] const libcamera::YamlObject ¶ms) +int Algorithm::read([[maybe_unused]] const libcamera::ValueNode ¶ms) { return 0; } diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h index 8839daa90916..214b06576bbf 100644 --- a/src/ipa/rpi/controller/algorithm.h +++ b/src/ipa/rpi/controller/algorithm.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "controller.h" @@ -32,7 +32,7 @@ public: } virtual ~Algorithm() = default; virtual char const *name() const = 0; - virtual int read(const libcamera::YamlObject ¶ms); + virtual int read(const libcamera::ValueNode ¶ms); virtual void initialise(); virtual void switchMode(CameraMode const &cameraMode, Metadata *metadata); virtual void prepare(Metadata *imageMetadata); diff --git a/src/ipa/rpi/controller/controller.cpp b/src/ipa/rpi/controller/controller.cpp index 138f6cb29141..168ab6495d80 100644 --- a/src/ipa/rpi/controller/controller.cpp +++ b/src/ipa/rpi/controller/controller.cpp @@ -102,7 +102,7 @@ int Controller::read(char const *filename) return -EINVAL; } - std::unique_ptr root = YamlParser::parse(file); + std::unique_ptr root = YamlParser::parse(file); if (!root) return -EINVAL; @@ -143,7 +143,7 @@ int Controller::read(char const *filename) return 0; } -int Controller::createAlgorithm(const std::string &name, const YamlObject ¶ms) +int Controller::createAlgorithm(const std::string &name, const ValueNode ¶ms) { /* Any algorithm may be disabled by setting "enabled" to false. */ bool enabled = params["enabled"].get(true); diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h index 573942886bc0..094917b08b6a 100644 --- a/src/ipa/rpi/controller/controller.h +++ b/src/ipa/rpi/controller/controller.h @@ -16,7 +16,7 @@ #include #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "camera_mode.h" #include "device_status.h" @@ -65,7 +65,7 @@ public: const HardwareConfig &getHardwareConfig() const; protected: - int createAlgorithm(const std::string &name, const libcamera::YamlObject ¶ms); + int createAlgorithm(const std::string &name, const libcamera::ValueNode ¶ms); Metadata globalMetadata_; std::vector algorithms_; diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp index 26e599303f24..47c2bf935835 100644 --- a/src/ipa/rpi/controller/rpi/af.cpp +++ b/src/ipa/rpi/controller/rpi/af.cpp @@ -68,7 +68,7 @@ Af::CfgParams::CfgParams() } template -static void readNumber(T &dest, const libcamera::YamlObject ¶ms, char const *name) +static void readNumber(T &dest, const libcamera::ValueNode ¶ms, char const *name) { auto value = params[name].get(); if (value) @@ -77,7 +77,7 @@ static void readNumber(T &dest, const libcamera::YamlObject ¶ms, char const LOG(RPiAf, Warning) << "Missing parameter \"" << name << "\""; } -void Af::RangeDependentParams::read(const libcamera::YamlObject ¶ms) +void Af::RangeDependentParams::read(const libcamera::ValueNode ¶ms) { readNumber(focusMin, params, "min"); @@ -85,7 +85,7 @@ void Af::RangeDependentParams::read(const libcamera::YamlObject ¶ms) readNumber(focusDefault, params, "default"); } -void Af::SpeedDependentParams::read(const libcamera::YamlObject ¶ms) +void Af::SpeedDependentParams::read(const libcamera::ValueNode ¶ms) { readNumber(stepCoarse, params, "step_coarse"); readNumber(stepFine, params, "step_fine"); @@ -100,7 +100,7 @@ void Af::SpeedDependentParams::read(const libcamera::YamlObject ¶ms) readNumber(stepFrames, params, "step_frames"); } -int Af::CfgParams::read(const libcamera::YamlObject ¶ms) +int Af::CfgParams::read(const libcamera::ValueNode ¶ms) { if (params.contains("ranges")) { auto &rr = params["ranges"]; @@ -226,7 +226,7 @@ char const *Af::name() const return NAME; } -int Af::read(const libcamera::YamlObject ¶ms) +int Af::read(const libcamera::ValueNode ¶ms) { return cfg_.read(params); } diff --git a/src/ipa/rpi/controller/rpi/af.h b/src/ipa/rpi/controller/rpi/af.h index d35a39d12049..b464927ba92b 100644 --- a/src/ipa/rpi/controller/rpi/af.h +++ b/src/ipa/rpi/controller/rpi/af.h @@ -47,7 +47,7 @@ public: Af(Controller *controller = NULL); ~Af(); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; /* IPA calls */ @@ -87,7 +87,7 @@ private: double focusDefault; /* default setting ("hyperfocal") */ RangeDependentParams(); - void read(const libcamera::YamlObject ¶ms); + void read(const libcamera::ValueNode ¶ms); }; struct SpeedDependentParams { @@ -104,7 +104,7 @@ private: uint32_t stepFrames; /* frames to skip in between steps of a scan */ SpeedDependentParams(); - void read(const libcamera::YamlObject ¶ms); + void read(const libcamera::ValueNode ¶ms); }; struct CfgParams { @@ -118,7 +118,7 @@ private: libcamera::ipa::Pwl map; /* converts dioptres -> lens driver position */ CfgParams(); - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); void initialise(); }; diff --git a/src/ipa/rpi/controller/rpi/agc.cpp b/src/ipa/rpi/controller/rpi/agc.cpp index a8d91f43df29..0c4f25962c3b 100644 --- a/src/ipa/rpi/controller/rpi/agc.cpp +++ b/src/ipa/rpi/controller/rpi/agc.cpp @@ -31,7 +31,7 @@ char const *Agc::name() const return NAME; } -int Agc::read(const libcamera::YamlObject ¶ms) +int Agc::read(const libcamera::ValueNode ¶ms) { /* * When there is only a single channel we can read the old style syntax. diff --git a/src/ipa/rpi/controller/rpi/agc.h b/src/ipa/rpi/controller/rpi/agc.h index 966630a26303..5189ad2a2bf8 100644 --- a/src/ipa/rpi/controller/rpi/agc.h +++ b/src/ipa/rpi/controller/rpi/agc.h @@ -27,7 +27,7 @@ class Agc : public AgcAlgorithm public: Agc(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; unsigned int getConvergenceFrames() const override; std::vector const &getWeights() const override; void setEv(unsigned int channel, double ev) override; diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp index ebef560be84e..cf0e77bdda6a 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp @@ -29,9 +29,9 @@ using namespace std::literals::chrono_literals; LOG_DECLARE_CATEGORY(RPiAgc) -int AgcMeteringMode::read(const libcamera::YamlObject ¶ms) +int AgcMeteringMode::read(const libcamera::ValueNode ¶ms) { - const YamlObject &yamlWeights = params["weights"]; + const ValueNode &yamlWeights = params["weights"]; for (const auto &p : yamlWeights.asList()) { auto value = p.get(); @@ -45,7 +45,7 @@ int AgcMeteringMode::read(const libcamera::YamlObject ¶ms) static std::tuple readMeteringModes(std::map &metering_modes, - const libcamera::YamlObject ¶ms) + const libcamera::ValueNode ¶ms) { std::string first; int ret; @@ -64,7 +64,7 @@ readMeteringModes(std::map &metering_modes, return { 0, first }; } -int AgcExposureMode::read(const libcamera::YamlObject ¶ms) +int AgcExposureMode::read(const libcamera::ValueNode ¶ms) { auto value = params["shutter"].get>(); if (!value) @@ -94,7 +94,7 @@ int AgcExposureMode::read(const libcamera::YamlObject ¶ms) static std::tuple readExposureModes(std::map &exposureModes, - const libcamera::YamlObject ¶ms) + const libcamera::ValueNode ¶ms) { std::string first; int ret; @@ -113,7 +113,7 @@ readExposureModes(std::map &exposureModes, return { 0, first }; } -int AgcConstraint::read(const libcamera::YamlObject ¶ms) +int AgcConstraint::read(const libcamera::ValueNode ¶ms) { std::string boundString = params["bound"].get(""); transform(boundString.begin(), boundString.end(), @@ -139,7 +139,7 @@ int AgcConstraint::read(const libcamera::YamlObject ¶ms) } static std::tuple -readConstraintMode(const libcamera::YamlObject ¶ms) +readConstraintMode(const libcamera::ValueNode ¶ms) { AgcConstraintMode mode; int ret; @@ -158,7 +158,7 @@ readConstraintMode(const libcamera::YamlObject ¶ms) static std::tuple readConstraintModes(std::map &constraintModes, - const libcamera::YamlObject ¶ms) + const libcamera::ValueNode ¶ms) { std::string first; int ret; @@ -175,7 +175,7 @@ readConstraintModes(std::map &constraintModes, return { 0, first }; } -int AgcChannelConstraint::read(const libcamera::YamlObject ¶ms) +int AgcChannelConstraint::read(const libcamera::ValueNode ¶ms) { auto channelValue = params["channel"].get(); if (!channelValue) { @@ -204,7 +204,7 @@ int AgcChannelConstraint::read(const libcamera::YamlObject ¶ms) } static int readChannelConstraints(std::vector &channelConstraints, - const libcamera::YamlObject ¶ms) + const libcamera::ValueNode ¶ms) { for (const auto &p : params.asList()) { AgcChannelConstraint constraint; @@ -218,7 +218,7 @@ static int readChannelConstraints(std::vector &channelCons return 0; } -int AgcConfig::read(const libcamera::YamlObject ¶ms) +int AgcConfig::read(const libcamera::ValueNode ¶ms) { LOG(RPiAgc, Debug) << "AgcConfig"; int ret; @@ -290,7 +290,7 @@ AgcChannel::AgcChannel() status_.ev = ev_; } -int AgcChannel::read(const libcamera::YamlObject ¶ms, +int AgcChannel::read(const libcamera::ValueNode ¶ms, const Controller::HardwareConfig &hardwareConfig) { int ret = config_.read(params); diff --git a/src/ipa/rpi/controller/rpi/agc_channel.h b/src/ipa/rpi/controller/rpi/agc_channel.h index 42d85ec15e8d..90e540a8a18d 100644 --- a/src/ipa/rpi/controller/rpi/agc_channel.h +++ b/src/ipa/rpi/controller/rpi/agc_channel.h @@ -26,13 +26,13 @@ using AgcChannelTotalExposures = std::vector; struct AgcMeteringMode { std::vector weights; - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); }; struct AgcExposureMode { std::vector exposureTime; std::vector gain; - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); }; struct AgcConstraint { @@ -42,7 +42,7 @@ struct AgcConstraint { double qLo; double qHi; libcamera::ipa::Pwl yTarget; - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); }; typedef std::vector AgcConstraintMode; @@ -53,11 +53,11 @@ struct AgcChannelConstraint { Bound bound; unsigned int channel; double factor; - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); }; struct AgcConfig { - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); std::map meteringModes; std::map exposureModes; std::map constraintModes; @@ -85,7 +85,7 @@ class AgcChannel { public: AgcChannel(); - int read(const libcamera::YamlObject ¶ms, + int read(const libcamera::ValueNode ¶ms, const Controller::HardwareConfig &hardwareConfig); unsigned int getConvergenceFrames() const; std::vector const &getWeights() const; diff --git a/src/ipa/rpi/controller/rpi/alsc.cpp b/src/ipa/rpi/controller/rpi/alsc.cpp index 21edb819ad1c..4c852db04f7f 100644 --- a/src/ipa/rpi/controller/rpi/alsc.cpp +++ b/src/ipa/rpi/controller/rpi/alsc.cpp @@ -50,7 +50,7 @@ char const *Alsc::name() const return NAME; } -static int generateLut(Array2D &lut, const libcamera::YamlObject ¶ms) +static int generateLut(Array2D &lut, const libcamera::ValueNode ¶ms) { /* These must be signed ints for the co-ordinate calculations below. */ int X = lut.dimensions().width, Y = lut.dimensions().height; @@ -82,7 +82,7 @@ static int generateLut(Array2D &lut, const libcamera::YamlObject ¶ms return 0; } -static int readLut(Array2D &lut, const libcamera::YamlObject ¶ms) +static int readLut(Array2D &lut, const libcamera::ValueNode ¶ms) { if (params.size() != lut.size()) { LOG(RPiAlsc, Error) << "Invalid number of entries in LSC table"; @@ -101,7 +101,7 @@ static int readLut(Array2D &lut, const libcamera::YamlObject ¶ms) } static int readCalibrations(std::vector &calibrations, - const libcamera::YamlObject ¶ms, + const libcamera::ValueNode ¶ms, std::string const &name, const Size &size) { if (params.contains(name)) { @@ -119,7 +119,7 @@ static int readCalibrations(std::vector &calibrations, AlscCalibration calibration; calibration.ct = lastCt = ct; - const libcamera::YamlObject &table = p["table"]; + const libcamera::ValueNode &table = p["table"]; if (table.size() != size.width * size.height) { LOG(RPiAlsc, Error) << "Incorrect number of values for ct " @@ -144,7 +144,7 @@ static int readCalibrations(std::vector &calibrations, return 0; } -int Alsc::read(const libcamera::YamlObject ¶ms) +int Alsc::read(const libcamera::ValueNode ¶ms) { config_.tableSize = getHardwareConfig().awbRegions; config_.framePeriod = params["frame_period"].get(12); diff --git a/src/ipa/rpi/controller/rpi/alsc.h b/src/ipa/rpi/controller/rpi/alsc.h index 310879820fba..0952ae8358c6 100644 --- a/src/ipa/rpi/controller/rpi/alsc.h +++ b/src/ipa/rpi/controller/rpi/alsc.h @@ -112,7 +112,7 @@ public: char const *name() const override; void initialise() override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; void process(StatisticsPtr &stats, Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/awb.cpp b/src/ipa/rpi/controller/rpi/awb.cpp index de5fa59bf905..1b20d117acaa 100644 --- a/src/ipa/rpi/controller/rpi/awb.cpp +++ b/src/ipa/rpi/controller/rpi/awb.cpp @@ -17,7 +17,7 @@ LOG_DEFINE_CATEGORY(RPiAwb) constexpr double kDefaultCT = 4500.0; -static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject ¶ms) +static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::ValueNode ¶ms) { if (params.size() % 3) { LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry"; @@ -53,7 +53,7 @@ static int readCtCurve(ipa::Pwl &ctR, ipa::Pwl &ctB, const libcamera::YamlObject return 0; } -int AwbMode::read(const libcamera::YamlObject ¶ms) +int AwbMode::read(const libcamera::ValueNode ¶ms) { auto value = params["lo"].get(); if (!value) @@ -68,7 +68,7 @@ int AwbMode::read(const libcamera::YamlObject ¶ms) return 0; } -int AwbConfig::read(const libcamera::YamlObject ¶ms) +int AwbConfig::read(const libcamera::ValueNode ¶ms) { int ret; diff --git a/src/ipa/rpi/controller/rpi/awb.h b/src/ipa/rpi/controller/rpi/awb.h index 5ee0717b8b98..855a3a7c0bd9 100644 --- a/src/ipa/rpi/controller/rpi/awb.h +++ b/src/ipa/rpi/controller/rpi/awb.h @@ -17,7 +17,7 @@ namespace RPiController { struct AwbMode { - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); double ctLo; /* low CT value for search */ double ctHi; /* high CT value for search */ }; @@ -25,7 +25,7 @@ struct AwbMode { struct AwbConfig { AwbConfig() : defaultMode(nullptr) {} - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); bool hasCtCurve() const; /* Only repeat the AWB calculation every "this many" frames */ diff --git a/src/ipa/rpi/controller/rpi/awb_bayes.cpp b/src/ipa/rpi/controller/rpi/awb_bayes.cpp index 7aaac20d2baa..286fe2fb0942 100644 --- a/src/ipa/rpi/controller/rpi/awb_bayes.cpp +++ b/src/ipa/rpi/controller/rpi/awb_bayes.cpp @@ -41,14 +41,14 @@ constexpr double kDefaultCT = 4500.0; namespace RPiController { struct AwbPrior { - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); double lux; /* lux level */ libcamera::ipa::Pwl prior; /* maps CT to prior log likelihood for this lux level */ }; struct AwbBayesConfig { AwbBayesConfig() {} - int read(const libcamera::YamlObject ¶ms, AwbConfig &config); + int read(const libcamera::ValueNode ¶ms, AwbConfig &config); /* table of illuminant priors at different lux levels */ std::vector priors; /* @@ -81,7 +81,7 @@ public: AwbBayes(Controller *controller = NULL); ~AwbBayes(); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; protected: void prepareStats() override; @@ -96,7 +96,7 @@ private: std::vector points_; }; -int AwbPrior::read(const libcamera::YamlObject ¶ms) +int AwbPrior::read(const libcamera::ValueNode ¶ms) { auto value = params["lux"].get(); if (!value) @@ -107,7 +107,7 @@ int AwbPrior::read(const libcamera::YamlObject ¶ms) return prior.empty() ? -EINVAL : 0; } -int AwbBayesConfig::read(const libcamera::YamlObject ¶ms, AwbConfig &config) +int AwbBayesConfig::read(const libcamera::ValueNode ¶ms, AwbConfig &config) { int ret; @@ -175,7 +175,7 @@ char const *AwbBayes::name() const return NAME; } -int AwbBayes::read(const libcamera::YamlObject ¶ms) +int AwbBayes::read(const libcamera::ValueNode ¶ms) { int ret; diff --git a/src/ipa/rpi/controller/rpi/black_level.cpp b/src/ipa/rpi/controller/rpi/black_level.cpp index 4c968f14a1ca..42ea1505014f 100644 --- a/src/ipa/rpi/controller/rpi/black_level.cpp +++ b/src/ipa/rpi/controller/rpi/black_level.cpp @@ -30,7 +30,7 @@ char const *BlackLevel::name() const return NAME; } -int BlackLevel::read(const libcamera::YamlObject ¶ms) +int BlackLevel::read(const libcamera::ValueNode ¶ms) { /* 64 in 10 bits scaled to 16 bits */ uint16_t blackLevel = params["black_level"].get(4096); diff --git a/src/ipa/rpi/controller/rpi/black_level.h b/src/ipa/rpi/controller/rpi/black_level.h index f50729dbc1e3..dbf29b282e4c 100644 --- a/src/ipa/rpi/controller/rpi/black_level.h +++ b/src/ipa/rpi/controller/rpi/black_level.h @@ -18,7 +18,7 @@ class BlackLevel : public BlackLevelAlgorithm public: BlackLevel(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialValues(uint16_t &blackLevelR, uint16_t &blackLevelG, uint16_t &blackLevelB) override; void prepare(Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/cac.cpp b/src/ipa/rpi/controller/rpi/cac.cpp index 17779ad5469b..ddc848f21f3f 100644 --- a/src/ipa/rpi/controller/rpi/cac.cpp +++ b/src/ipa/rpi/controller/rpi/cac.cpp @@ -27,7 +27,7 @@ char const *Cac::name() const return NAME; } -static bool arrayToSet(const libcamera::YamlObject ¶ms, std::vector &inputArray, const Size &size) +static bool arrayToSet(const libcamera::ValueNode ¶ms, std::vector &inputArray, const Size &size) { int num = 0; int max_num = (size.width + 1) * (size.height + 1); @@ -51,7 +51,7 @@ static void setStrength(std::vector &inputArray, std::vector &ou } } -int Cac::read(const libcamera::YamlObject ¶ms) +int Cac::read(const libcamera::ValueNode ¶ms) { config_.enabled = params.contains("lut_rx") && params.contains("lut_ry") && params.contains("lut_bx") && params.contains("lut_by"); diff --git a/src/ipa/rpi/controller/rpi/cac.h b/src/ipa/rpi/controller/rpi/cac.h index 533cca44424b..11c47a7c4323 100644 --- a/src/ipa/rpi/controller/rpi/cac.h +++ b/src/ipa/rpi/controller/rpi/cac.h @@ -24,7 +24,7 @@ class Cac : public Algorithm public: Cac(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; private: diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp index 2806b4967158..d3231182913d 100644 --- a/src/ipa/rpi/controller/rpi/ccm.cpp +++ b/src/ipa/rpi/controller/rpi/ccm.cpp @@ -40,7 +40,7 @@ char const *Ccm::name() const return NAME; } -int Ccm::read(const libcamera::YamlObject ¶ms) +int Ccm::read(const libcamera::ValueNode ¶ms) { if (params.contains("saturation")) { config_.saturation = params["saturation"].get(ipa::Pwl{}); diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h index 70f28ed33d5e..a0f5b698db9b 100644 --- a/src/ipa/rpi/controller/rpi/ccm.h +++ b/src/ipa/rpi/controller/rpi/ccm.h @@ -31,7 +31,7 @@ class Ccm : public CcmAlgorithm public: Ccm(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void enableAuto() override; void setSaturation(double saturation) override; void setCcm(Matrix3x3 const &matrix) override; diff --git a/src/ipa/rpi/controller/rpi/contrast.cpp b/src/ipa/rpi/controller/rpi/contrast.cpp index fe866a544293..3457e04002e0 100644 --- a/src/ipa/rpi/controller/rpi/contrast.cpp +++ b/src/ipa/rpi/controller/rpi/contrast.cpp @@ -38,7 +38,7 @@ char const *Contrast::name() const return NAME; } -int Contrast::read(const libcamera::YamlObject ¶ms) +int Contrast::read(const libcamera::ValueNode ¶ms) { // enable adaptive enhancement by default config_.ceEnable = params["ce_enable"].get(1); diff --git a/src/ipa/rpi/controller/rpi/contrast.h b/src/ipa/rpi/controller/rpi/contrast.h index c0f7db981c7d..3571626d8623 100644 --- a/src/ipa/rpi/controller/rpi/contrast.h +++ b/src/ipa/rpi/controller/rpi/contrast.h @@ -35,7 +35,7 @@ class Contrast : public ContrastAlgorithm public: Contrast(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void setBrightness(double brightness) override; void setContrast(double contrast) override; void enableCe(bool enable) override; diff --git a/src/ipa/rpi/controller/rpi/decompand.cpp b/src/ipa/rpi/controller/rpi/decompand.cpp index 2d457926c060..5646984132f6 100644 --- a/src/ipa/rpi/controller/rpi/decompand.cpp +++ b/src/ipa/rpi/controller/rpi/decompand.cpp @@ -22,7 +22,7 @@ char const *Decompand::name() const return NAME; } -int Decompand::read(const libcamera::YamlObject ¶ms) +int Decompand::read(const libcamera::ValueNode ¶ms) { config_.bitdepth = params["bitdepth"].get(0); config_.decompandCurve = params["decompand_curve"].get(ipa::Pwl{}); diff --git a/src/ipa/rpi/controller/rpi/decompand.h b/src/ipa/rpi/controller/rpi/decompand.h index 6db779c359a8..847769af4338 100644 --- a/src/ipa/rpi/controller/rpi/decompand.h +++ b/src/ipa/rpi/controller/rpi/decompand.h @@ -17,7 +17,7 @@ class Decompand : public DecompandAlgorithm public: Decompand(Controller *controller = nullptr); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; void initialValues(libcamera::ipa::Pwl &decompandCurve) override; diff --git a/src/ipa/rpi/controller/rpi/denoise.cpp b/src/ipa/rpi/controller/rpi/denoise.cpp index cabe3e2a08aa..0cd0dd53fe97 100644 --- a/src/ipa/rpi/controller/rpi/denoise.cpp +++ b/src/ipa/rpi/controller/rpi/denoise.cpp @@ -21,7 +21,7 @@ LOG_DEFINE_CATEGORY(RPiDenoise) #define NAME "rpi.denoise" -int DenoiseConfig::read(const libcamera::YamlObject ¶ms) +int DenoiseConfig::read(const libcamera::ValueNode ¶ms) { sdnEnable = params.contains("sdn"); if (sdnEnable) { @@ -82,7 +82,7 @@ char const *Denoise::name() const return NAME; } -int Denoise::read(const libcamera::YamlObject ¶ms) +int Denoise::read(const libcamera::ValueNode ¶ms) { if (!params.contains("normal")) { configs_["normal"].read(params); diff --git a/src/ipa/rpi/controller/rpi/denoise.h b/src/ipa/rpi/controller/rpi/denoise.h index e23a2e8ff525..499b2ab74749 100644 --- a/src/ipa/rpi/controller/rpi/denoise.h +++ b/src/ipa/rpi/controller/rpi/denoise.h @@ -31,7 +31,7 @@ struct DenoiseConfig { bool tdnEnable; bool sdnEnable; bool cdnEnable; - int read(const libcamera::YamlObject ¶ms); + int read(const libcamera::ValueNode ¶ms); }; class Denoise : public DenoiseAlgorithm @@ -39,7 +39,7 @@ class Denoise : public DenoiseAlgorithm public: Denoise(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; void prepare(Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/dpc.cpp b/src/ipa/rpi/controller/rpi/dpc.cpp index 8aac03f794fc..a92207999fab 100644 --- a/src/ipa/rpi/controller/rpi/dpc.cpp +++ b/src/ipa/rpi/controller/rpi/dpc.cpp @@ -31,7 +31,7 @@ char const *Dpc::name() const return NAME; } -int Dpc::read(const libcamera::YamlObject ¶ms) +int Dpc::read(const libcamera::ValueNode ¶ms) { config_.strength = params["strength"].get(1); if (config_.strength < 0 || config_.strength > 2) { diff --git a/src/ipa/rpi/controller/rpi/dpc.h b/src/ipa/rpi/controller/rpi/dpc.h index 9cefb06d4a7c..a1a02af59cbc 100644 --- a/src/ipa/rpi/controller/rpi/dpc.h +++ b/src/ipa/rpi/controller/rpi/dpc.h @@ -22,7 +22,7 @@ class Dpc : public Algorithm public: Dpc(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; private: diff --git a/src/ipa/rpi/controller/rpi/geq.cpp b/src/ipa/rpi/controller/rpi/geq.cpp index 40e7191ba16a..9382a033f055 100644 --- a/src/ipa/rpi/controller/rpi/geq.cpp +++ b/src/ipa/rpi/controller/rpi/geq.cpp @@ -34,7 +34,7 @@ char const *Geq::name() const return NAME; } -int Geq::read(const libcamera::YamlObject ¶ms) +int Geq::read(const libcamera::ValueNode ¶ms) { config_.offset = params["offset"].get(0); config_.slope = params["slope"].get(0.0); diff --git a/src/ipa/rpi/controller/rpi/geq.h b/src/ipa/rpi/controller/rpi/geq.h index e8b9f42708c0..4827051b4f46 100644 --- a/src/ipa/rpi/controller/rpi/geq.h +++ b/src/ipa/rpi/controller/rpi/geq.h @@ -26,7 +26,7 @@ class Geq : public Algorithm public: Geq(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; private: diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp index 06400ea79a95..c256f7485f77 100644 --- a/src/ipa/rpi/controller/rpi/hdr.cpp +++ b/src/ipa/rpi/controller/rpi/hdr.cpp @@ -23,7 +23,7 @@ LOG_DEFINE_CATEGORY(RPiHdr) #define NAME "rpi.hdr" -void HdrConfig::read(const libcamera::YamlObject ¶ms, const std::string &modeName) +void HdrConfig::read(const libcamera::ValueNode ¶ms, const std::string &modeName) { name = modeName; @@ -111,7 +111,7 @@ char const *Hdr::name() const return NAME; } -int Hdr::read(const libcamera::YamlObject ¶ms) +int Hdr::read(const libcamera::ValueNode ¶ms) { /* Make an "HDR off" mode by default so that tuning files don't have to. */ HdrConfig &offMode = config_["Off"]; diff --git a/src/ipa/rpi/controller/rpi/hdr.h b/src/ipa/rpi/controller/rpi/hdr.h index 5c2f3988d789..58ff5ba83d2e 100644 --- a/src/ipa/rpi/controller/rpi/hdr.h +++ b/src/ipa/rpi/controller/rpi/hdr.h @@ -52,7 +52,7 @@ struct HdrConfig { uint8_t diffPower; double motionThreshold; - void read(const libcamera::YamlObject ¶ms, const std::string &name); + void read(const libcamera::ValueNode ¶ms, const std::string &name); }; class Hdr : public HdrAlgorithm @@ -61,7 +61,7 @@ public: Hdr(Controller *controller); char const *name() const override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; void process(StatisticsPtr &stats, Metadata *imageMetadata) override; int setMode(std::string const &mode) override; diff --git a/src/ipa/rpi/controller/rpi/lux.cpp b/src/ipa/rpi/controller/rpi/lux.cpp index efc2c511dbdc..2c9ca30b7812 100644 --- a/src/ipa/rpi/controller/rpi/lux.cpp +++ b/src/ipa/rpi/controller/rpi/lux.cpp @@ -35,7 +35,7 @@ char const *Lux::name() const return NAME; } -int Lux::read(const libcamera::YamlObject ¶ms) +int Lux::read(const libcamera::ValueNode ¶ms) { auto value = params["reference_shutter_speed"].get(); if (!value) diff --git a/src/ipa/rpi/controller/rpi/lux.h b/src/ipa/rpi/controller/rpi/lux.h index db2227e41455..c9ffe38b69db 100644 --- a/src/ipa/rpi/controller/rpi/lux.h +++ b/src/ipa/rpi/controller/rpi/lux.h @@ -23,7 +23,7 @@ class Lux : public Algorithm public: Lux(Controller *controller); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; void prepare(Metadata *imageMetadata) override; void process(StatisticsPtr &stats, Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/noise.cpp b/src/ipa/rpi/controller/rpi/noise.cpp index 145175fb4940..d6e01c2cfe4e 100644 --- a/src/ipa/rpi/controller/rpi/noise.cpp +++ b/src/ipa/rpi/controller/rpi/noise.cpp @@ -41,7 +41,7 @@ void Noise::switchMode(CameraMode const &cameraMode, modeFactor_ = std::max(1.0, cameraMode.noiseFactor); } -int Noise::read(const libcamera::YamlObject ¶ms) +int Noise::read(const libcamera::ValueNode ¶ms) { auto value = params["reference_constant"].get(); if (!value) diff --git a/src/ipa/rpi/controller/rpi/noise.h b/src/ipa/rpi/controller/rpi/noise.h index 6deae1f0282e..c449fa52ffd2 100644 --- a/src/ipa/rpi/controller/rpi/noise.h +++ b/src/ipa/rpi/controller/rpi/noise.h @@ -19,7 +19,7 @@ public: Noise(Controller *controller); char const *name() const override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void prepare(Metadata *imageMetadata) override; private: diff --git a/src/ipa/rpi/controller/rpi/saturation.cpp b/src/ipa/rpi/controller/rpi/saturation.cpp index b83c5887c02e..5001f930a1ec 100644 --- a/src/ipa/rpi/controller/rpi/saturation.cpp +++ b/src/ipa/rpi/controller/rpi/saturation.cpp @@ -27,7 +27,7 @@ char const *Saturation::name() const return NAME; } -int Saturation::read(const libcamera::YamlObject ¶ms) +int Saturation::read(const libcamera::ValueNode ¶ms) { config_.shiftR = params["shift_r"].get(0); config_.shiftG = params["shift_g"].get(0); diff --git a/src/ipa/rpi/controller/rpi/saturation.h b/src/ipa/rpi/controller/rpi/saturation.h index c67d496ef065..e6a22c8bb94d 100644 --- a/src/ipa/rpi/controller/rpi/saturation.h +++ b/src/ipa/rpi/controller/rpi/saturation.h @@ -21,7 +21,7 @@ class Saturation : public Algorithm public: Saturation(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; void prepare(Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/sdn.cpp b/src/ipa/rpi/controller/rpi/sdn.cpp index 594ea70133ac..13dfad02068c 100644 --- a/src/ipa/rpi/controller/rpi/sdn.cpp +++ b/src/ipa/rpi/controller/rpi/sdn.cpp @@ -35,7 +35,7 @@ char const *Sdn::name() const return NAME; } -int Sdn::read(const libcamera::YamlObject ¶ms) +int Sdn::read(const libcamera::ValueNode ¶ms) { deviation_ = params["deviation"].get(3.2); strength_ = params["strength"].get(0.75); diff --git a/src/ipa/rpi/controller/rpi/sdn.h b/src/ipa/rpi/controller/rpi/sdn.h index cb226de88c3c..20d847f0cefa 100644 --- a/src/ipa/rpi/controller/rpi/sdn.h +++ b/src/ipa/rpi/controller/rpi/sdn.h @@ -18,7 +18,7 @@ class Sdn : public DenoiseAlgorithm public: Sdn(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; void prepare(Metadata *imageMetadata) override; void setMode(DenoiseMode mode) override; diff --git a/src/ipa/rpi/controller/rpi/sharpen.cpp b/src/ipa/rpi/controller/rpi/sharpen.cpp index 1d143ff53287..e8723916c965 100644 --- a/src/ipa/rpi/controller/rpi/sharpen.cpp +++ b/src/ipa/rpi/controller/rpi/sharpen.cpp @@ -37,7 +37,7 @@ void Sharpen::switchMode(CameraMode const &cameraMode, modeFactor_ = std::max(1.0, cameraMode.noiseFactor); } -int Sharpen::read(const libcamera::YamlObject ¶ms) +int Sharpen::read(const libcamera::ValueNode ¶ms) { threshold_ = params["threshold"].get(1.0); strength_ = params["strength"].get(1.0); diff --git a/src/ipa/rpi/controller/rpi/sharpen.h b/src/ipa/rpi/controller/rpi/sharpen.h index 96ccd60934f8..2814ec85fef1 100644 --- a/src/ipa/rpi/controller/rpi/sharpen.h +++ b/src/ipa/rpi/controller/rpi/sharpen.h @@ -19,7 +19,7 @@ public: Sharpen(Controller *controller); char const *name() const override; void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void setStrength(double strength) override; void prepare(Metadata *imageMetadata) override; diff --git a/src/ipa/rpi/controller/rpi/tonemap.cpp b/src/ipa/rpi/controller/rpi/tonemap.cpp index 3422adfe7dee..3cbecf5379ac 100644 --- a/src/ipa/rpi/controller/rpi/tonemap.cpp +++ b/src/ipa/rpi/controller/rpi/tonemap.cpp @@ -27,7 +27,7 @@ char const *Tonemap::name() const return NAME; } -int Tonemap::read(const libcamera::YamlObject ¶ms) +int Tonemap::read(const libcamera::ValueNode ¶ms) { config_.detailConstant = params["detail_constant"].get(0); config_.detailSlope = params["detail_slope"].get(0.1); diff --git a/src/ipa/rpi/controller/rpi/tonemap.h b/src/ipa/rpi/controller/rpi/tonemap.h index 4e513b1d00da..4d486d136499 100644 --- a/src/ipa/rpi/controller/rpi/tonemap.h +++ b/src/ipa/rpi/controller/rpi/tonemap.h @@ -25,7 +25,7 @@ class Tonemap : public Algorithm public: Tonemap(Controller *controller = NULL); char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; + int read(const libcamera::ValueNode ¶ms) override; void initialise() override; void prepare(Metadata *imageMetadata) override; diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp index 068e98404709..8bf39c4c8d87 100644 --- a/src/ipa/simple/algorithms/adjust.cpp +++ b/src/ipa/simple/algorithms/adjust.cpp @@ -24,7 +24,7 @@ constexpr float kDefaultSaturation = 1.0f; LOG_DEFINE_CATEGORY(IPASoftAdjust) -int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData) +int Adjust::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) { context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma); diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h index fb133b14098c..49c1f26c35c9 100644 --- a/src/ipa/simple/algorithms/adjust.h +++ b/src/ipa/simple/algorithms/adjust.h @@ -25,7 +25,7 @@ public: Adjust() = default; ~Adjust() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; void queueRequest(typename Module::Context &context, diff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp index 464e43c278f3..677be56ed669 100644 --- a/src/ipa/simple/algorithms/blc.cpp +++ b/src/ipa/simple/algorithms/blc.cpp @@ -24,7 +24,7 @@ BlackLevel::BlackLevel() } int BlackLevel::init([[maybe_unused]] IPAContext &context, - const YamlObject &tuningData) + const ValueNode &tuningData) { auto blackLevel = tuningData["blackLevel"].get(); if (blackLevel.has_value()) { diff --git a/src/ipa/simple/algorithms/blc.h b/src/ipa/simple/algorithms/blc.h index a5592d08740f..2933ff1fffe7 100644 --- a/src/ipa/simple/algorithms/blc.h +++ b/src/ipa/simple/algorithms/blc.h @@ -22,7 +22,7 @@ public: BlackLevel(); ~BlackLevel() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; void prepare(IPAContext &context, const uint32_t frame, diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp index 911a5af2c90b..ace9c35dc462 100644 --- a/src/ipa/simple/algorithms/ccm.cpp +++ b/src/ipa/simple/algorithms/ccm.cpp @@ -27,7 +27,7 @@ namespace ipa::soft::algorithms { LOG_DEFINE_CATEGORY(IPASoftCcm) -int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData) +int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) { int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm"); if (ret < 0) { diff --git a/src/ipa/simple/algorithms/ccm.h b/src/ipa/simple/algorithms/ccm.h index 62009a3750c7..b20a7da8aa33 100644 --- a/src/ipa/simple/algorithms/ccm.h +++ b/src/ipa/simple/algorithms/ccm.h @@ -25,7 +25,7 @@ public: Ccm() = default; ~Ccm() = default; - int init(IPAContext &context, const YamlObject &tuningData) override; + int init(IPAContext &context, const ValueNode &tuningData) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 7d25bdd26017..629e1a32de8a 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -118,7 +118,7 @@ int IPASoftSimple::init(const IPASettings &settings, return ret; } - std::unique_ptr data = YamlParser::parse(file); + std::unique_ptr data = YamlParser::parse(file); if (!data) return -EINVAL; diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp index 19aa44bdeb32..44f7ec035e62 100644 --- a/src/libcamera/converter/converter_dw100.cpp +++ b/src/libcamera/converter/converter_dw100.cpp @@ -97,7 +97,7 @@ ConverterDW100Module::createModule(DeviceEnumerator *enumerator) * \sa Dw100VertexMap::setDewarpParams() * \return 0 if successful, an error code otherwise */ -int ConverterDW100Module::init(const YamlObject ¶ms) +int ConverterDW100Module::init(const ValueNode ¶ms) { DewarpParms dp; diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index 621008844c74..010c1d8c35d9 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" /** * \file geometry.h @@ -933,7 +933,7 @@ std::ostream &operator<<(std::ostream &out, const Rectangle &r) */ template<> std::optional -YamlObject::Accessor::get(const YamlObject &obj) const +ValueNode::Accessor::get(const ValueNode &obj) const { if (obj.type_ != Type::List) return std::nullopt; diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index 99d16e7c38c6..c4999d32d7c7 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -65,7 +65,7 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) return true; } - std::unique_ptr configuration = YamlParser::parse(file); + std::unique_ptr configuration = YamlParser::parse(file); if (!configuration) { LOG(Configuration, Error) << "Failed to parse configuration file " << fileName; @@ -146,7 +146,7 @@ GlobalConfiguration::GlobalConfiguration() std::optional> GlobalConfiguration::listOption( const std::initializer_list confPath) const { - const YamlObject *c = &configuration(); + const ValueNode *c = &configuration(); for (auto part : confPath) { c = &(*c)[part]; if (!*c) @@ -237,10 +237,10 @@ unsigned int GlobalConfiguration::version() const * This returns the whole configuration stored in the top-level section * `%configuration` of the YAML configuration file. * - * The requested part of the configuration can be accessed using \a YamlObject + * The requested part of the configuration can be accessed using \a ValueNode * methods. * - * \note \a YamlObject type itself shouldn't be used in type declarations to + * \note \a ValueNode type itself shouldn't be used in type declarations to * avoid trouble if we decide to change the underlying data objects in future. * * \return The whole configuration section diff --git a/src/libcamera/matrix.cpp b/src/libcamera/matrix.cpp index b7c07e896538..4fe210830421 100644 --- a/src/libcamera/matrix.cpp +++ b/src/libcamera/matrix.cpp @@ -314,7 +314,7 @@ template bool matrixInvert(Span data, Span dataOut * to the product of the number of rows and columns of the matrix (Rows x * Cols). The values shall be stored in row-major order. */ -bool matrixValidateYaml(const YamlObject &obj, unsigned int size) +bool matrixValidateYaml(const ValueNode &obj, unsigned int size) { if (!obj.isList()) return false; diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 1aafc84b8802..6aedacbdb04d 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -57,8 +57,8 @@ libcamera_internal_sources = files([ 'v4l2_request.cpp', 'v4l2_subdevice.cpp', 'v4l2_videodevice.cpp', + 'value_node.cpp', 'vector.cpp', - 'yaml_object.cpp', 'yaml_parser.cpp', ]) diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index a74ac8287199..1b7712bc547b 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -443,7 +443,7 @@ int RkISP1CameraData::loadTuningFile(const std::string &path) return ret; } - std::unique_ptr data = YamlParser::parse(file); + std::unique_ptr data = YamlParser::parse(file); if (!data) return -EINVAL; diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 08a2b32dc30f..f6ef8a3beb99 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -1122,7 +1122,7 @@ int CameraData::loadPipelineConfiguration() LOG(RPI, Info) << "Using configuration file '" << filename << "'"; - std::unique_ptr root = YamlParser::parse(file); + std::unique_ptr root = YamlParser::parse(file); if (!root) { LOG(RPI, Warning) << "Failed to parse configuration file, using defaults"; return 0; @@ -1135,7 +1135,7 @@ int CameraData::loadPipelineConfiguration() return 0; } - const YamlObject &phConfig = (*root)["pipeline_handler"]; + const ValueNode &phConfig = (*root)["pipeline_handler"]; if (phConfig.contains("disable_startup_frame_drops")) LOG(RPI, Warning) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index 8b5d78d2d2dc..7bfac33eb0c7 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -26,7 +26,7 @@ #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" #include "libcamera/internal/v4l2_videodevice.h" -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include #include @@ -96,7 +96,7 @@ public: virtual V4L2VideoDevice::Formats rawFormats() const = 0; virtual V4L2VideoDevice *frontendDevice() = 0; - virtual int platformPipelineConfigure(const std::unique_ptr &root) = 0; + virtual int platformPipelineConfigure(const std::unique_ptr &root) = 0; std::unique_ptr ipa_; diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp index dff73a79bb3f..c7799f2b4bd9 100644 --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp @@ -743,7 +743,7 @@ public: CameraConfiguration::Status platformValidate(RPi::RPiCameraConfiguration *rpiConfig) const override; - int platformPipelineConfigure(const std::unique_ptr &root) override; + int platformPipelineConfigure(const std::unique_ptr &root) override; void platformStart() override; void platformStop() override; @@ -1333,7 +1333,7 @@ PiSPCameraData::platformValidate(RPi::RPiCameraConfiguration *rpiConfig) const return status; } -int PiSPCameraData::platformPipelineConfigure(const std::unique_ptr &root) +int PiSPCameraData::platformPipelineConfigure(const std::unique_ptr &root) { config_ = { .numCfeConfigStatsBuffers = 12, @@ -1358,7 +1358,7 @@ int PiSPCameraData::platformPipelineConfigure(const std::unique_ptr return -EINVAL; } - const YamlObject &phConfig = (*root)["pipeline_handler"]; + const ValueNode &phConfig = (*root)["pipeline_handler"]; config_.numCfeConfigStatsBuffers = phConfig["num_cfe_config_stats_buffers"].get(config_.numCfeConfigStatsBuffers); config_.numCfeConfigQueue = diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp index b734889dd016..f99cfdbcf60c 100644 --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp @@ -69,7 +69,7 @@ public: CameraConfiguration::Status platformValidate(RPi::RPiCameraConfiguration *rpiConfig) const override; - int platformPipelineConfigure(const std::unique_ptr &root) override; + int platformPipelineConfigure(const std::unique_ptr &root) override; void platformStart() override; void platformStop() override; @@ -498,7 +498,7 @@ CameraConfiguration::Status Vc4CameraData::platformValidate(RPi::RPiCameraConfig return status; } -int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr &root) +int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr &root) { config_ = { .minUnicamBuffers = 2, @@ -521,7 +521,7 @@ int Vc4CameraData::platformPipelineConfigure(const std::unique_ptr & return -EINVAL; } - const YamlObject &phConfig = (*root)["pipeline_handler"]; + const ValueNode &phConfig = (*root)["pipeline_handler"]; config_.minUnicamBuffers = phConfig["min_unicam_buffers"].get(config_.minUnicamBuffers); config_.minTotalUnicamBuffers = diff --git a/src/libcamera/pipeline/virtual/README.md b/src/libcamera/pipeline/virtual/README.md index a9f39c15172b..0a02a9ea44b3 100644 --- a/src/libcamera/pipeline/virtual/README.md +++ b/src/libcamera/pipeline/virtual/README.md @@ -48,7 +48,7 @@ in Virtual Pipeline Handler. `parseConfigFile()` is exposed to use in Virtual Pipeline Handler. This is the procedure of the Parser class: -1. `parseConfigFile()` parses the config file to `YamlObject` using `YamlParser::parse()`. +1. `parseConfigFile()` parses the config file to `ValueNode` using `YamlParser::parse()`. - Parse the top level of config file which are the camera ids and look into each camera properties. 2. For each camera, `parseCameraConfigData()` returns a camera with the configuration. diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp index fdc729509371..5169fd39bc38 100644 --- a/src/libcamera/pipeline/virtual/config_parser.cpp +++ b/src/libcamera/pipeline/virtual/config_parser.cpp @@ -29,7 +29,7 @@ ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe) { std::vector> configurations; - std::unique_ptr cameras = YamlParser::parse(file); + std::unique_ptr cameras = YamlParser::parse(file); if (!cameras) { LOG(Virtual, Error) << "Failed to parse config file."; return configurations; @@ -72,7 +72,7 @@ ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe) } std::unique_ptr -ConfigParser::parseCameraConfigData(const YamlObject &cameraConfigData, +ConfigParser::parseCameraConfigData(const ValueNode &cameraConfigData, PipelineHandler *pipe) { std::vector resolutions; @@ -94,13 +94,13 @@ ConfigParser::parseCameraConfigData(const YamlObject &cameraConfigData, return data; } -int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData, +int ConfigParser::parseSupportedFormats(const ValueNode &cameraConfigData, std::vector *resolutions) { if (cameraConfigData.contains("supported_formats")) { - const YamlObject &supportedResolutions = cameraConfigData["supported_formats"]; + const ValueNode &supportedResolutions = cameraConfigData["supported_formats"]; - for (const YamlObject &supportedResolution : supportedResolutions.asList()) { + for (const ValueNode &supportedResolution : supportedResolutions.asList()) { unsigned int width = supportedResolution["width"].get(1920); unsigned int height = supportedResolution["height"].get(1080); if (width == 0 || height == 0) { @@ -152,7 +152,7 @@ int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData, return 0; } -int ConfigParser::parseFrameGenerator(const YamlObject &cameraConfigData, VirtualCameraData *data) +int ConfigParser::parseFrameGenerator(const ValueNode &cameraConfigData, VirtualCameraData *data) { const std::string testPatternKey = "test_pattern"; const std::string framesKey = "frames"; @@ -178,7 +178,7 @@ int ConfigParser::parseFrameGenerator(const YamlObject &cameraConfigData, Virtua return 0; } - const YamlObject &frames = cameraConfigData[framesKey]; + const ValueNode &frames = cameraConfigData[framesKey]; /* When there is no frames provided in the config file, use color bar test pattern */ if (!frames) { @@ -231,7 +231,7 @@ int ConfigParser::parseFrameGenerator(const YamlObject &cameraConfigData, Virtua return 0; } -int ConfigParser::parseLocation(const YamlObject &cameraConfigData, VirtualCameraData *data) +int ConfigParser::parseLocation(const ValueNode &cameraConfigData, VirtualCameraData *data) { /* Default value is properties::CameraLocationFront */ int32_t location = properties::CameraLocationFront; @@ -252,7 +252,7 @@ int ConfigParser::parseLocation(const YamlObject &cameraConfigData, VirtualCamer return 0; } -int ConfigParser::parseModel(const YamlObject &cameraConfigData, VirtualCameraData *data) +int ConfigParser::parseModel(const ValueNode &cameraConfigData, VirtualCameraData *data) { std::string model = cameraConfigData["model"].get("Unknown"); diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h index f696d8862897..97d6dffa31ec 100644 --- a/src/libcamera/pipeline/virtual/config_parser.h +++ b/src/libcamera/pipeline/virtual/config_parser.h @@ -13,7 +13,7 @@ #include #include "libcamera/internal/pipeline_handler.h" -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "virtual.h" @@ -27,13 +27,13 @@ public: private: std::unique_ptr - parseCameraConfigData(const YamlObject &cameraConfigData, PipelineHandler *pipe); + parseCameraConfigData(const ValueNode &cameraConfigData, PipelineHandler *pipe); - int parseSupportedFormats(const YamlObject &cameraConfigData, + int parseSupportedFormats(const ValueNode &cameraConfigData, std::vector *resolutions); - int parseFrameGenerator(const YamlObject &cameraConfigData, VirtualCameraData *data); - int parseLocation(const YamlObject &cameraConfigData, VirtualCameraData *data); - int parseModel(const YamlObject &cameraConfigData, VirtualCameraData *data); + int parseFrameGenerator(const ValueNode &cameraConfigData, VirtualCameraData *data); + int parseLocation(const ValueNode &cameraConfigData, VirtualCameraData *data); + int parseModel(const ValueNode &cameraConfigData, VirtualCameraData *data); }; } /* namespace libcamera */ diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index d8fc64d22448..c179956e22ab 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -36,7 +36,7 @@ #include "libcamera/internal/framebuffer.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" -#include "libcamera/internal/yaml_object.h" +#include "libcamera/internal/value_node.h" #include "pipeline/virtual/config_parser.h" diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp new file mode 100644 index 000000000000..52ccf432fb96 --- /dev/null +++ b/src/libcamera/value_node.cpp @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * Copyright (C) 2025, Ideas on Board. + * + * Data structure to manage tree of values + */ + +#include "libcamera/internal/value_node.h" + +#include +#include +#include +#include + +#include + +/** + * \file value_node.h + * \brief Data structure to manage tree of values + */ + +namespace libcamera { + +namespace { + +/* Empty static ValueNode as a safe result for invalid operations */ +static const ValueNode empty; + +} /* namespace */ + +/** + * \class ValueNode + * \brief A class representing a tree structure of values + * + * The ValueNode class is designed to model a tree of values. Each node in the + * tree is represented by a ValueNode instance. Intermediate nodes store + * children either as an ordered list (sequence) or a string-indexed dictionary + * (mapping). Leaf nodes can be empty or store a string value. + */ + +ValueNode::ValueNode() + : type_(Type::Empty) +{ +} + +ValueNode::~ValueNode() = default; + +/** + * \fn ValueNode::isValue() + * \brief Return whether the ValueNode is a value + * + * \return True if the ValueNode is a value, false otherwise + */ + +/** + * \fn ValueNode::isList() + * \brief Return whether the ValueNode is a list + * + * \return True if the ValueNode is a list, false otherwise + */ + +/** + * \fn ValueNode::isDictionary() + * \brief Return whether the ValueNode is a dictionary + * + * \return True if the ValueNode is a dictionary, false otherwise + */ + +/** + * \fn ValueNode::isEmpty() + * \brief Return whether the ValueNode is an empty + * + * \return True if the ValueNode is empty, false otherwise + */ + +/** + * \fn ValueNode::operator bool() + * \brief Return whether the ValueNode is a non-empty + * + * \return False if the ValueNode is empty, true otherwise + */ + +/** + * \fn ValueNode::size() + * \brief Retrieve the number of elements in a dictionary or list ValueNode + * + * This function retrieves the size of the ValueNode, defined as the number of + * child elements it contains. Only ValueNode instances of Dictionary or List + * types have a size, calling this function on other types of instances is + * invalid and results in undefined behaviour. + * + * \return The size of the ValueNode + */ +std::size_t ValueNode::size() const +{ + switch (type_) { + case Type::Dictionary: + case Type::List: + return list_.size(); + default: + return 0; + } +} + +/** + * \fn template ValueNode::get() const + * \brief Parse the ValueNode as a \a T value + * \tparam T Type of the value + * + * This function parses the value of the ValueNode as a \a T object, and + * returns the value. If parsing fails (usually because the ValueNode doesn't + * store a \a T value), std::nullopt is returned. + * + * If the type \a T is an std::vector, the ValueNode will be parsed as a list + * of values. + * + * \return The ValueNode value, or std::nullopt if parsing failed + */ + +/** + * \fn template ValueNode::get(U &&defaultValue) const + * \brief Parse the ValueNode as a \a T value + * \tparam T Type of the value + * \tparam U Type of the default value + * \param[in] defaultValue The default value when failing to parse + * + * This function parses the value of the ValueNode as a \a T object, and + * returns the value. If parsing fails (usually because the ValueNode doesn't + * store a \a T value), the \a defaultValue is returned. Type \a U must be + * convertible to type \a T. + * + * Unlike the get() function, this overload does not support std::vector for the + * type \a T. + * + * \return The ValueNode value, or \a defaultValue if parsing failed + */ + +/** + * \fn template ValueNode::set(T &&value) + * \brief Set the value of a ValueNode + * \tparam T Type of the value + * \param[in] value The value + * + * This function sets the value stored in a ValueNode to \a value. The value is + * converted to a string in an implementation-specific way that guarantees that + * subsequent calls to get() will return the same value. + * + * Values can only be set on ValueNode of Type::Value type or empty ValueNode. + * Attempting to set a value on a node of type Type::Dict or Type::List does not + * modify the ValueNode. + */ + +#ifndef __DOXYGEN__ + +template<> +std::optional +ValueNode::Accessor::get(const ValueNode &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_ == "true") + return true; + else if (obj.value_ == "false") + return false; + + return std::nullopt; +} + +template<> +void ValueNode::Accessor::set(ValueNode &obj, bool value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = value ? "true" : "false"; +} + +template +struct ValueNode::Accessor || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v>> +{ + std::optional get(const ValueNode &obj) const + { + if (obj.type_ != Type::Value) + return std::nullopt; + + const std::string &str = obj.value_; + T value = {}; + + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), + value); + if (ptr != str.data() + str.size() || ec != std::errc()) + return std::nullopt; + + return value; + } + + void set(ValueNode &obj, T value) + { + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); + } +}; + +template struct ValueNode::Accessor; +template struct ValueNode::Accessor; +template struct ValueNode::Accessor; +template struct ValueNode::Accessor; +template struct ValueNode::Accessor; +template struct ValueNode::Accessor; + +template<> +std::optional +ValueNode::Accessor::get(const ValueNode &obj) const +{ + return obj.get(); +} + +template<> +void ValueNode::Accessor::set(ValueNode &obj, float value) +{ + obj.set(std::forward(value)); +} + +template<> +std::optional +ValueNode::Accessor::get(const ValueNode &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_.empty()) + return std::nullopt; + + char *end; + + errno = 0; + double value = utils::strtod(obj.value_.c_str(), &end); + + if ('\0' != *end || errno == ERANGE) + return std::nullopt; + + return value; +} + +template<> +void ValueNode::Accessor::set(ValueNode &obj, double value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); +} + +template<> +std::optional +ValueNode::Accessor::get(const ValueNode &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + return obj.value_; +} + +template<> +void ValueNode::Accessor::set(ValueNode &obj, std::string value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::move(value); +} + +template +struct ValueNode::Accessor, std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v>> +{ + std::optional> get(const ValueNode &obj) const + { + if (obj.type_ != Type::List) + return std::nullopt; + + std::vector values; + values.reserve(obj.list_.size()); + + for (const ValueNode &entry : obj.asList()) { + const auto value = entry.get(); + if (!value) + return std::nullopt; + values.emplace_back(*value); + } + + return values; + } +}; + +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +template struct ValueNode::Accessor>; +#endif /* __DOXYGEN__ */ + +/** + * \fn ValueNode::asDict() const + * \brief Wrap a dictionary ValueNode in an adapter that exposes iterators + * + * The ValueNode class doesn't directly implement iterators, as the iterator + * type depends on whether the node is a Dictionary or List. This function wraps + * a ValueNode of Dictionary type into an adapter that exposes iterators, as + * well as begin() and end() functions, allowing usage of range-based for loops + * with ValueNode. As mappings are not ordered, the iteration order is not + * specified. + * + * The iterator's value_type is a + * std::pair. + * + * If the ValueNode is not of Dictionary type, the returned adapter operates + * as an empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \fn ValueNode::asList() const + * \brief Wrap a list ValueNode in an adapter that exposes iterators + * + * The ValueNode class doesn't directly implement iterators, as the iterator + * type depends on whether the node is a Dictionary or List. This function wraps + * a ValueNode of List type into an adapter that exposes iterators, as well as + * begin() and end() functions, allowing usage of range-based for loops with + * ValueNode. As lists are ordered, the iteration order is matches the order in + * which child nodes have been added. + * + * The iterator's value_type is a const ValueNode &. + * + * If the ValueNode is not of List type, the returned adapter operates as an + * empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \brief Retrieve the element from list ValueNode by index + * \param[in] index The element index + * + * This function retrieves an element of the ValueNode. Only ValueNode + * instances of List type associate elements with index, calling this function + * on other types of instances or with an invalid index results in an empty + * node. + * + * \return The ValueNode as an element of the list + */ +const ValueNode &ValueNode::operator[](std::size_t index) const +{ + if (type_ != Type::List || index >= size()) + return empty; + + return *list_[index].value; +} + +/** + * \brief Check if an element of a dictionary exists + * \param[in] key The element key + * + * This function checks if the ValueNode contains an element for the given + * \a key. Only ValueNode instances of Dictionary type associate elements with + * keys, calling this function on other types of instances is invalid and + * results in undefined behaviour. + * + * \return True if an element exists, false otherwise + */ +bool ValueNode::contains(std::string_view key) const +{ + return dictionary_.find(key) != dictionary_.end(); +} + +/** + * \brief Retrieve a member by key from the dictionary + * \param[in] key The element key + * + * This function retrieves a member of a ValueNode by \a key. Only ValueNode + * instances of Dictionary type associate elements with keys, calling this + * function on other types of instances or with a nonexistent key results in an + * empty node. + * + * \return The ValueNode corresponding to the \a key member + */ +const ValueNode &ValueNode::operator[](std::string_view key) const +{ + if (type_ != Type::Dictionary) + return empty; + + auto iter = dictionary_.find(key); + if (iter == dictionary_.end()) + return empty; + + return *iter->second; +} + +/** + * \brief Add a child node to a list + * \param[in] child The child node + * + * Append the \a child node as the last element of this node's children list. + * This node must be empty, in which case it is converted to the Type::List + * type, or be a list. Otherwise, the function returns a nullptr and the + * \a child is not modified. + * + * \return A pointer to the \a child node if successfully added, nullptr + * otherwise + */ +ValueNode *ValueNode::add(std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::List; + + if (type_ != Type::List) + return nullptr; + + Value &elem = list_.emplace_back(std::string{}, std::move(child)); + return elem.value.get(); +} + +/** + * \brief Add a child node to a dictionary + * \param[in] key The dictionary key + * \param[in] child The child node + * + * Add the \a child node with the given \a key to this node's children. This + * node must be empty, in which case it is converted to the Type::Dictionary + * type, or be a dictionary. Otherwise, the function returns a nullptr and the + * \a child is not modified. + * + * Keys are unique. If a child with the same \a key already exist, the function + * returns a nullptr and the \a child is not modified. + * + * \return A pointer to the \a child node if successfully added, nullptr + * otherwise + */ +ValueNode *ValueNode::add(std::string key, std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::Dictionary; + + if (type_ != Type::Dictionary) + return nullptr; + + auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); + if (!inserted) + return nullptr; + + return list_.emplace_back(it->first, std::move(child)).value.get(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/vector.cpp b/src/libcamera/vector.cpp index 4dad1b9001c5..86b9f9bb6d5e 100644 --- a/src/libcamera/vector.cpp +++ b/src/libcamera/vector.cpp @@ -337,7 +337,7 @@ LOG_DEFINE_CATEGORY(Vector) */ #ifndef __DOXYGEN__ -bool vectorValidateYaml(const YamlObject &obj, unsigned int size) +bool vectorValidateYaml(const ValueNode &obj, unsigned int size) { if (!obj.isList()) return false; diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp deleted file mode 100644 index 5bb78b66c7e7..000000000000 --- a/src/libcamera/yaml_object.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2022, Google Inc. - * Copyright (C) 2025, Ideas on Board. - * - * libcamera YAML object - */ - -#include "libcamera/internal/yaml_object.h" - -#include -#include -#include -#include - -#include - -/** - * \file yaml_object.h - * \brief YAML objects - */ - -namespace libcamera { - -namespace { - -/* Empty static YamlObject as a safe result for invalid operations */ -static const YamlObject empty; - -} /* namespace */ - -/** - * \class YamlObject - * \brief A class representing the tree structure of the YAML content - * - * The YamlObject class represents the tree structure of YAML content. A - * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a - * tree leaf. - */ - -YamlObject::YamlObject() - : type_(Type::Empty) -{ -} - -YamlObject::~YamlObject() = default; - -/** - * \fn YamlObject::isValue() - * \brief Return whether the YamlObject is a value - * - * \return True if the YamlObject is a value, false otherwise - */ - -/** - * \fn YamlObject::isList() - * \brief Return whether the YamlObject is a list - * - * \return True if the YamlObject is a list, false otherwise - */ - -/** - * \fn YamlObject::isDictionary() - * \brief Return whether the YamlObject is a dictionary - * - * \return True if the YamlObject is a dictionary, false otherwise - */ - -/** - * \fn YamlObject::isEmpty() - * \brief Return whether the YamlObject is an empty - * - * \return True if the YamlObject is empty, false otherwise - */ - -/** - * \fn YamlObject::operator bool() - * \brief Return whether the YamlObject is a non-empty - * - * \return False if the YamlObject is empty, true otherwise - */ - -/** - * \brief Retrieve the number of elements in a dictionary or list YamlObject - * - * This function retrieves the size of the YamlObject, defined as the number of - * child elements it contains. Only YamlObject instances of Dictionary or List - * types have a size, calling this function on other types of instances is - * invalid and results in undefined behaviour. - * - * \return The size of the YamlObject - */ -std::size_t YamlObject::size() const -{ - switch (type_) { - case Type::Dictionary: - case Type::List: - return list_.size(); - default: - return 0; - } -} - -/** - * \fn template YamlObject::get() const - * \brief Parse the YamlObject as a \a T value - * \tparam T Type of the value - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), std::nullopt is returned. - * - * If the type \a T is an std::vector, the YamlObject will be parsed as a list - * of values. - * - * \return The YamlObject value, or std::nullopt if parsing failed - */ - -/** - * \fn template YamlObject::get(U &&defaultValue) const - * \brief Parse the YamlObject as a \a T value - * \tparam T Type of the value - * \tparam U Type of the default value - * \param[in] defaultValue The default value when failing to parse - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), the \a defaultValue is returned. Type \a U must be - * convertible to type \a T. - * - * Unlike the get() function, this overload does not support std::vector for the - * type \a T. - * - * \return The YamlObject value, or \a defaultValue if parsing failed - */ - -/** - * \fn template YamlObject::set(T &&value) - * \brief Set the value of a YamlObject - * \tparam T Type of the value - * \param[in] value The value - * - * This function sets the value stored in a YamlObject to \a value. The value is - * converted to a string in an implementation-specific way that guarantees that - * subsequent calls to get() will return the same value. - * - * Values can only be set on YamlObject of Type::Value type or empty YamlObject. - * Attempting to set a value on an object of type Type::Dict or Type::List does - * not modify the YamlObject. - */ - -#ifndef __DOXYGEN__ - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_ == "true") - return true; - else if (obj.value_ == "false") - return false; - - return std::nullopt; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, bool value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = value ? "true" : "false"; -} - -template -struct YamlObject::Accessor || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v>> -{ - std::optional get(const YamlObject &obj) const - { - if (obj.type_ != Type::Value) - return std::nullopt; - - const std::string &str = obj.value_; - T value = {}; - - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), - value); - if (ptr != str.data() + str.size() || ec != std::errc()) - return std::nullopt; - - return value; - } - - void set(YamlObject &obj, T value) - { - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); - } -}; - -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - return obj.get(); -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, float value) -{ - obj.set(std::forward(value)); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_.empty()) - return std::nullopt; - - char *end; - - errno = 0; - double value = utils::strtod(obj.value_.c_str(), &end); - - if ('\0' != *end || errno == ERANGE) - return std::nullopt; - - return value; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, double value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - return obj.value_; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, std::string value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::move(value); -} - -template -struct YamlObject::Accessor> { - std::optional> get(const YamlObject &obj) const - { - if (obj.type_ != Type::List) - return std::nullopt; - - std::vector values; - values.reserve(obj.list_.size()); - - for (const YamlObject &entry : obj.asList()) { - auto value = entry.get(); - if (!value) - return std::nullopt; - values.emplace_back(std::move(*value)); - } - - return values; - } -}; - -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -#endif /* __DOXYGEN__ */ - -/** - * \fn YamlObject::asDict() const - * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of Dictionary type into an adapter that exposes - * iterators, as well as begin() and end() functions, allowing usage of - * range-based for loops with YamlObject. As YAML mappings are not ordered, the - * iteration order is not specified. - * - * The iterator's value_type is a - * std::pair. - * - * If the YamlObject is not of Dictionary type, the returned adapter operates - * as an empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \fn YamlObject::asList() const - * \brief Wrap a list YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of List type into an adapter that exposes iterators, as - * well as begin() and end() functions, allowing usage of range-based for loops - * with YamlObject. As YAML lists are ordered, the iteration order is identical - * to the list order in the YAML data. - * - * The iterator's value_type is a const YamlObject &. - * - * If the YamlObject is not of List type, the returned adapter operates as an - * empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \brief Retrieve the element from list YamlObject by index - * \param[in] index The element index - * - * This function retrieves an element of the YamlObject. Only YamlObject - * instances of List type associate elements with index, calling this function - * on other types of instances or with an invalid index results in an empty - * object. - * - * \return The YamlObject as an element of the list - */ -const YamlObject &YamlObject::operator[](std::size_t index) const -{ - if (type_ != Type::List || index >= size()) - return empty; - - return *list_[index].value; -} - -/** - * \brief Check if an element of a dictionary exists - * \param[in] key The element key - * - * This function checks if the YamlObject contains an element for the given - * \a key. Only YamlObject instances of Dictionary type associate elements with - * keys, calling this function on other types of instances is invalid and - * results in undefined behaviour. - * - * \return True if an element exists, false otherwise - */ -bool YamlObject::contains(std::string_view key) const -{ - return dictionary_.find(key) != dictionary_.end(); -} - -/** - * \brief Retrieve a member by key from the dictionary - * \param[in] key The element key - * - * This function retrieves a member of a YamlObject by \a key. Only YamlObject - * instances of Dictionary type associate elements with keys, calling this - * function on other types of instances or with a nonexistent key results in an - * empty object. - * - * \return The YamlObject corresponding to the \a key member - */ -const YamlObject &YamlObject::operator[](std::string_view key) const -{ - if (type_ != Type::Dictionary) - return empty; - - auto iter = dictionary_.find(key); - if (iter == dictionary_.end()) - return empty; - - return *iter->second; -} - -/** - * \brief Add a child object to a list - * \param[in] child The child object - * - * Append the \a child object as the last element of this object's children - * list. This object must be empty, in which case it is converted to the - * Type::List type, or be a list. Otherwise, the function returns a nullptr and - * the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::List; - - if (type_ != Type::List) - return nullptr; - - Value &elem = list_.emplace_back(std::string{}, std::move(child)); - return elem.value.get(); -} - -/** - * \brief Add a child object to a dictionary - * \param[in] key The dictionary key - * \param[in] child The child object - * - * Add the \a child object with the given \a key to this object's children. This - * object must be empty, in which case it is converted to the Type::Dictionary - * type, or be a dictionary. Otherwise, the function returns a nullptr and the - * \a child is not modified. - * - * Keys are unique. If a child with the same \a key already exist, the function - * returns a nullptr and the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::Dictionary; - - if (type_ != Type::Dictionary) - return nullptr; - - auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); - if (!inserted) - return nullptr; - - return list_.emplace_back(it->first, std::move(child)).value.get(); -} - -} /* namespace libcamera */ diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 91c8f3e5b84f..64b743767bc6 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -35,7 +35,7 @@ public: ~YamlParserContext(); int init(File &file); - int parseContent(YamlObject &yamlObject); + int parseContent(ValueNode &valueNode); private: struct EventDeleter { @@ -55,7 +55,7 @@ private: std::string readValue(const EventPtr &event); int parseDictionaryOrList(yaml_event_type_t endEventType, const std::function &parseItem); - int parseNextYamlObject(YamlObject &yamlObject, EventPtr event); + int parseNextNode(ValueNode &valueNode, EventPtr event); bool parserValid_; yaml_parser_t parser_; @@ -153,15 +153,15 @@ YamlParserContext::EventPtr YamlParserContext::nextEvent() /** * \brief Parse the content of a YAML document - * \param[in] yamlObject The result of YamlObject + * \param[in] valueNode The result of ValueNode * * Check YAML start and end events of a YAML document, and parse the root object - * of the YAML document into a YamlObject. + * of the YAML document into a ValueNode. * * \return 0 on success or a negative error code otherwise * \retval -EINVAL The parser has failed to validate end of a YAML file */ -int YamlParserContext::parseContent(YamlObject &yamlObject) +int YamlParserContext::parseContent(ValueNode &valueNode) { /* Check start of the YAML file. */ EventPtr event = nextEvent(); @@ -174,7 +174,7 @@ int YamlParserContext::parseContent(YamlObject &yamlObject) /* Parse the root object. */ event = nextEvent(); - if (parseNextYamlObject(yamlObject, std::move(event))) + if (parseNextNode(valueNode, std::move(event))) return -EINVAL; /* Check end of the YAML file. */ @@ -247,8 +247,8 @@ int YamlParserContext::parseDictionaryOrList(yaml_event_type_t endEventType, } /** - * \brief Parse next YAML event and read it as a YamlObject - * \param[in] yamlObject The result of YamlObject + * \brief Parse next YAML event and read it as a ValueNode + * \param[in] valueNode The result of ValueNode * \param[in] event The leading event of the object * * Parse next YAML object separately as a value, list or dictionary. @@ -256,26 +256,26 @@ int YamlParserContext::parseDictionaryOrList(yaml_event_type_t endEventType, * \return 0 on success or a negative error code otherwise * \retval -EINVAL Fail to parse the YAML file. */ -int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr event) +int YamlParserContext::parseNextNode(ValueNode &valueNode, EventPtr event) { if (!event) return -EINVAL; switch (event->type) { case YAML_SCALAR_EVENT: - yamlObject.set(readValue(event)); + valueNode.set(readValue(event)); return 0; case YAML_SEQUENCE_START_EVENT: { - auto handler = [this, &yamlObject](EventPtr evt) { - YamlObject *child = yamlObject.add(std::make_unique()); - return parseNextYamlObject(*child, std::move(evt)); + auto handler = [this, &valueNode](EventPtr evt) { + ValueNode *child = valueNode.add(std::make_unique()); + return parseNextNode(*child, std::move(evt)); }; return parseDictionaryOrList(YAML_SEQUENCE_END_EVENT, handler); } case YAML_MAPPING_START_EVENT: { - auto handler = [this, &yamlObject](EventPtr evtKey) { + auto handler = [this, &valueNode](EventPtr evtKey) { /* Parse key */ if (evtKey->type != YAML_SCALAR_EVENT) { LOG(YamlParser, Error) << "Expect key at line: " @@ -292,8 +292,8 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even if (!evtValue) return -EINVAL; - YamlObject *child = yamlObject.add(std::move(key), - std::make_unique()); + ValueNode *child = valueNode.add(std::move(key), + std::make_unique()); if (!child) { LOG(YamlParser, Error) << "Duplicated key at line " @@ -301,7 +301,7 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even return -EINVAL; } - return parseNextYamlObject(*child, std::move(evtValue)); + return parseNextNode(*child, std::move(evtValue)); }; int ret = parseDictionaryOrList(YAML_MAPPING_END_EVENT, handler); if (ret) @@ -323,7 +323,7 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even * \brief A helper class for parsing a YAML file * * The YamlParser class provides an easy interface to parse the contents of a - * YAML file into a tree of YamlObject instances. + * YAML file into a tree of ValueNode instances. * * Example usage: * @@ -341,17 +341,17 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even * * \code{.cpp} * - * std::unique_ptr root = YamlParser::parse(fh); + * std::unique_ptr root = YamlParser::parse(fh); * if (!root) * return; * * if (!root->isDictionary()) * return; * - * const YamlObject &name = (*root)["name"]; + * const ValueNode &name = (*root)["name"]; * std::cout << name.get("") << std::endl; * - * const YamlObject &numbers = (*root)["numbers"]; + * const ValueNode &numbers = (*root)["numbers"]; * if (!numbers.isList()) * return; * @@ -361,7 +361,7 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even * \endcode * * The YamlParser::parse() function takes an open FILE, parses its contents, and - * returns a pointer to a YamlObject corresponding to the root node of the YAML + * returns a pointer to a ValueNode corresponding to the root node of the YAML * document. * * The parser preserves the order of items in the YAML file, for both lists and @@ -369,23 +369,23 @@ int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr even */ /** - * \brief Parse a YAML file as a YamlObject + * \brief Parse a YAML file as a ValueNode * \param[in] file The YAML file to parse * * The YamlParser::parse() function takes a file, parses its contents, and - * returns a pointer to a YamlObject corresponding to the root node of the YAML + * returns a pointer to a ValueNode corresponding to the root node of the YAML * document. * - * \return Pointer to result YamlObject on success or nullptr otherwise + * \return Pointer to result ValueNode on success or nullptr otherwise */ -std::unique_ptr YamlParser::parse(File &file) +std::unique_ptr YamlParser::parse(File &file) { YamlParserContext context; if (context.init(file)) return nullptr; - std::unique_ptr root = std::make_unique(); + std::unique_ptr root = std::make_unique(); if (context.parseContent(*root)) { LOG(YamlParser, Error) diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index 01e734d23059..8c5826f4885b 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -94,7 +94,7 @@ protected: Dictionary, }; - int testObjectType(const YamlObject &obj, const char *name, Type type) + int testObjectType(const ValueNode &obj, const char *name, Type type) { bool isList = type == Type::List || type == Type::Size; bool isScalar = !isList && type != Type::Dictionary; @@ -194,7 +194,7 @@ protected: return TestPass; } - int testIntegerObject(const YamlObject &obj, const char *name, Type type, + int testIntegerObject(const ValueNode &obj, const char *name, Type type, int64_t value) { uint64_t unsignedValue = static_cast(value); @@ -292,7 +292,7 @@ protected: return TestFail; } - std::unique_ptr root = YamlParser::parse(file); + std::unique_ptr root = YamlParser::parse(file); if (root) { cerr << "Invalid YAML file parse successfully" << std::endl; return TestFail; From patchwork Tue Apr 7 15:34:05 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26460 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 4D8D6C3304 for ; Tue, 7 Apr 2026 15:35:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9B50162DF7; Tue, 7 Apr 2026 17:35:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Qjy3nDnW"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6198B62DEE for ; Tue, 7 Apr 2026 17:34:56 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id C37536DC for ; Tue, 7 Apr 2026 17:33:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576009; bh=srDjWtA9sSjt/lKH4vSR14aLiLHWhPBzw6YG5ZzPnMs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Qjy3nDnWL98QH4VStsZCXK9gz3IgdEGTLnDXymLL6Np3hW94lKjMvQOKIYd9o1zXV zwg2RI+G4sBa3i5WuzMauk4ABcT8q4LBFBExOGdbEgnkrpv4H+VLGTvN4bBwqIbCLn u4DIpocO8O9wG2Ewp02RHAQJfPzKxmRPpJFa14Mg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 20/42] libcamera: value_node: Add constructor with value Date: Tue, 7 Apr 2026 18:34:05 +0300 Message-ID: <20260407153427.1825999-21-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 new constructor takes a value, allowing creation of a leaf ValueNode with a value in a single operation instead of having to call set() on an empty node. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Initialize type_ to Empty --- include/libcamera/internal/value_node.h | 8 ++++++++ src/libcamera/value_node.cpp | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index e42aa9ba0ce0..a7f916e22bc8 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -143,6 +143,14 @@ public: #endif /* __DOXYGEN__ */ ValueNode(); + + template + ValueNode(T &&value) + : type_(Type::Empty) + { + set(std::forward(value)); + } + ~ValueNode(); bool isValue() const diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index 52ccf432fb96..6c9e2239165b 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -44,6 +44,13 @@ ValueNode::ValueNode() { } +/** + * \fn template ValueNode::ValueNode(T &&value) + * \brief Construct a ValueNode instance with a value + * \tparam T Type of the value + * \param[in] value The value + */ + ValueNode::~ValueNode() = default; /** From patchwork Tue Apr 7 15:34:06 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26461 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 40C8EC3305 for ; Tue, 7 Apr 2026 15:35:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4866262E09; Tue, 7 Apr 2026 17:35:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BY/0tMOm"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A7D2462DF5 for ; Tue, 7 Apr 2026 17:34:57 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 2907278E for ; Tue, 7 Apr 2026 17:33:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576010; bh=EfQ3ejPh+as/ini3dHIUSM0q8G1dLsiYvXruH2QZn3U=; h=From:To:Subject:Date:In-Reply-To:References:From; b=BY/0tMOmMdi0sBfQr8jMvmywFybUOscxJSIBJlJ2D75C7LZIpvggG3OLU2luBjKbf 2bp9SvDwfvjvG3L0+vYTQNKGkqijf3fawLoX2hIHvjD5VkM0c1RecwPD/OLqNEb+Ip 9HPSIcgGHLUP5JxzCWzhssJXraPi7vB6os/Yr0j8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 21/42] libcamera: value_node: Rework templates to prepare for mutable views Date: Tue, 7 Apr 2026 18:34:06 +0300 Message-ID: <20260407153427.1825999-22-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" ValueNode provides adapter classes to expose the object as an iteratable list or dictionary. The Iterator and Adapter classes hardcode the assumption that the ValueNode is const. To prepare for mutable views, move the const specifier to the top-level DictAdapter and ListAdapter class templates. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/value_node.h | 54 ++++++++++++++++--------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index a7f916e22bc8..f8d0329f7670 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -38,14 +38,14 @@ private: public: #ifndef __DOXYGEN__ - template + template class Iterator { public: using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; - Iterator(typename ValueContainer::const_iterator it) + Iterator(ContainerIterator it) : it_(it) { } @@ -74,14 +74,14 @@ public: } protected: - ValueContainer::const_iterator it_; + ContainerIterator it_; }; - template + template class Adapter { public: - Adapter(const ValueContainer &container) + Adapter(Container &container) : container_(container) { } @@ -97,47 +97,63 @@ public: } protected: - const ValueContainer &container_; + Container &container_; }; - class ListIterator : public Iterator + template + class ListIterator : public Iterator, + ContainerIterator> { - public: - using value_type = const ValueNode &; - using pointer = const ValueNode *; - using reference = value_type; + private: + using Base = Iterator, + ContainerIterator>; - value_type operator*() const + public: + using value_type = Value; + using pointer = value_type *; + using reference = value_type &; + + reference operator*() const { - return *it_->value.get(); + return *Base::it_->value.get(); } pointer operator->() const { - return it_->value.get(); + return Base::it_->value.get(); } }; - class DictIterator : public Iterator + template + class DictIterator : public Iterator, + ContainerIterator> { + private: + using Base = Iterator, + ContainerIterator>; + public: - using value_type = std::pair; + using value_type = std::pair; using pointer = value_type *; using reference = value_type &; value_type operator*() const { - return { it_->key, *it_->value.get() }; + return { Base::it_->key, *Base::it_->value.get() }; } }; - class DictAdapter : public Adapter + class DictAdapter : public Adapter, + const ValueContainer> { public: using key_type = std::string; }; - class ListAdapter : public Adapter + class ListAdapter : public Adapter, + const ValueContainer> { }; #endif /* __DOXYGEN__ */ From patchwork Tue Apr 7 15:34:07 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26462 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 4E2CDC3306 for ; Tue, 7 Apr 2026 15:35:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 891AD62E0B; Tue, 7 Apr 2026 17:35:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PMKYYp1X"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 014CC62DD4 for ; Tue, 7 Apr 2026 17:34:59 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6A1B678E for ; Tue, 7 Apr 2026 17:33:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576011; bh=AFlAO62YzdTVI1vHXr1V9D7kB1G0XmGp6wg/1WxMcmQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=PMKYYp1Xo5jnAlZU3NFIkOrzKmy8tgYPXpoRDyG57vbgR6x6H5dBMLVtIU0OfrkXj 0T308ctZwPOCuHvnvj01YGGJXp8U0on1sYa9TfFolCwVDHeG3Atna2lSRC5r8j3lCT EA1f8oAvN/2QQpPBFuT5Kn268iuNszlDmXBGLKj0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 22/42] libcamera: value_node: Add mutable adapters Date: Tue, 7 Apr 2026 18:34:07 +0300 Message-ID: <20260407153427.1825999-23-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 ValueNode class was initially designed to store read-only property trees. It is useful to also provide mutable access. Add non-const adapters and iterators and adapters. This allows obtaining a mutable ValueNode instance when traversing a tree. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/value_node.h | 32 ++++++++++++++++++------- src/libcamera/value_node.cpp | 10 ++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index f8d0329f7670..3bdb537e9eab 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -143,17 +143,31 @@ public: } }; - class DictAdapter : public Adapter, - const ValueContainer> + class DictAdapter : public Adapter, + ValueContainer> { public: using key_type = std::string; }; - class ListAdapter : public Adapter, - const ValueContainer> + class ListAdapter : public Adapter, + ValueContainer> + { + }; + + class ConstDictAdapter : public Adapter, + const ValueContainer> + { + public: + using key_type = std::string; + }; + + class ConstListAdapter : public Adapter, + const ValueContainer> { }; #endif /* __DOXYGEN__ */ @@ -211,8 +225,10 @@ public: .set(*this, std::forward(value)); } - DictAdapter asDict() const { return DictAdapter{ list_ }; } - ListAdapter asList() const { return ListAdapter{ list_ }; } + DictAdapter asDict() { return DictAdapter{ list_ }; } + ListAdapter asList() { return ListAdapter{ list_ }; } + ConstDictAdapter asDict() const { return ConstDictAdapter{ list_ }; } + ConstListAdapter asList() const { return ConstListAdapter{ list_ }; } const ValueNode &operator[](std::size_t index) const; diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index 6c9e2239165b..5aef72cd29a4 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -335,6 +335,11 @@ template struct ValueNode::Accessor>; template struct ValueNode::Accessor>; #endif /* __DOXYGEN__ */ +/** + * \fn ValueNode::asDict() + * \copydoc ValueNode::asDict() const + */ + /** * \fn ValueNode::asDict() const * \brief Wrap a dictionary ValueNode in an adapter that exposes iterators @@ -355,6 +360,11 @@ template struct ValueNode::Accessor>; * \return An adapter of unspecified type compatible with range-based for loops */ +/** + * \fn ValueNode::asList() + * \copydoc ValueNode::asList() const + */ + /** * \fn ValueNode::asList() const * \brief Wrap a list ValueNode in an adapter that exposes iterators From patchwork Tue Apr 7 15:34:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26463 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 4804EC3307 for ; Tue, 7 Apr 2026 15:35:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 98EFE62E0E; Tue, 7 Apr 2026 17:35:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="M6DXkGC4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4686F62DD4 for ; Tue, 7 Apr 2026 17:35:00 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id AAC2278E for ; Tue, 7 Apr 2026 17:33:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576012; bh=JqgS7hrHDzNYfjW9RzfFDyQAkTr40KmcOSJvIKKsprQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=M6DXkGC42+zdKVZ2Aqo8rcoh03dfM1iaZ/P7rQBajiDLxnJ/X9CsdOZgdKgq7hekQ 1sar4kxi4tUDQJyew0IMQFOIfeBtmIi9HBJU0DhSGcHyiiejhgSg6GSAYtJ4a/BBIQ r0EjiEB9NQua/8foMXNIcXAz6fIefjoR5fN0ANbQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 23/42] libcamera: value_node: Add mutable children accessors Date: Tue, 7 Apr 2026 18:34:08 +0300 Message-ID: <20260407153427.1825999-24-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 at() functions that return mutable pointer to child nodes, based on an index for lists and a key for dictionaries. The API differs from const children accessors that use operator[] and return a reference in order to allow better error handling: while the const accessors return a reference to a global instance of an empty ValueNode when the requested child doesn't exist, a mutable accessor can't do the same without allowing modifying the empty global instance. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Improve documentation - Fix typo in commit message --- include/libcamera/internal/value_node.h | 2 ++ src/libcamera/value_node.cpp | 42 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index 3bdb537e9eab..d237b2c1e0d6 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -230,9 +230,11 @@ public: ConstDictAdapter asDict() const { return ConstDictAdapter{ list_ }; } ConstListAdapter asList() const { return ConstListAdapter{ list_ }; } + ValueNode *at(std::size_t index); const ValueNode &operator[](std::size_t index) const; bool contains(std::string_view key) const; + ValueNode *at(std::string_view key); const ValueNode &operator[](std::string_view key) const; ValueNode *add(std::unique_ptr &&child); diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index 5aef72cd29a4..4c3a5e4d381d 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -384,6 +384,25 @@ template struct ValueNode::Accessor>; * \return An adapter of unspecified type compatible with range-based for loops */ +/** + * \brief Retrieve the element from list ValueNode by index + * \param[in] index The element index + * + * This function retrieves an element of the ValueNode. Only ValueNode + * instances of List type associate elements with an index, calling this + * function on other types of instances or with an invalid index returns a null + * pointer. + * + * \return The ValueNode corresponding to \a index + */ +ValueNode *ValueNode::at(std::size_t index) +{ + if (type_ != Type::List || index >= size()) + return nullptr; + + return list_[index].value.get(); +} + /** * \brief Retrieve the element from list ValueNode by index * \param[in] index The element index @@ -419,6 +438,29 @@ bool ValueNode::contains(std::string_view key) const return dictionary_.find(key) != dictionary_.end(); } +/** + * \brief Retrieve a member by key from the dictionary + * \param[in] key The element key + * + * This function retrieves a member of a ValueNode by \a key. Only ValueNode + * instances of Dictionary type associate elements with keys, calling this + * function on other types of instances or with a nonexistent key returns a null + * pointer. + * + * \return The ValueNode corresponding to the \a key member + */ +ValueNode *ValueNode::at(std::string_view key) +{ + if (type_ != Type::Dictionary) + return nullptr; + + auto iter = dictionary_.find(key); + if (iter == dictionary_.end()) + return nullptr; + + return iter->second; +} + /** * \brief Retrieve a member by key from the dictionary * \param[in] key The element key From patchwork Tue Apr 7 15:34:09 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26464 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 0C3E0C3308 for ; Tue, 7 Apr 2026 15:35:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 89D5362E08; Tue, 7 Apr 2026 17:35:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ddkpBlOm"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A2C062DF3 for ; Tue, 7 Apr 2026 17:35:01 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 035F6596 for ; Tue, 7 Apr 2026 17:33:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576014; bh=ZfrG/3RBI3gpHlOmZR8SJEw+XMb4nfySn9LrELgFp7s=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ddkpBlOm0fPwc7tEs6DVGeUEZZLlNSCunkHxGQ92W8q4PpGo1XGuklrHhzlyrp7Th HMpD0u7QZ2NWJKomOfdIzu/EPVnWt4/FzXMmn1sDubI+CChPHe5ilvj6XCuLIF1lEt oGUSxpE2he8ZVYzwY8RhqBFVCIEAcA/Pr3fh+O1U= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 24/42] libcamera: value_node: Support adding nested children in one operation Date: Tue, 7 Apr 2026 18:34:09 +0300 Message-ID: <20260407153427.1825999-25-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 GlobalConfiguration class will need to add nested children to a ValueNode. Add a new overload to the add() function for this purpose. Signed-off-by: Laurent Pinchart --- Changes since v1: - Documentation improvements - Replace NULL with nullptr - Don't move child node if add fails --- include/libcamera/internal/value_node.h | 3 ++ src/libcamera/value_node.cpp | 59 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index d237b2c1e0d6..be28c2b73832 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -239,6 +240,8 @@ public: ValueNode *add(std::unique_ptr &&child); ValueNode *add(std::string key, std::unique_ptr &&child); + ValueNode *add(std::initializer_list path, + std::unique_ptr &&child); private: LIBCAMERA_DISABLE_COPY_AND_MOVE(ValueNode) diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index 4c3a5e4d381d..e8db7ef3c37f 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include /** @@ -22,6 +24,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(ValueNode) + namespace { /* Empty static ValueNode as a safe result for invalid operations */ @@ -539,4 +543,59 @@ ValueNode *ValueNode::add(std::string key, std::unique_ptr &&child) return list_.emplace_back(it->first, std::move(child)).value.get(); } +/** + * \brief Add a child node at the given path + * \param[in] path The path + * \param[in] child The child node + * + * Add the \a child node at the given \a path starting at this node. Missing + * nodes are created along the path. Nodes along the path must be empty (in + * which case they are converted to the Type::Dictionary type), be a dictionary, + * or be missing. Otherwise, the function returns a nullptr and the \a child is + * not modified. + * + * Path elements are unique in the context of a parent node. If a child with the + * same \a key already exist at the end of the path, the function returns a + * nullptr and the \a child is not modified. + * + * \note Any node added along the \a path will remain even if this function + * returns a failure. + * + * \return A pointer to the \a child node if successfully added, nullptr + * otherwise + */ +ValueNode *ValueNode::add(std::initializer_list path, + std::unique_ptr &&child) +{ + if (!path.size()) + return nullptr; + + ValueNode *node = this; + + for (const auto [i, name] : utils::enumerate(path)) { + auto iter = node->dictionary_.find(name); + if (iter == node->dictionary_.end()) { + std::unique_ptr obj; + + if (i < path.size() - 1) + obj = std::make_unique(); + else + obj = std::move(child); + + node = node->add(std::string{ name }, std::move(obj)); + if (!node) { + Span pathName{ std::data(path), i + 1 }; + LOG(ValueNode, Error) + << "Failed to populate '" + << utils::join(pathName, "/") << "'"; + return nullptr; + } + } else { + node = iter->second; + } + } + + return node; +} + } /* namespace libcamera */ From patchwork Tue Apr 7 15:34:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26465 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 8F36EC330A for ; Tue, 7 Apr 2026 15:35:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BDE8762E0F; Tue, 7 Apr 2026 17:35:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="QD2pUbqe"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E6DC062DFF for ; Tue, 7 Apr 2026 17:35:02 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6047A6A6 for ; Tue, 7 Apr 2026 17:33:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576015; bh=ciUfVyVHVmbZDdVkctsK/9fbleSWzXsDOR4lyHxsANM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=QD2pUbqe35StF5JJk1YK/eqQ7+c8Ao8+lZCEepMJe2W31+H0c2mC8bpXyFmLog/Ws LbTleARGbn3Ut/55bKx5D15gpyp3nHme7RzRZk2xxGKmq5fl2rjI4/5o/FrHanhvgF nJMs1CAedKAckWGre/weQPkTKzeUnh1rhai4Mb7c= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 25/42] libcamera: value_node: Support looking up descendant node by path Date: Tue, 7 Apr 2026 18:34:10 +0300 Message-ID: <20260407153427.1825999-26-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Looking up a descendant node based on a path is a common operation. Add a helper function to do so, to avoid loops in the callers. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Use reference in range-based for loop - Improve commit message --- include/libcamera/internal/value_node.h | 1 + src/libcamera/value_node.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index be28c2b73832..14c5e0eb0202 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -237,6 +237,7 @@ public: bool contains(std::string_view key) const; ValueNode *at(std::string_view key); const ValueNode &operator[](std::string_view key) const; + const ValueNode &operator[](std::initializer_list path) const; ValueNode *add(std::unique_ptr &&child); ValueNode *add(std::string key, std::unique_ptr &&child); diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index e8db7ef3c37f..990e46d70358 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -488,6 +488,29 @@ const ValueNode &ValueNode::operator[](std::string_view key) const return *iter->second; } +/** + * \brief Retrieve a descendant node by path + * \param[in] path The path + * + * This function retrieves a descendant of a ValueNode by following a \a path. + * The path is a list of keys that index nested dictionary nodes. If any node + * along the path is not a Dictionary node, an empty node is returned. + * + * \return The ValueNode corresponding to the \a path + */ +const ValueNode &ValueNode::operator[](std::initializer_list path) const +{ + const ValueNode *node = this; + + for (const auto &part : path) { + node = &(*node)[part]; + if (!*node) + return empty; + } + + return *node; +} + /** * \brief Add a child node to a list * \param[in] child The child node From patchwork Tue Apr 7 15:34:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26466 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 43065C330F for ; Tue, 7 Apr 2026 15:35:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B369C62E19; Tue, 7 Apr 2026 17:35:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="AUTP5JaN"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3D3AB62DFA for ; Tue, 7 Apr 2026 17:35:04 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B400C6DC for ; Tue, 7 Apr 2026 17:33:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576016; bh=RBN/KyATuh2ePugqLEn7mn3YOx/34nrCZSFLXo1eNVY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=AUTP5JaN9PfD6C1g2s4Uf/LKAYp0QebhbXGdhcuyu9KICQr6HNO1QqMn2wEf9TEPF YDokhjOIFULlgHYXRcg262PUtCnRgHQ+iveqSWbiXKbJpvia6h+GOPWI1ef7CZ3bBR wZ+A4t/U0DcXkkKSVaK9fc5pkUEjJs5YVXXd2DL0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 26/42] libcamera: value_node: Add functions to erase child nodes Date: Tue, 7 Apr 2026 18:34:11 +0300 Message-ID: <20260407153427.1825999-27-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" There will be a need to erase child nodes when implementing support for overriding configuration options. Add two erase() functions to the ValueNode class, mimicking the add() API. More erase() overloads could be added, for instance taking iterators as parameters, to improve efficiency. This should be considered later, when usage of the ValueNode class will expand, based on the actual usage patterns. Signed-off-by: Laurent Pinchart --- include/libcamera/internal/value_node.h | 3 ++ src/libcamera/value_node.cpp | 51 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index 14c5e0eb0202..4e3757af7d09 100644 --- a/include/libcamera/internal/value_node.h +++ b/include/libcamera/internal/value_node.h @@ -244,6 +244,9 @@ public: ValueNode *add(std::initializer_list path, std::unique_ptr &&child); + void erase(const std::string &key); + void erase(std::initializer_list path); + private: LIBCAMERA_DISABLE_COPY_AND_MOVE(ValueNode) diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp index 990e46d70358..8a1af13a5d6e 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -621,4 +621,55 @@ ValueNode *ValueNode::add(std::initializer_list path, return node; } +/** + * \brief Erase a child node in a dictionary + * \param[in] key The dictionary key + * + * Erase the child node referenced by \a key in a dictionary node. If the \a + * key does not exist, or if this node is not a dictionary, the function + * returns without performing any operation. + */ +void ValueNode::erase(const std::string &key) +{ + auto node = dictionary_.extract(key); + if (node.empty()) + return; + + for (auto iter = list_.begin(); iter != list_.end(); ++iter) { + if (iter->value.get() != node.mapped()) + continue; + + list_.erase(iter); + return; + } +} + +/** + * \brief Erase the child node at the given path + * \param[in] path The path + * + * Erase the child node at the given \a path. If no child node exists for the + * path, the function returns without performing any operation. + */ +void ValueNode::erase(std::initializer_list path) +{ + if (!path.size()) + return; + + ValueNode *node = this; + + for (const auto [i, name] : utils::enumerate(path)) { + if (i == path.size() - 1) { + node->erase(std::string{ name }); + return; + } + + auto iter = node->dictionary_.find(name); + if (iter == node->dictionary_.end()) + return; + + node = iter->second; + } +} + } /* namespace libcamera */ From patchwork Tue Apr 7 15:34:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26467 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 6C02FC3316 for ; Tue, 7 Apr 2026 15:35:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BA8DC62E09; Tue, 7 Apr 2026 17:35:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PdMV9S7U"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A23AB62E0F for ; Tue, 7 Apr 2026 17:35:05 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 0E85778E for ; Tue, 7 Apr 2026 17:33:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576018; bh=HOt119qKSFJu/3Px9eFQewp9OFlZyFGA9ULH743CQQ0=; h=From:To:Subject:Date:In-Reply-To:References:From; b=PdMV9S7UzK2ElSf02CtiVk915juilZutdPdIcxEuw6gAkmRQf4Vjs8NxziPVQPR15 XZQV19eLDfpJAJNeFYjJfg7/uDRiPJIXPvy34B3YaZxMZp66yJRdcNSgf8ObC8kFDc PkUv7j0WN5yloVX9lPNKeHi/ADrC9e2X7kwq1Zpo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 27/42] test: Add ValueNode unit test Date: Tue, 7 Apr 2026 18:34:12 +0300 Message-ID: <20260407153427.1825999-28-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Signed-off-by: Laurent Pinchart --- test/meson.build | 1 + test/value-node.cpp | 565 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 test/value-node.cpp diff --git a/test/meson.build b/test/meson.build index 52f04364e4fc..e4450625ee4c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -74,6 +74,7 @@ internal_tests = [ {'name': 'timer-thread', 'sources': ['timer-thread.cpp']}, {'name': 'unique-fd', 'sources': ['unique-fd.cpp']}, {'name': 'utils', 'sources': ['utils.cpp']}, + {'name': 'value-node', 'sources': ['value-node.cpp']}, {'name': 'vector', 'sources': ['vector.cpp']}, {'name': 'yaml-parser', 'sources': ['yaml-parser.cpp']}, ] diff --git a/test/value-node.cpp b/test/value-node.cpp new file mode 100644 index 000000000000..e9cb7ae11b9f --- /dev/null +++ b/test/value-node.cpp @@ -0,0 +1,565 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026, Ideas on Board + * + * ValueNode tests + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/value_node.h" + +#include "test.h" + +using namespace libcamera; +using namespace std; + +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...) -> overloaded; + +class ValueNodeTest : public Test +{ +protected: + enum class NodeType { + Empty, + Value, + List, + Dictionary, + }; + + enum class ValueType { + Int8, + UInt8, + Int16, + UInt16, + Int32, + UInt32, + Float, + Double, + String, + Size, + }; + + int testNodeValueType(const ValueNode &node, std::string_view name, ValueType type) + { + bool isInteger8 = type == ValueType::Int8 || type == ValueType::UInt8; + bool isInteger16 = type == ValueType::Int16 || type == ValueType::UInt16; + bool isInteger32 = type == ValueType::Int32 || type == ValueType::UInt32; + bool isIntegerUpTo16 = isInteger8 || isInteger16; + bool isIntegerUpTo32 = isIntegerUpTo16 || isInteger32; + bool isSigned = type == ValueType::Int8 || type == ValueType::Int16 || + type == ValueType::Int32; + + if (!isInteger8 && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "int8_t" << std::endl; + return TestFail; + } + + if ((!isInteger8 || isSigned) && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "uint8_t" << std::endl; + return TestFail; + } + + if (!isIntegerUpTo16 && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "int16_t" << std::endl; + return TestFail; + } + + if ((!isIntegerUpTo16 || isSigned) && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "uint16_t" << std::endl; + return TestFail; + } + + if (!isIntegerUpTo32 && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "int32_t" << std::endl; + return TestFail; + } + + if ((!isIntegerUpTo32 || isSigned) && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "uint32_t" << std::endl; + return TestFail; + } + + if (!isIntegerUpTo32 && type != ValueType::Float && + type != ValueType::Double && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "double" << std::endl; + return TestFail; + } + + if (type != ValueType::Size && node.get()) { + std::cerr + << "Node " << name << " didn't fail to parse as " + << "Size" << std::endl; + return TestFail; + } + + return TestPass; + } + + int testIntegerValue(const ValueNode &node, std::string_view name, + ValueType type, int64_t value) + { + uint64_t unsignedValue = static_cast(value); + std::string strValue = std::to_string(value); + bool isSigned = type == ValueType::Int8 || type == ValueType::Int16 || + type == ValueType::Int32; + bool isInteger8 = type == ValueType::Int8 || type == ValueType::UInt8; + bool isInteger16 = type == ValueType::Int16 || type == ValueType::UInt16; + + /* All integers can be accessed as strings and double. */ + + if (node.get().value_or("") != strValue || + node.get("") != strValue) { + std::cerr + << "Node " << name << " failed to parse as " + << "string" << std::endl; + return TestFail; + } + + if (node.get().value_or(0.0) != value || + node.get(0.0) != value) { + std::cerr + << "Node " << name << " failed to parse as " + << "double" << std::endl; + return TestFail; + } + + if (isInteger8) { + if (node.get().value_or(0) != value || + node.get(0) != value) { + std::cerr + << "Node " << name << " failed to parse as " + << "int8_t" << std::endl; + return TestFail; + } + } + + if (isInteger8 && !isSigned) { + if (node.get().value_or(0) != unsignedValue || + node.get(0) != unsignedValue) { + std::cerr + << "Node " << name << " failed to parse as " + << "uint8_t" << std::endl; + return TestFail; + } + } + + if (isInteger8 || isInteger16) { + if (node.get().value_or(0) != value || + node.get(0) != value) { + std::cerr + << "Node " << name << " failed to parse as " + << "int16_t" << std::endl; + return TestFail; + } + } + + if ((isInteger8 || isInteger16) && !isSigned) { + if (node.get().value_or(0) != unsignedValue || + node.get(0) != unsignedValue) { + std::cerr + << "Node " << name << " failed to parse as " + << "uint16_t" << std::endl; + return TestFail; + } + } + + if (node.get().value_or(0) != value || + node.get(0) != value) { + std::cerr + << "Node " << name << " failed to parse as " + << "int32_t" << std::endl; + return TestFail; + } + + if (!isSigned) { + if (node.get().value_or(0) != unsignedValue || + node.get(0) != unsignedValue) { + std::cerr + << "Node " << name << " failed to parse as " + << "uint32_t" << std::endl; + return TestFail; + } + } + + return TestPass; + } + + template + bool equal(const ValueNode &node, T value) + { + constexpr T eps = std::numeric_limits::epsilon(); + + if (std::abs(node.get().value_or(0.0) - value) >= eps) + return false; + if (std::abs(node.get(0.0) - value) >= eps) + return false; + return true; + } + + int testFloatValue(const ValueNode &node, std::string_view name, double value) + { + std::string strValue = std::to_string(value); + + if (node.get().value_or("") != strValue || + node.get("") != strValue) { + std::cerr + << "Node " << name << " failed to parse as " + << "string" << std::endl; + return TestFail; + } + + if (!equal(node, value)) { + std::cerr + << "Node " << name << " failed to parse as " + << "float" << std::endl; + return TestFail; + } + + if (!equal(node, value)) { + std::cerr + << "Node " << name << " failed to parse as " + << "double" << std::endl; + return TestFail; + } + + return TestPass; + } + + bool testNodeType(const ValueNode &node, NodeType nodeType) + { + using NodeFunc = bool (ValueNode::*)() const; + using NodeDesc = std::tuple; + + static constexpr std::array nodeTypes = { { + NodeDesc{ NodeType::Empty, "empty", &ValueNode::isEmpty }, + NodeDesc{ NodeType::Value, "value", &ValueNode::isValue }, + NodeDesc{ NodeType::List, "list", &ValueNode::isList }, + NodeDesc{ NodeType::Dictionary, "dictionary", &ValueNode::isDictionary }, + } }; + + for (const auto &[type, name, func] : nodeTypes) { + bool value = type == nodeType; + if ((node.*func)() != value) { + std::cerr + << "Empty ValueNode should " + << (value ? "" : "not ") << "be a " + << name << std::endl; + return false; + } + } + + return true; + } + + int run() + { + /* Tests on empty nodes. */ + ValueNode emptyNode; + + if (!testNodeType(emptyNode, NodeType::Empty)) { + std::cerr + << "Empty node should have empty type" + << std::endl; + return TestFail; + } + + if (static_cast(emptyNode)) { + std::cerr + << "Empty node should cast to false" + << std::endl; + return TestFail; + } + + if (emptyNode.size()) { + std::cerr + << "Empty node should have zero size" + << std::endl; + return TestFail; + } + + if (emptyNode.get()) { + std::cerr + << "Empty node should have no value" + << std::endl; + return TestFail; + } + + /* Tests on list nodes. */ + ValueNode listNode; + + static constexpr std::array listElemNames = { + "libcamera", "linux", "isp" + }; + + for (const auto &name : listElemNames) + listNode.add(std::make_unique(std::string{ name })); + + if (!testNodeType(listNode, NodeType::List)) + return TestFail; + + if (!static_cast(listNode)) { + std::cerr + << "List node should cast to true" + << std::endl; + return TestFail; + } + + if (listNode.size() != 3) { + std::cerr << "Invalid list node size" << std::endl; + return TestFail; + } + + listNode.set("value"s); + if (listNode.get()) { + std::cerr + << "Setting a value on a list node should fail" + << std::endl; + return TestFail; + } + + std::set names{ + listElemNames.begin(), listElemNames.end() + }; + + for (const auto &child : listNode.asList()) { + const std::string childName = child.get(""); + + if (!names.erase(childName)) { + std::cerr + << "Invalid list child '" << childName + << "'" << std::endl; + return TestFail; + } + } + + if (!names.empty()) { + std::cerr + << "Missing elements in list: " + << utils::join(names, ", ") << std::endl; + return TestFail; + } + + /* Tests on dictionary nodes. */ + ValueNode dictNode; + + static const std::array, 3> dictElemKeyValues = { { + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + } }; + + for (const auto &[key, value] : dictElemKeyValues) + dictNode.add(key, std::make_unique(value)); + + if (!testNodeType(dictNode, NodeType::Dictionary)) + return TestFail; + + if (!static_cast(dictNode)) { + std::cerr + << "Dictionary node should cast to true" + << std::endl; + return TestFail; + } + + if (dictNode.size() != 3) { + std::cerr << "Invalid dictionary node size" << std::endl; + return TestFail; + } + + dictNode.set("value"s); + if (dictNode.get()) { + std::cerr + << "Setting a value on a dict node should fail" + << std::endl; + return TestFail; + } + + std::map keyValues{ + dictElemKeyValues.begin(), dictElemKeyValues.end() + }; + + for (const auto &[key, child] : dictNode.asDict()) { + auto iter = keyValues.find(key); + if (iter == keyValues.end()) { + std::cerr + << "Invalid dictionary key '" << key + << "'" << std::endl; + return TestFail; + } + + const int value = child.get(0); + if (value != iter->second) { + std::cerr + << "Invalid dictionary value " << value + << " for key '" << key << "'" << std::endl; + return TestFail; + } + + if (dictNode[key].get(0) != value) { + std::cerr + << "Dictionary lookup failed for key '" + << key << "'" << std::endl; + return TestFail; + } + + keyValues.erase(iter); + } + + if (!keyValues.empty()) { + std::cerr + << "Missing elements in dictionary: " + << utils::join(utils::map_keys(keyValues), ", ") + << std::endl; + return TestFail; + } + + if (!dictNode["nonexistent"].isEmpty()) { + std::cerr + << "Accessing nonexistent dictionary element returns non-empty node" + << std::endl; + return TestFail; + } + + /* Make sure utils::map_keys() works on the adapter. */ + (void)utils::map_keys(dictNode.asDict()); + + /* Tests on value nodes. */ + ValueNode values; + + values.add("int8_t", std::make_unique(static_cast(-100))); + values.add("uint8_t", std::make_unique(static_cast(100))); + values.add("int16_t", std::make_unique(static_cast(-1000))); + values.add("uint16_t", std::make_unique(static_cast(1000))); + values.add("int32_t", std::make_unique(static_cast(-100000))); + values.add("uint32_t", std::make_unique(static_cast(100000))); + values.add("float", std::make_unique(3.14159f)); + values.add("double", std::make_unique(3.14159)); + values.add("string", std::make_unique("libcamera"s)); + + std::unique_ptr sizeNode = std::make_unique(); + sizeNode->add(std::make_unique(640)); + sizeNode->add(std::make_unique(480)); + + values.add("size", std::move(sizeNode)); + + using ValueVariant = std::variant; + + static const + std::array, 10> nodesValues{ { + { "int8_t", ValueType::Int8, static_cast(-100) }, + { "uint8_t", ValueType::UInt8, static_cast(100) }, + { "int16_t", ValueType::Int16, static_cast(-1000) }, + { "uint16_t", ValueType::UInt16, static_cast(1000) }, + { "int32_t", ValueType::Int32, static_cast(-100000) }, + { "uint32_t", ValueType::UInt32, static_cast(100000) }, + { "float", ValueType::Float, 3.14159 }, + { "double", ValueType::Double, 3.14159 }, + { "string", ValueType::String, "libcamera" }, + { "size", ValueType::Size, Size{ 640, 480 } }, + } }; + + for (const auto &nodeValue : nodesValues) { + /* + * P0588R1 (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0588r1.html) + * explicitly forbids a lambda from capturing structured + * bindings. This was fixed in a later release of the + * C++ specification, but some compilers (including + * clang-14 used in CI) choke on it. We can't use + * structured bindings in the for loop, unpack the tuple + * manually instead. + */ + const auto &name = std::get<0>(nodeValue); + const auto &type = std::get<1>(nodeValue); + const auto &value = std::get<2>(nodeValue); + + const ValueNode &node = values[name]; + + if (testNodeValueType(node, name, type) != TestPass) + return TestFail; + + int ret = std::visit(overloaded{ + [&](int64_t arg) -> int { + return testIntegerValue(node, name, type, arg); + }, + + [&](double arg) -> int { + return testFloatValue(node, name, arg); + }, + + [&](const Size &arg) -> int { + if (node.get().value_or(Size{}) != arg || + node.get(Size{}) != arg) { + std::cerr + << "Invalid node size value" + << std::endl; + return TestFail; + } + + return TestPass; + }, + + [&](const std::string &arg) -> int { + if (node.get().value_or(std::string{}) != arg || + node.get(std::string{}) != arg) { + std::cerr + << "Invalid node string value" + << std::endl; + return TestFail; + } + + return TestPass; + }, + }, value); + + if (ret != TestPass) + return ret; + } + + /* Test erasure. */ + values.erase("float"); + if (values.contains("float")) { + std::cerr << "Failed to erase child node" << std::endl; + return TestFail; + } + + values.add({ "a", "b", "c" }, std::make_unique(0)); + values.erase({ "a", "b" }); + if (values["a"].contains("b")) { + std::cerr << "Failed to erase descendant node" << std::endl; + return TestFail; + } + + return TestPass; + } +}; + +TEST_REGISTER(ValueNodeTest) From patchwork Tue Apr 7 15:34:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26468 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 B02DBC3318 for ; Tue, 7 Apr 2026 15:35:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C204262E14; Tue, 7 Apr 2026 17:35:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="u7Y+BOH4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1A3A062DF3 for ; Tue, 7 Apr 2026 17:35:07 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6709178E for ; Tue, 7 Apr 2026 17:33:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576019; bh=DAIODyJlod0XmZfvzA3o03Ugcr9m2dtZ71EOuQ5PcgQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=u7Y+BOH45NfBT7KGLZ12A9M7dU6eCqq2Z92HD+0XScKwo4hruTBlBVOFVIGC9S0/L IWLENa/EVYbipdM8gcDCwLmyn8p758iVPAOYcB7kLowcH/Rh1lzM5H5L7DlQ38VfWd iHsyYEf/TZc2+oYpEHKnpcplYctrwVfbB9H0v0lk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 28/42] test: yaml-parser: Simplify test Date: Tue, 7 Apr 2026 18:34:13 +0300 Message-ID: <20260407153427.1825999-29-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Signed-off-by: Laurent Pinchart --- test/yaml-parser.cpp | 469 +++++-------------------------------------- 1 file changed, 50 insertions(+), 419 deletions(-) diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index 8c5826f4885b..0bbda3af8ea9 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -12,7 +12,6 @@ #include #include -#include #include @@ -24,24 +23,12 @@ using namespace libcamera; using namespace std; static const string testYaml = - "string: libcamera\n" - "double: 3.14159\n" - "int8_t: -100\n" - "uint8_t: 100\n" - "int16_t: -1000\n" - "uint16_t: 1000\n" - "int32_t: -100000\n" - "uint32_t: 100000\n" - "size: [1920, 1080]\n" + "empty:\n" + "value: 42\n" "list:\n" - " - James\n" - " - Mary\n" + " - libcamera\n" + " - linux\n" " - \n" - "dictionary:\n" - " a: 1\n" - " c: 3\n" - " b: 2\n" - " empty:\n" "level1:\n" " level2:\n" " - [1, 2]\n" @@ -80,212 +67,9 @@ protected: return TestPass; } - enum class Type { - String, - Int8, - UInt8, - Int16, - UInt16, - Int32, - UInt32, - Double, - Size, - List, - Dictionary, - }; - - int testObjectType(const ValueNode &obj, const char *name, Type type) - { - bool isList = type == Type::List || type == Type::Size; - bool isScalar = !isList && type != Type::Dictionary; - bool isInteger8 = type == Type::Int8 || type == Type::UInt8; - bool isInteger16 = type == Type::Int16 || type == Type::UInt16; - bool isInteger32 = type == Type::Int32 || type == Type::UInt32; - bool isIntegerUpTo16 = isInteger8 || isInteger16; - bool isIntegerUpTo32 = isIntegerUpTo16 || isInteger32; - bool isSigned = type == Type::Int8 || type == Type::Int16 || - type == Type::Int32; - - if ((isScalar && !obj.isValue()) || (!isScalar && obj.isValue())) { - std::cerr - << "Object " << name << " type mismatch when compared to " - << "value" << std::endl; - return TestFail; - } - - if ((isList && !obj.isList()) || (!isList && obj.isList())) { - std::cerr - << "Object " << name << " type mismatch when compared to " - << "list" << std::endl; - return TestFail; - } - - if ((type == Type::Dictionary && !obj.isDictionary()) || - (type != Type::Dictionary && obj.isDictionary())) { - std::cerr - << "Object " << name << " type mismatch when compared to " - << "dictionary" << std::endl; - return TestFail; - } - - if (!isScalar && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "string" << std::endl; - return TestFail; - } - - if (!isInteger8 && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "int8_t" << std::endl; - return TestFail; - } - - if ((!isInteger8 || isSigned) && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "uint8_t" << std::endl; - return TestFail; - } - - if (!isIntegerUpTo16 && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "int16_t" << std::endl; - return TestFail; - } - - if ((!isIntegerUpTo16 || isSigned) && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "uint16_t" << std::endl; - return TestFail; - } - - if (!isIntegerUpTo32 && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "int32_t" << std::endl; - return TestFail; - } - - if ((!isIntegerUpTo32 || isSigned) && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "uint32_t" << std::endl; - return TestFail; - } - - if (!isIntegerUpTo32 && type != Type::Double && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "double" << std::endl; - return TestFail; - } - - if (type != Type::Size && obj.get()) { - std::cerr - << "Object " << name << " didn't fail to parse as " - << "Size" << std::endl; - return TestFail; - } - - return TestPass; - } - - int testIntegerObject(const ValueNode &obj, const char *name, Type type, - int64_t value) - { - uint64_t unsignedValue = static_cast(value); - std::string strValue = std::to_string(value); - bool isInteger8 = type == Type::Int8 || type == Type::UInt8; - bool isInteger16 = type == Type::Int16 || type == Type::UInt16; - bool isSigned = type == Type::Int8 || type == Type::Int16 || - type == Type::Int32; - - /* All integers can be parsed as strings or double. */ - - if (obj.get().value_or("") != strValue || - obj.get("") != strValue) { - std::cerr - << "Object " << name << " failed to parse as " - << "string" << std::endl; - return TestFail; - } - - if (obj.get().value_or(0.0) != value || - obj.get(0.0) != value) { - std::cerr - << "Object " << name << " failed to parse as " - << "double" << std::endl; - return TestFail; - } - - if (isInteger8) { - if (obj.get().value_or(0) != value || - obj.get(0) != value) { - std::cerr - << "Object " << name << " failed to parse as " - << "int8_t" << std::endl; - return TestFail; - } - } - - if (isInteger8 && !isSigned) { - if (obj.get().value_or(0) != unsignedValue || - obj.get(0) != unsignedValue) { - std::cerr - << "Object " << name << " failed to parse as " - << "uint8_t" << std::endl; - return TestFail; - } - } - - if (isInteger8 || isInteger16) { - if (obj.get().value_or(0) != value || - obj.get(0) != value) { - std::cerr - << "Object " << name << " failed to parse as " - << "int16_t" << std::endl; - return TestFail; - } - } - - if ((isInteger8 || isInteger16) && !isSigned) { - if (obj.get().value_or(0) != unsignedValue || - obj.get(0) != unsignedValue) { - std::cerr - << "Object " << name << " failed to parse as " - << "uint16_t" << std::endl; - return TestFail; - } - } - - if (obj.get().value_or(0) != value || - obj.get(0) != value) { - std::cerr - << "Object " << name << " failed to parse as " - << "int32_t" << std::endl; - return TestFail; - } - - if (!isSigned) { - if (obj.get().value_or(0) != unsignedValue || - obj.get(0) != unsignedValue) { - std::cerr - << "Object " << name << " failed to parse as " - << "uint32_t" << std::endl; - return TestFail; - } - } - - return TestPass; - } - int run() { - /* Test invalid YAML file */ + /* Test parsing invalid YAML file. */ File file{ invalidYamlFile_ }; if (!file.open(File::OpenModeFlag::ReadOnly)) { cerr << "Fail to open invalid YAML file" << std::endl; @@ -298,7 +82,7 @@ protected: return TestFail; } - /* Test YAML file */ + /* Test parsing valid YAML file. */ file.close(); file.setFileName(testYamlFile_); if (!file.open(File::OpenModeFlag::ReadOnly)) { @@ -313,130 +97,66 @@ protected: return TestFail; } + /* Test that the root dictionary node has been parsed correctly. */ if (!root->isDictionary()) { - cerr << "YAML root is not dictionary" << std::endl; + cerr << "Dictionary node has wrong type" << std::endl; return TestFail; } - std::vector rootElemNames = { - "string", "double", "int8_t", "uint8_t", "int16_t", - "uint16_t", "int32_t", "uint32_t", "size", "list", - "dictionary", "level1", - }; + using NodeFunc = bool (ValueNode::*)() const; - for (const char *name : rootElemNames) { - if (!root->contains(name)) { - cerr << "Missing " << name << " object in YAML root" - << std::endl; + std::map topLevelNodes = { { + { "empty", &ValueNode::isValue }, + { "value", &ValueNode::isValue }, + { "list", &ValueNode::isList }, + { "level1", &ValueNode::isDictionary }, + } }; + + if (root->size() != topLevelNodes.size()) { + std::cerr << "Dictionary node has wrong size" << std::endl; + return TestFail; + } + + for (const auto &[key, value] : root->asDict()) { + const auto iter = topLevelNodes.find(key); + if (iter == topLevelNodes.end()) { + std::cerr << "Dictionary key '" << key << "' unknown" + << std::endl; return TestFail; } + + const auto &func = iter->second; + if (!(value.*func)()) { + std::cerr << "Node '" << key << "' has wrong type" + << std::endl; + return TestFail; + } + + topLevelNodes.erase(iter); } - /* Test string object */ - auto &strObj = (*root)["string"]; + /* Test empty node. */ + auto &emptyNode = (*root)["empty"]; - if (testObjectType(strObj, "string", Type::String) != TestPass) - return TestFail; - - if (strObj.get().value_or("") != "libcamera" || - strObj.get("") != "libcamera") { - cerr << "String object parse as wrong content" << std::endl; + if (emptyNode.get("-") != "") { + std::cerr << "Empty node has incorrect content" << std::endl; return TestFail; } - /* Test int8_t object */ - auto &int8Obj = (*root)["int8_t"]; + /* Test value node. */ + auto &valueNode = (*root)["value"]; - if (testObjectType(int8Obj, "int8_t", Type::Int8) != TestPass) - return TestFail; - - if (testIntegerObject(int8Obj, "int8_t", Type::Int8, -100) != TestPass) - return TestFail; - - /* Test uint8_t object */ - auto &uint8Obj = (*root)["uint8_t"]; - - if (testObjectType(uint8Obj, "uint8_t", Type::UInt8) != TestPass) - return TestFail; - - if (testIntegerObject(uint8Obj, "uint8_t", Type::UInt8, 100) != TestPass) - return TestFail; - - /* Test int16_t object */ - auto &int16Obj = (*root)["int16_t"]; - - if (testObjectType(int16Obj, "int16_t", Type::Int16) != TestPass) - return TestFail; - - if (testIntegerObject(int16Obj, "int16_t", Type::Int16, -1000) != TestPass) - return TestFail; - - /* Test uint16_t object */ - auto &uint16Obj = (*root)["uint16_t"]; - - if (testObjectType(uint16Obj, "uint16_t", Type::UInt16) != TestPass) - return TestFail; - - if (testIntegerObject(uint16Obj, "uint16_t", Type::UInt16, 1000) != TestPass) - return TestFail; - - /* Test int32_t object */ - auto &int32Obj = (*root)["int32_t"]; - - if (testObjectType(int32Obj, "int32_t", Type::Int32) != TestPass) - return TestFail; - - if (testIntegerObject(int32Obj, "int32_t", Type::Int32, -100000) != TestPass) - return TestFail; - - /* Test uint32_t object */ - auto &uint32Obj = (*root)["uint32_t"]; - - if (testObjectType(uint32Obj, "uint32_t", Type::UInt32) != TestPass) - return TestFail; - - if (testIntegerObject(uint32Obj, "uint32_t", Type::UInt32, 100000) != TestPass) - return TestFail; - - /* Test double value */ - auto &doubleObj = (*root)["double"]; - - if (testObjectType(doubleObj, "double", Type::Double) != TestPass) - return TestFail; - - if (doubleObj.get().value_or("") != "3.14159" || - doubleObj.get("") != "3.14159") { - cerr << "Double object fail to parse as string" << std::endl; + if (valueNode.get("") != "42") { + std::cerr << "Value node has incorrect content" << std::endl; return TestFail; } - if (doubleObj.get().value_or(0.0) != 3.14159 || - doubleObj.get(0.0) != 3.14159) { - cerr << "Double object parse as wrong value" << std::endl; - return TestFail; - } - - /* Test Size value */ - auto &sizeObj = (*root)["size"]; - - if (testObjectType(sizeObj, "size", Type::Size) != TestPass) - return TestFail; - - if (sizeObj.get().value_or(Size(0, 0)) != Size(1920, 1080) || - sizeObj.get(Size(0, 0)) != Size(1920, 1080)) { - cerr << "Size object parse as wrong value" << std::endl; - return TestFail; - } - - /* Test list object */ + /* Test list node. */ auto &listObj = (*root)["list"]; - if (testObjectType(listObj, "list", Type::List) != TestPass) - return TestFail; - static constexpr std::array listValues{ - "James", - "Mary", + "libcamera", + "linux", "", }; @@ -470,102 +190,13 @@ protected: i++; } - /* Ensure that empty objects get parsed as empty strings. */ + /* Ensure that empty list elements get parsed as empty strings. */ if (!listObj[2].isValue()) { - cerr << "Empty object is not a value" << std::endl; + cerr << "Empty list element is not a value" << std::endl; return TestFail; } - /* Test dictionary object */ - auto &dictObj = (*root)["dictionary"]; - - if (testObjectType(dictObj, "dictionary", Type::Dictionary) != TestPass) - return TestFail; - - static constexpr std::array, 4> dictValues{ { - { "a", 1 }, - { "c", 3 }, - { "b", 2 }, - { "empty", -100 }, - } }; - - size_t dictSize = dictValues.size(); - - if (dictObj.size() != dictSize) { - cerr << "Dictionary object has wrong size" << std::endl; - return TestFail; - } - - i = 0; - for (const auto &[key, elem] : dictObj.asDict()) { - if (i >= dictSize) { - std::cerr << "Too many elements in dictionary during iteration" - << std::endl; - return TestFail; - } - - const auto &item = dictValues[i]; - if (item.first != key) { - std::cerr << "Dictionary key " << i << " has wrong value" - << std::endl; - return TestFail; - } - - if (&elem != &dictObj[key]) { - std::cerr << "Dictionary element " << i << " has wrong address" - << std::endl; - return TestFail; - } - - if (elem.get(-100) != item.second) { - std::cerr << "Dictionary element " << i << " has wrong value" - << std::endl; - return TestFail; - } - - i++; - } - - /* Ensure that empty objects get parsed as empty strings. */ - if (!dictObj["empty"].isValue()) { - cerr << "Empty object is not of type value" << std::endl; - return TestFail; - } - - /* Ensure that keys without values are added to a dict. */ - if (!dictObj.contains("empty")) { - cerr << "Empty element is missing in dict" << std::endl; - return TestFail; - } - - /* Test access to nonexistent member. */ - if (dictObj["nonexistent"].get("default") != "default") { - cerr << "Accessing nonexistent dict entry fails to return default" << std::endl; - return TestFail; - } - - /* Test nonexistent object has value type empty. */ - if (!dictObj["nonexistent"].isEmpty()) { - cerr << "Accessing nonexistent object returns non-empty object" << std::endl; - return TestFail; - } - - /* Test explicit cast to bool on an empty object returns true. */ - if (!!dictObj["empty"] != true) { - cerr << "Casting empty entry to bool returns false" << std::endl; - return TestFail; - } - - /* Test explicit cast to bool on nonexistent object returns false. */ - if (!!dictObj["nonexistent"] != false) { - cerr << "Casting nonexistent dict entry to bool returns true" << std::endl; - return TestFail; - } - - /* Make sure utils::map_keys() works on the adapter. */ - (void)utils::map_keys(dictObj.asDict()); - - /* Test leveled objects */ + /* Test nested nodes. */ auto &level1Obj = (*root)["level1"]; if (!level1Obj.isDictionary()) { @@ -576,7 +207,7 @@ protected: auto &level2Obj = level1Obj["level2"]; if (!level2Obj.isList() || level2Obj.size() != 2) { - cerr << "level2 object should be 2 element list" << std::endl; + cerr << "level2 object should be a 2 elements list" << std::endl; return TestFail; } From patchwork Tue Apr 7 15:34:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26469 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 C1ED7C331D for ; Tue, 7 Apr 2026 15:35:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 052B862E3D; Tue, 7 Apr 2026 17:35:14 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="AIf4hIJn"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 988A462E36 for ; Tue, 7 Apr 2026 17:35:08 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 11D55A30 for ; Tue, 7 Apr 2026 17:33:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576021; bh=E8G5dU8Wcu8VzDRpVpi+VQ97VML0mZ5vya9frb5Xnuc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=AIf4hIJnlMrZsoO8CEirgLVqNubq+fvKfGmQz8qPGCGojD/jU75El6YW489/zCihY S4Ced3Vap23cUtG1/KvW2AT9d4/su3zIrdx9ON2snxaUlkx3vPyLZU6pULfBsp+IpG +qPR1m+iKJo8yGoAX8UqHMkid7rK02RJUuHozsaY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 29/42] test: yaml-parser: Standardize on explicitly qualifying std namespace Date: Tue, 7 Apr 2026 18:34:14 +0300 Message-ID: <20260407153427.1825999-30-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" yaml-parser.cpp mixes unqualified and qualified identifier in the std namespace. Standardize on qualified identifiers, and drop the "using namespace std" statement. Signed-off-by: Laurent Pinchart --- test/yaml-parser.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index 0bbda3af8ea9..e8fd9ad61cd0 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -20,9 +20,8 @@ #include "test.h" using namespace libcamera; -using namespace std; -static const string testYaml = +static const std::string testYaml = "empty:\n" "value: 42\n" "list:\n" @@ -34,13 +33,13 @@ static const string testYaml = " - [1, 2]\n" " - {one: 1, two: 2}\n"; -static const string invalidYaml = +static const std::string invalidYaml = "Invalid : - YAML : - Content"; class YamlParserTest : public Test { protected: - bool createFile(const string &content, string &filename) + bool createFile(const std::string &content, std::string &filename) { filename = "/tmp/libcamera.test.XXXXXX"; int fd = mkstemp(&filename.front()); @@ -72,13 +71,13 @@ protected: /* Test parsing invalid YAML file. */ File file{ invalidYamlFile_ }; if (!file.open(File::OpenModeFlag::ReadOnly)) { - cerr << "Fail to open invalid YAML file" << std::endl; + std::cerr << "Fail to open invalid YAML file" << std::endl; return TestFail; } std::unique_ptr root = YamlParser::parse(file); if (root) { - cerr << "Invalid YAML file parse successfully" << std::endl; + std::cerr << "Invalid YAML file parse successfully" << std::endl; return TestFail; } @@ -86,20 +85,20 @@ protected: file.close(); file.setFileName(testYamlFile_); if (!file.open(File::OpenModeFlag::ReadOnly)) { - cerr << "Fail to open test YAML file" << std::endl; + std::cerr << "Fail to open test YAML file" << std::endl; return TestFail; } root = YamlParser::parse(file); if (!root) { - cerr << "Fail to parse test YAML file: " << std::endl; + std::cerr << "Fail to parse test YAML file: " << std::endl; return TestFail; } /* Test that the root dictionary node has been parsed correctly. */ if (!root->isDictionary()) { - cerr << "Dictionary node has wrong type" << std::endl; + std::cerr << "Dictionary node has wrong type" << std::endl; return TestFail; } @@ -138,7 +137,7 @@ protected: /* Test empty node. */ auto &emptyNode = (*root)["empty"]; - if (emptyNode.get("-") != "") { + if (emptyNode.get("-") != "") { std::cerr << "Empty node has incorrect content" << std::endl; return TestFail; } @@ -146,7 +145,7 @@ protected: /* Test value node. */ auto &valueNode = (*root)["value"]; - if (valueNode.get("") != "42") { + if (valueNode.get("") != "42") { std::cerr << "Value node has incorrect content" << std::endl; return TestFail; } @@ -161,7 +160,7 @@ protected: }; if (listObj.size() != listValues.size()) { - cerr << "List object parse with wrong size" << std::endl; + std::cerr << "List object parsed with wrong size" << std::endl; return TestFail; } @@ -192,7 +191,7 @@ protected: /* Ensure that empty list elements get parsed as empty strings. */ if (!listObj[2].isValue()) { - cerr << "Empty list element is not a value" << std::endl; + std::cerr << "Empty list element is not a value" << std::endl; return TestFail; } @@ -200,14 +199,14 @@ protected: auto &level1Obj = (*root)["level1"]; if (!level1Obj.isDictionary()) { - cerr << "level1 object fail to parse as Dictionary" << std::endl; + std::cerr << "level1 object fail to parse as Dictionary" << std::endl; return TestFail; } auto &level2Obj = level1Obj["level2"]; if (!level2Obj.isList() || level2Obj.size() != 2) { - cerr << "level2 object should be a 2 elements list" << std::endl; + std::cerr << "level2 object should be a 2 elements list" << std::endl; return TestFail; } @@ -216,13 +215,13 @@ protected: firstElement.size() != 2 || firstElement[0].get(0) != 1 || firstElement[1].get(0) != 2) { - cerr << "The first element of level2 object fail to parse as integer list" << std::endl; + std::cerr << "The first element of level2 object fail to parse as integer list" << std::endl; return TestFail; } const auto &values = firstElement.get>(); if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) { - cerr << "get() failed to return correct vector" << std::endl; + std::cerr << "get() failed to return correct vector" << std::endl; return TestFail; } @@ -232,7 +231,7 @@ protected: !secondElement.contains("two") || secondElement["one"].get(0) != 1 || secondElement["two"].get(0) != 2) { - cerr << "The second element of level2 object fail to parse as dictionary" << std::endl; + std::cerr << "The second element of level2 object fail to parse as dictionary" << std::endl; return TestFail; } From patchwork Tue Apr 7 15:34:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26470 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 74A25C32F6 for ; Tue, 7 Apr 2026 15:35:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 11FD462E49; Tue, 7 Apr 2026 17:35:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Kw2yoNGS"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id ED12762E34 for ; Tue, 7 Apr 2026 17:35:09 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 632FA6A6 for ; Tue, 7 Apr 2026 17:33:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576022; bh=iAl7Uhf4ozNMtd1aKDUtx6PDQo9FCvFjccikshDLOhc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Kw2yoNGShZ3T4VrBigHDU5WUA7tFIvFyjrPNDKfXLV8kHUPoRKj29lMmpPp8X6y54 ktshTXO8ER5ZqO3Ginxj6oxP3xZP73Z1PP/drem0LTr5/0WKgwzSuIRQeGqJ5RKIc6 lAgU4ApwKFJ98hsIiLH1uU7TGFZhjIG2/EY3JSs0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 30/42] test: yaml-parser: Replace "object" with "node" Date: Tue, 7 Apr 2026 18:34:15 +0300 Message-ID: <20260407153427.1825999-31-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Now that the storage class for the property tree storing data parsed from YAML has been renamed from YamlObject to ValueNode, rename variables and update error messages accordingly. Signed-off-by: Laurent Pinchart --- test/yaml-parser.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index e8fd9ad61cd0..c8ef51fe18aa 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -151,7 +151,7 @@ protected: } /* Test list node. */ - auto &listObj = (*root)["list"]; + auto &listNode = (*root)["list"]; static constexpr std::array listValues{ "libcamera", @@ -159,13 +159,13 @@ protected: "", }; - if (listObj.size() != listValues.size()) { - std::cerr << "List object parsed with wrong size" << std::endl; + if (listNode.size() != listValues.size()) { + std::cerr << "List node parsed with wrong size" << std::endl; return TestFail; } unsigned int i = 0; - for (auto &elem : listObj.asList()) { + for (auto &elem : listNode.asList()) { if (i >= listValues.size()) { std::cerr << "Too many elements in list during iteration" << std::endl; @@ -174,7 +174,7 @@ protected: std::string value = listValues[i]; - if (&elem != &listObj[i]) { + if (&elem != &listNode[i]) { std::cerr << "List element " << i << " has wrong address" << std::endl; return TestFail; @@ -190,32 +190,32 @@ protected: } /* Ensure that empty list elements get parsed as empty strings. */ - if (!listObj[2].isValue()) { + if (!listNode[2].isValue()) { std::cerr << "Empty list element is not a value" << std::endl; return TestFail; } /* Test nested nodes. */ - auto &level1Obj = (*root)["level1"]; + auto &level1Node = (*root)["level1"]; - if (!level1Obj.isDictionary()) { - std::cerr << "level1 object fail to parse as Dictionary" << std::endl; + if (!level1Node.isDictionary()) { + std::cerr << "level1 node fail to parse as Dictionary" << std::endl; return TestFail; } - auto &level2Obj = level1Obj["level2"]; + auto &level2Node = level1Node["level2"]; - if (!level2Obj.isList() || level2Obj.size() != 2) { - std::cerr << "level2 object should be a 2 elements list" << std::endl; + if (!level2Node.isList() || level2Node.size() != 2) { + std::cerr << "level2 node should be a 2 elements list" << std::endl; return TestFail; } - auto &firstElement = level2Obj[0]; + auto &firstElement = level2Node[0]; if (!firstElement.isList() || firstElement.size() != 2 || firstElement[0].get(0) != 1 || firstElement[1].get(0) != 2) { - std::cerr << "The first element of level2 object fail to parse as integer list" << std::endl; + std::cerr << "The first element of level2 node fail to parse as integer list" << std::endl; return TestFail; } @@ -225,13 +225,13 @@ protected: return TestFail; } - auto &secondElement = level2Obj[1]; + auto &secondElement = level2Node[1]; if (!secondElement.isDictionary() || !secondElement.contains("one") || !secondElement.contains("two") || secondElement["one"].get(0) != 1 || secondElement["two"].get(0) != 2) { - std::cerr << "The second element of level2 object fail to parse as dictionary" << std::endl; + std::cerr << "The second element of level2 node fail to parse as dictionary" << std::endl; return TestFail; } From patchwork Tue Apr 7 15:34:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26471 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 16992C32F7 for ; Tue, 7 Apr 2026 15:35:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90A5762E34; Tue, 7 Apr 2026 17:35:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="B2MGRFd9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7F87062E1A for ; Tue, 7 Apr 2026 17:35:11 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E87D7C58 for ; Tue, 7 Apr 2026 17:33:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576024; bh=3at8PC8gVCz6SvWxMVJ345PHN657ctcrqP5XClwUvfw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=B2MGRFd9UOgB+1+Nu2EPJMk2+2xzXKjJnZN/F8Vr/FWi/xFj26PkJGOYxBQIRC0CB TT8AvgtBMs8Cbo+wcM/gmLU30E28AT57CKmdg7JlOLYul3Y4rTsRhe6/OTGO8hHxvB DlWmXpD+ys4RTWPLe5aCba3LEfoozX+FU7zKIQ38= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 31/42] libcamera: Pass CameraManager around instead of GlobalConfiguration Date: Tue, 7 Apr 2026 18:34:16 +0300 Message-ID: <20260407153427.1825999-32-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 GlobalConfiguration is explicitly passed around through constructors of various objects that need access to the configuration. This ad-hoc solution works for the specific use cases it was meant to support, but isn't very generic. We have a top-level object in libcamera, the CameraManager, that also needs to be accessed from various locations and is passed to object constructors. Standardize on passing the CameraManager everywhere, and access the global configuration through it. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/ipa_manager.h | 10 +++++----- include/libcamera/internal/ipa_proxy.h | 4 ++-- .../libcamera/internal/software_isp/benchmark.h | 6 +++--- .../internal/software_isp/swstats_cpu.h | 6 +++--- src/libcamera/camera_manager.cpp | 3 ++- src/libcamera/ipa_manager.cpp | 7 +++++-- src/libcamera/ipa_proxy.cpp | 16 +++++++++++----- src/libcamera/software_isp/benchmark.cpp | 7 ++++++- src/libcamera/software_isp/debayer.cpp | 14 ++++++-------- src/libcamera/software_isp/debayer.h | 4 ++-- src/libcamera/software_isp/debayer_cpu.cpp | 7 ++++--- src/libcamera/software_isp/debayer_cpu.h | 4 ++-- src/libcamera/software_isp/debayer_egl.cpp | 7 +++---- src/libcamera/software_isp/debayer_egl.h | 4 +++- src/libcamera/software_isp/software_isp.cpp | 9 +++++---- src/libcamera/software_isp/swstats_cpu.cpp | 8 ++++---- test/ipa/ipa_interface_test.cpp | 5 +---- .../module_ipa_proxy.cpp.tmpl | 8 ++++---- .../libcamera_templates/module_ipa_proxy.h.tmpl | 4 ++-- 19 files changed, 73 insertions(+), 60 deletions(-) diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index ecb6ae896e0c..aaa3ca37c493 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -16,13 +16,13 @@ #include #include -#include "libcamera/internal/camera_manager.h" #include "libcamera/internal/pub_key.h" namespace libcamera { LOG_DECLARE_CATEGORY(IPAManager) +class CameraManager; class GlobalConfiguration; class IPAModule; class PipelineHandler; @@ -30,7 +30,7 @@ class PipelineHandler; class IPAManager { public: - IPAManager(const GlobalConfiguration &configuration); + IPAManager(const CameraManager &cm); ~IPAManager(); template @@ -43,9 +43,9 @@ public: auto proxy = [&]() -> std::unique_ptr { if (isSignatureValid(m)) - return std::make_unique(m, configuration_); + return std::make_unique(m, cm_); else - return std::make_unique(m, configuration_); + return std::make_unique(m, cm_); }(); if (!proxy->isValid()) { @@ -73,7 +73,7 @@ private: bool isSignatureValid(IPAModule *ipa) const; - const GlobalConfiguration &configuration_; + const CameraManager &cm_; std::vector> modules_; #if HAVE_IPA_PUBKEY diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h index f1865d67e8d3..723978426950 100644 --- a/include/libcamera/internal/ipa_proxy.h +++ b/include/libcamera/internal/ipa_proxy.h @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/camera_manager.h" namespace libcamera { @@ -28,7 +28,7 @@ public: ProxyRunning, }; - IPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration); + IPAProxy(IPAModule *ipam, const CameraManager &cm); ~IPAProxy(); bool isValid() const { return valid_; } diff --git a/include/libcamera/internal/software_isp/benchmark.h b/include/libcamera/internal/software_isp/benchmark.h index 5526abc56f64..cc7f9d71e7fa 100644 --- a/include/libcamera/internal/software_isp/benchmark.h +++ b/include/libcamera/internal/software_isp/benchmark.h @@ -13,15 +13,15 @@ #include #include #include -#include -#include "libcamera/internal/global_configuration.h" namespace libcamera { +class CameraManager; + class Benchmark { public: - Benchmark(const GlobalConfiguration &configuration, const std::string &name); + Benchmark(const CameraManager &cm, const std::string &name); ~Benchmark(); void startFrame(void); diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index feee92f999d2..802370bdb59c 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -20,7 +20,6 @@ #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/shared_mem_object.h" #include "libcamera/internal/software_isp/swisp_stats.h" @@ -28,14 +27,15 @@ namespace libcamera { -class PixelFormat; +class CameraManager; class MappedFrameBuffer; +class PixelFormat; struct StreamConfiguration; class SwStatsCpu { public: - SwStatsCpu(const GlobalConfiguration &configuration); + SwStatsCpu(const CameraManager &cm); ~SwStatsCpu() = default; /* diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 5762c210ffc2..f774bd84291b 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -93,7 +93,8 @@ void CameraManager::Private::run() int CameraManager::Private::init() { - ipaManager_ = std::make_unique(configuration()); + CameraManager *const o = LIBCAMERA_O_PTR(); + ipaManager_ = std::make_unique(*o); enumerator_ = DeviceEnumerator::create(); if (!enumerator_ || enumerator_->enumerate()) diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 1e905e8b82e8..dd1f483beec3 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -100,13 +100,16 @@ LOG_DEFINE_CATEGORY(IPAManager) /** * \brief Construct an IPAManager instance + * \param[in] cm The camera manager * * The IPAManager class is meant to only be instantiated once, by the * CameraManager. */ -IPAManager::IPAManager(const GlobalConfiguration &configuration) - : configuration_(configuration) +IPAManager::IPAManager(const CameraManager &cm) + : cm_(cm) { + const GlobalConfiguration &configuration = cm._d()->configuration(); + #if HAVE_IPA_PUBKEY if (!pubKey_.isValid()) LOG(IPAManager, Warning) << "Public key not valid"; diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index a3ccfa6035e1..6c8780a012d5 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -117,13 +117,19 @@ std::string ipaConfigurationFile(const std::string &ipaName, const std::string & /** * \brief Construct an IPAProxy instance * \param[in] ipam The IPA module - * \param[in] configuration The global configuration + * \param[in] cm The camera manager */ -IPAProxy::IPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration) - : valid_(false), state_(ProxyStopped), ipam_(ipam), - configPaths_(configuration.envListOption("LIBCAMERA_IPA_CONFIG_PATH", { "ipa", "config_paths" }).value_or(std::vector())), - execPaths_(configuration.envListOption("LIBCAMERA_IPA_PROXY_PATH", { "ipa", "proxy_paths" }).value_or(std::vector())) +IPAProxy::IPAProxy(IPAModule *ipam, const CameraManager &cm) + : valid_(false), state_(ProxyStopped), ipam_(ipam) { + const GlobalConfiguration &configuration = cm._d()->configuration(); + + configPaths_ = configuration.envListOption("LIBCAMERA_IPA_CONFIG_PATH", + { "ipa", "config_paths" }) + .value_or(std::vector()); + execPaths_ = configuration.envListOption("LIBCAMERA_IPA_PROXY_PATH", + { "ipa", "proxy_paths" }) + .value_or(std::vector()); } IPAProxy::~IPAProxy() diff --git a/src/libcamera/software_isp/benchmark.cpp b/src/libcamera/software_isp/benchmark.cpp index 36c49770e1a7..96f0ae004563 100644 --- a/src/libcamera/software_isp/benchmark.cpp +++ b/src/libcamera/software_isp/benchmark.cpp @@ -12,6 +12,9 @@ #include +#include "libcamera/internal/camera_manager.h" +#include "libcamera/internal/global_configuration.h" + namespace libcamera { LOG_DEFINE_CATEGORY(Benchmark) @@ -26,9 +29,11 @@ LOG_DEFINE_CATEGORY(Benchmark) /** * \brief Constructs a Benchmark object */ -Benchmark::Benchmark(const GlobalConfiguration &configuration, const std::string &name) +Benchmark::Benchmark(const CameraManager &cm, const std::string &name) : name_(name) { + const GlobalConfiguration &configuration = cm._d()->configuration(); + skipBeforeMeasure_ = configuration.option( { "software_isp", "measure", "skip" }) .value_or(skipBeforeMeasure_); diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index a6bceb58b787..2d7abfb8325d 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -18,12 +18,6 @@ namespace libcamera { * \brief Struct to hold the debayer parameters. */ -/** - * \fn Debayer::Debayer(const GlobalConfiguration &configuration) - * \brief Construct a Debayer object - * \param[in] configuration Global configuration reference - */ - /** * \var DebayerParams::gains * \brief Colour channel gains @@ -58,8 +52,12 @@ namespace libcamera { LOG_DEFINE_CATEGORY(Debayer) -Debayer::Debayer(const GlobalConfiguration &configuration) - : bench_(configuration, "Debayer") +/** + * \brief Construct a Debayer object + * \param[in] cm The camera manager + */ +Debayer::Debayer(const CameraManager &cm) + : bench_(cm, "Debayer") { } diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h index ea1ec6dcf857..a2a17ec18495 100644 --- a/src/libcamera/software_isp/debayer.h +++ b/src/libcamera/software_isp/debayer.h @@ -22,12 +22,12 @@ #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/dma_buf_allocator.h" -#include "libcamera/internal/global_configuration.h" #include "libcamera/internal/software_isp/benchmark.h" #include "libcamera/internal/software_isp/debayer_params.h" namespace libcamera { +class CameraManager; class FrameBuffer; LOG_DECLARE_CATEGORY(Debayer) @@ -35,7 +35,7 @@ LOG_DECLARE_CATEGORY(Debayer) class Debayer : public Object { public: - Debayer(const GlobalConfiguration &configuration); + Debayer(const CameraManager &cm); virtual ~Debayer() = 0; virtual int configure(const StreamConfiguration &inputCfg, diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp index dd0fff871414..d9f5b32689e7 100644 --- a/src/libcamera/software_isp/debayer_cpu.cpp +++ b/src/libcamera/software_isp/debayer_cpu.cpp @@ -89,10 +89,10 @@ DebayerCpuThread::DebayerCpuThread(DebayerCpu *debayer, unsigned int threadIndex /** * \brief Constructs a DebayerCpu object * \param[in] stats Pointer to the stats object to use - * \param[in] configuration The global configuration + * \param[in] cm The camera manager */ -DebayerCpu::DebayerCpu(std::unique_ptr stats, const GlobalConfiguration &configuration) - : Debayer(configuration), stats_(std::move(stats)) +DebayerCpu::DebayerCpu(std::unique_ptr stats, const CameraManager &cm) + : Debayer(cm), stats_(std::move(stats)) { /* * Reading from uncached buffers may be very slow. @@ -105,6 +105,7 @@ DebayerCpu::DebayerCpu(std::unique_ptr stats, const GlobalConfigurat * \todo Make memcpy automatic based on runtime detection of platform * capabilities. */ + const GlobalConfiguration &configuration = cm._d()->configuration(); bool enableInputMemcpy = configuration.option({ "software_isp", "copy_input_buffer" }).value_or(true); diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h index 05fecc8feb56..4f7a924b0abd 100644 --- a/src/libcamera/software_isp/debayer_cpu.h +++ b/src/libcamera/software_isp/debayer_cpu.h @@ -19,7 +19,7 @@ #include #include "libcamera/internal/bayer_format.h" -#include "libcamera/internal/global_configuration.h" +#include "libcamera/internal/camera_manager.h" #include "libcamera/internal/software_isp/debayer_params.h" #include "libcamera/internal/software_isp/swstats_cpu.h" @@ -31,7 +31,7 @@ class DebayerCpuThread; class DebayerCpu : public Debayer { public: - DebayerCpu(std::unique_ptr stats, const GlobalConfiguration &configuration); + DebayerCpu(std::unique_ptr stats, const CameraManager &cm); ~DebayerCpu(); int configure(const StreamConfiguration &inputCfg, diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 2ad258bca69f..8f0c229fd823 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -29,13 +29,12 @@ namespace libcamera { */ /** - * \fn DebayerEGL::DebayerEGL(std::unique_ptr stats, const GlobalConfiguration &configuration) * \brief Construct a DebayerEGL object * \param[in] stats Statistics processing object - * \param[in] configuration Global configuration reference + * \param[in] cm The camera manager */ -DebayerEGL::DebayerEGL(std::unique_ptr stats, const GlobalConfiguration &configuration) - : Debayer(configuration), stats_(std::move(stats)) +DebayerEGL::DebayerEGL(std::unique_ptr stats, const CameraManager &cm) + : Debayer(cm), stats_(std::move(stats)) { } diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h index bdde676f2394..5b6c440f5d27 100644 --- a/src/libcamera/software_isp/debayer_egl.h +++ b/src/libcamera/software_isp/debayer_egl.h @@ -35,10 +35,12 @@ namespace libcamera { #define DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS 4 #define DEBAYER_OPENGL_COORDS 4 +class CameraManager; + class DebayerEGL : public Debayer { public: - DebayerEGL(std::unique_ptr stats, const GlobalConfiguration &configuration); + DebayerEGL(std::unique_ptr stats, const CameraManager &cm); ~DebayerEGL(); int configure(const StreamConfiguration &inputCfg, diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 5b225c7cff93..cd0e9d06a1e1 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -95,9 +95,9 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, return; } - const GlobalConfiguration &configuration = pipe->cameraManager()->_d()->configuration(); + const CameraManager &cm = *pipe->cameraManager(); - auto stats = std::make_unique(configuration); + auto stats = std::make_unique(cm); if (!stats->isValid()) { LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object"; return; @@ -105,6 +105,7 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, stats->statsReady.connect(this, &SoftwareIsp::statsReady); #if HAVE_DEBAYER_EGL + const GlobalConfiguration &configuration = cm._d()->configuration(); std::optional softISPMode = configuration.envOption("LIBCAMERA_SOFTISP_MODE", { "software_isp", "mode" }); if (softISPMode) { if (softISPMode != "gpu" && softISPMode != "cpu") { @@ -117,11 +118,11 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, } if (!softISPMode || softISPMode == "gpu") - debayer_ = std::make_unique(std::move(stats), configuration); + debayer_ = std::make_unique(std::move(stats), cm); #endif if (!debayer_) - debayer_ = std::make_unique(std::move(stats), configuration); + debayer_ = std::make_unique(std::move(stats), cm); debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady); debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady); diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 3595c9e690f1..5366e019fe3f 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -36,9 +36,9 @@ namespace libcamera { */ /** - * \fn SwStatsCpu::SwStatsCpu(const GlobalConfiguration &configuration) + * \fn SwStatsCpu::SwStatsCpu(const CameraManager &cm) * \brief Construct a SwStatsCpu object - * \param[in] configuration Global configuration reference + * \param[in] cm The camera manager * * Creates a SwStatsCpu object and initialises shared memory for statistics * exchange. @@ -159,8 +159,8 @@ namespace libcamera { LOG_DEFINE_CATEGORY(SwStatsCpu) -SwStatsCpu::SwStatsCpu(const GlobalConfiguration &configuration) - : sharedStats_("softIsp_stats"), bench_(configuration, "CPU stats") +SwStatsCpu::SwStatsCpu(const CameraManager &cm) + : sharedStats_("softIsp_stats"), bench_(cm, "CPU stats") { if (!sharedStats_) LOG(SwStatsCpu, Error) diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index c1fe2267cc6e..271c4e2c9dc2 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -47,7 +47,6 @@ public: notifier_.reset(); ipa_.reset(); ipaManager_.reset(); - config_.reset(); cameraManager_.reset(); } @@ -90,8 +89,7 @@ protected: notifier_->activated.connect(this, &IPAInterfaceTest::readTrace); /* Create the IPA manager. */ - config_ = std::make_unique(); - ipaManager_ = std::make_unique(*config_); + ipaManager_ = std::make_unique(*cameraManager_); return TestPass; } @@ -169,7 +167,6 @@ private: std::shared_ptr pipe_; std::unique_ptr ipa_; std::unique_ptr cameraManager_; - std::unique_ptr config_; std::unique_ptr ipaManager_; enum ipa::vimc::IPAOperationCode trace_; std::unique_ptr notifier_; diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl index e6e19b3030b9..0d0a16147edd 100644 --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl @@ -45,8 +45,8 @@ namespace {{ns}} { {% endfor %} {%- endif %} -{{proxy_name}}Threaded::{{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration) - : {{proxy_name}}(ipam, configuration), thread_("{{proxy_name}}") +{{proxy_name}}Threaded::{{proxy_name}}Threaded(IPAModule *ipam, const CameraManager &cm) + : {{proxy_name}}(ipam, cm), thread_("{{proxy_name}}") { LOG(IPAProxy, Debug) << "initializing {{module_name}} proxy in thread: loading IPA from " @@ -127,8 +127,8 @@ namespace {{ns}} { /* ========================================================================== */ -{{proxy_name}}Isolated::{{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration) - : {{proxy_name}}(ipam, configuration), +{{proxy_name}}Isolated::{{proxy_name}}Isolated(IPAModule *ipam, const CameraManager &cm) + : {{proxy_name}}(ipam, cm), controlSerializer_(ControlSerializer::Role::Proxy), seq_(0) { LOG(IPAProxy, Debug) diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl index ef280ca423f6..d48b90dcfa41 100644 --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl @@ -50,7 +50,7 @@ protected: class {{proxy_name}}Threaded : public {{proxy_name}} { public: - {{proxy_name}}Threaded(IPAModule *ipam, const GlobalConfiguration &configuration); + {{proxy_name}}Threaded(IPAModule *ipam, const CameraManager &cm); ~{{proxy_name}}Threaded(); {% for method in interface_main.methods %} @@ -112,7 +112,7 @@ private: class {{proxy_name}}Isolated : public {{proxy_name}} { public: - {{proxy_name}}Isolated(IPAModule *ipam, const GlobalConfiguration &configuration); + {{proxy_name}}Isolated(IPAModule *ipam, const CameraManager &cm); ~{{proxy_name}}Isolated(); {% for method in interface_main.methods %} From patchwork Tue Apr 7 15:34:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26472 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 E2190C3323 for ; Tue, 7 Apr 2026 15:35:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 644F862E3A; Tue, 7 Apr 2026 17:35:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="MrbxktGT"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D95E762E12 for ; Tue, 7 Apr 2026 17:35:12 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 42893DA4 for ; Tue, 7 Apr 2026 17:33:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576025; bh=lXFT8sbMKANHO79mqPYgg46EfdY7jAa3FvD524sI4Qo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=MrbxktGTHMLKjqVN6w03anK4GRkdpxeTzKlzksfHY2uAKN8xrakLbrgNnHhLPLWCa PzKwTUClaxcwL0lQhN+CJVuGQmpFsi+mckQf8ALxygJ/PjpBA2BaN32kGVcJJquaDL IEP1tKAjTUuLgWVaMsUaeARxowatSSxTNsx2iRlo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 32/42] libcamera: global_configuration: Reorder functions Date: Tue, 7 Apr 2026 18:34:17 +0300 Message-ID: <20260407153427.1825999-33-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Member functions should be implemented in the .cpp file in the same order as in the class definition, within each access group. Move them to the right location. While at it, move load() before loadFile() in the class definition to match execution order, making the code easier to read. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- .../libcamera/internal/global_configuration.h | 2 +- src/libcamera/global_configuration.cpp | 144 +++++++++--------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 7ae923977aa6..84bdf90244d9 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -52,8 +52,8 @@ public: const std::string delimiter = ":") const; private: - bool loadFile(const std::filesystem::path &fileName); void load(); + bool loadFile(const std::filesystem::path &fileName); std::unique_ptr yamlConfiguration_ = std::make_unique(); diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index c4999d32d7c7..c853a028c91d 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -53,6 +53,48 @@ LOG_DEFINE_CATEGORY(Configuration) * options, or configuration() to access the whole configuration. */ +/** + * \typedef GlobalConfiguration::Configuration + * \brief Type representing global libcamera configuration + * + * All code outside GlobalConfiguration must use this type declaration and not + * the underlying type. + */ + +/** + * \brief Initialize the global configuration + */ +GlobalConfiguration::GlobalConfiguration() +{ + load(); +} + +void GlobalConfiguration::load() +{ + std::filesystem::path userConfigurationDirectory; + const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME"); + if (xdgConfigHome) { + userConfigurationDirectory = xdgConfigHome; + } else { + const char *home = utils::secure_getenv("HOME"); + if (home) + userConfigurationDirectory = + std::filesystem::path(home) / ".config"; + } + + if (!userConfigurationDirectory.empty()) { + std::filesystem::path user_configuration_file = + userConfigurationDirectory / "libcamera" / "configuration.yaml"; + if (loadFile(user_configuration_file)) + return; + } + + for (const auto &path : globalConfigurationFiles) { + if (loadFile(path)) + return; + } +} + bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) { File file(fileName); @@ -85,47 +127,39 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) return true; } -void GlobalConfiguration::load() -{ - std::filesystem::path userConfigurationDirectory; - const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME"); - if (xdgConfigHome) { - userConfigurationDirectory = xdgConfigHome; - } else { - const char *home = utils::secure_getenv("HOME"); - if (home) - userConfigurationDirectory = - std::filesystem::path(home) / ".config"; - } - - if (!userConfigurationDirectory.empty()) { - std::filesystem::path user_configuration_file = - userConfigurationDirectory / "libcamera" / "configuration.yaml"; - if (loadFile(user_configuration_file)) - return; - } - - for (const auto &path : globalConfigurationFiles) { - if (loadFile(path)) - return; - } -} - /** - * \brief Initialize the global configuration - */ -GlobalConfiguration::GlobalConfiguration() -{ - load(); -} - -/** - * \typedef GlobalConfiguration::Configuration - * \brief Type representing global libcamera configuration + * \brief Retrieve the configuration version * - * All code outside GlobalConfiguration must use this type declaration and not - * the underlying type. + * The version is declared in the configuration file in the top-level `%version` + * element, alongside `%configuration`. This has currently no real use but may be + * needed in future if configuration incompatibilities occur. + * + * \return Configuration version as declared in the configuration file or 0 if + * no global configuration is available */ +unsigned int GlobalConfiguration::version() const +{ + return (*yamlConfiguration_)["version"].get().value_or(0); +} + +/** + * \brief Retrieve the libcamera global configuration + * + * This returns the whole configuration stored in the top-level section + * `%configuration` of the YAML configuration file. + * + * The requested part of the configuration can be accessed using \a ValueNode + * methods. + * + * \note \a ValueNode type itself shouldn't be used in type declarations to + * avoid trouble if we decide to change the underlying data objects in future. + * + * \return The whole configuration section + */ +GlobalConfiguration::Configuration GlobalConfiguration::configuration() const +{ + return (*yamlConfiguration_)["configuration"]; +} /** * \fn std::optional GlobalConfiguration::option(const std::initializer_list &confPath) const @@ -216,38 +250,4 @@ std::optional> GlobalConfiguration::envListOption( return listOption(confPath); } -/** - * \brief Retrieve the configuration version - * - * The version is declared in the configuration file in the top-level `%version` - * element, alongside `%configuration`. This has currently no real use but may be - * needed in future if configuration incompatibilities occur. - * - * \return Configuration version as declared in the configuration file or 0 if - * no global configuration is available - */ -unsigned int GlobalConfiguration::version() const -{ - return (*yamlConfiguration_)["version"].get().value_or(0); -} - -/** - * \brief Retrieve the libcamera global configuration - * - * This returns the whole configuration stored in the top-level section - * `%configuration` of the YAML configuration file. - * - * The requested part of the configuration can be accessed using \a ValueNode - * methods. - * - * \note \a ValueNode type itself shouldn't be used in type declarations to - * avoid trouble if we decide to change the underlying data objects in future. - * - * \return The whole configuration section - */ -GlobalConfiguration::Configuration GlobalConfiguration::configuration() const -{ - return (*yamlConfiguration_)["configuration"]; -} - } /* namespace libcamera */ From patchwork Tue Apr 7 15:34:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26473 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 DA34EC32BB for ; Tue, 7 Apr 2026 15:35:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4554462E47; Tue, 7 Apr 2026 17:35:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Jj2xZPc9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2160262E3E for ; Tue, 7 Apr 2026 17:35:14 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 8CA3798A for ; Tue, 7 Apr 2026 17:33:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576026; bh=qxKzXGT95FCT6ihpiiKUA04SklOcwwbHdZyr4O5W5vM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Jj2xZPc9kUgJP/jxAWVhfaH2GDj1Z5GTZYUWrwCWcqobbOmpBNqN4kXPeBV773Ke+ MNRIfs4uo2/13/t2x/NnOdd8+5KDKJYi75f2c+2H+2GiavI1boN5ZdxXOVjomkpd1g Fj7FFNUJ4w2PHs7eqF6+qOBbHizSFbJyRKg7ngwY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 33/42] libcamera: global_configuration: Add missing include and comment Date: Tue, 7 Apr 2026 18:34:18 +0300 Message-ID: <20260407153427.1825999-34-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" global_configuration.h uses std::initializer_list, include the corresponding header. Add a comment in global_configuration.cpp to close an anonymous namespace, as mandated by the coding style. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/global_configuration.h | 1 + src/libcamera/global_configuration.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 84bdf90244d9..80b57b38ba03 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index c853a028c91d..44ed206de78f 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -24,11 +24,13 @@ namespace libcamera { namespace { + const std::vector globalConfigurationFiles = { std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml", std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml", }; -} + +} /* namespace */ LOG_DEFINE_CATEGORY(Configuration) From patchwork Tue Apr 7 15:34:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26474 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 A1B4AC3330 for ; Tue, 7 Apr 2026 15:35:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4137862E41; Tue, 7 Apr 2026 17:35:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="gpVI4Bnc"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 787EC62E12 for ; Tue, 7 Apr 2026 17:35:15 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id D0B4A98A for ; Tue, 7 Apr 2026 17:33:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576028; bh=d4KZd/nQT5ofaCmwJv0FFCNdjLX/sttzLaQ3mvuWbDg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=gpVI4Bnc3P5f6jpDykiTCJy4c0pskHhh4SzmfFSKUUtTuq01TNB5sthIw3xfcbgkg lmFYl+M+FrHdf89Jmj7qZlC5a/VDzbabRAso1ccai6ET80OWKAON5o8tNGWHxYxylD ZtitViL+5CMGkK7pPhh9+kxLxXESVv95OWqGSgCw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 34/42] libcamera: global_configuration: Rename yamlConfiguration_ Date: Tue, 7 Apr 2026 18:34:19 +0300 Message-ID: <20260407153427.1825999-35-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" Once parsed, the global configuration isn't a YAML file anymore. Rename the yamlConfiguration_ to configuration_. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/global_configuration.h | 2 +- src/libcamera/global_configuration.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 80b57b38ba03..5c907ee92bfe 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -56,7 +56,7 @@ private: void load(); bool loadFile(const std::filesystem::path &fileName); - std::unique_ptr yamlConfiguration_ = + std::unique_ptr configuration_ = std::make_unique(); }; diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index 44ed206de78f..ee7d9c185b80 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -125,7 +125,7 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) return true; } - yamlConfiguration_ = std::move(configuration); + configuration_ = std::move(configuration); return true; } @@ -141,7 +141,7 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) */ unsigned int GlobalConfiguration::version() const { - return (*yamlConfiguration_)["version"].get().value_or(0); + return (*configuration_)["version"].get().value_or(0); } /** @@ -160,7 +160,7 @@ unsigned int GlobalConfiguration::version() const */ GlobalConfiguration::Configuration GlobalConfiguration::configuration() const { - return (*yamlConfiguration_)["configuration"]; + return (*configuration_)["configuration"]; } /** From patchwork Tue Apr 7 15:34:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26475 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 BBA50C3332 for ; Tue, 7 Apr 2026 15:35:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 381E262E4D; Tue, 7 Apr 2026 17:35:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FX9l3Knw"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E513C62E47 for ; Tue, 7 Apr 2026 17:35:16 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 52FD7596 for ; Tue, 7 Apr 2026 17:33:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576029; bh=3SSGp8PFIxLFOhWlmAlNBj1WQXbI9717NlG/x15IoSU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=FX9l3KnwC+39yY4cRs0HbEZU8VvIEpyDdNXc8UN6J/6QKkX/GTGrKLv3bj8nCPjxu TOyiIvUp8NI3XhNX/i1HvbMn+jrNaRUtvH5h5XsMlpVaNKzM57EdTTwagc2IB3I0Xl NmxZkRCHkZxOpqTwqc9LlGSJy2ATpnZXJtHPcmnc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 35/42] libcamera: global_configuration: Rename Configuration to Option Date: Tue, 7 Apr 2026 18:34:20 +0300 Message-ID: <20260407153427.1825999-36-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 GlobalConfiguration::Configuration type represent a configuration option. Rename it to Option to make this clearer. This shortens lines as an added bonus. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- include/libcamera/internal/global_configuration.h | 4 ++-- src/libcamera/global_configuration.cpp | 8 ++++---- src/libcamera/pipeline/simple/simple.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 5c907ee92bfe..2c0bfadb4676 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -22,12 +22,12 @@ namespace libcamera { class GlobalConfiguration { public: - using Configuration = const ValueNode &; + using Option = const ValueNode &; GlobalConfiguration(); unsigned int version() const; - Configuration configuration() const; + Option configuration() const; template std::optional option( diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index ee7d9c185b80..4d154c026e44 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -56,8 +56,8 @@ LOG_DEFINE_CATEGORY(Configuration) */ /** - * \typedef GlobalConfiguration::Configuration - * \brief Type representing global libcamera configuration + * \typedef GlobalConfiguration::Option + * \brief Type representing a configuration option * * All code outside GlobalConfiguration must use this type declaration and not * the underlying type. @@ -156,9 +156,9 @@ unsigned int GlobalConfiguration::version() const * \note \a ValueNode type itself shouldn't be used in type declarations to * avoid trouble if we decide to change the underlying data objects in future. * - * \return The whole configuration section + * \return The top-level configuration option */ -GlobalConfiguration::Configuration GlobalConfiguration::configuration() const +GlobalConfiguration::Option GlobalConfiguration::configuration() const { return (*configuration_)["configuration"]; } diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 812ff79695d1..0aa5b1c5255a 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -1880,7 +1880,7 @@ bool SimplePipelineHandler::matchDevice(std::shared_ptr media, swIspEnabled_ = info.swIspEnabled; const GlobalConfiguration &configuration = cameraManager()->_d()->configuration(); - for (GlobalConfiguration::Configuration entry : + for (GlobalConfiguration::Option entry : configuration.configuration()["pipelines"]["simple"]["supported_devices"] .asList()) { auto name = entry["driver"].get(); From patchwork Tue Apr 7 15:34:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26476 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 BCEA9C32F8 for ; Tue, 7 Apr 2026 15:35:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3B3FF62E59; Tue, 7 Apr 2026 17:35:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="gySEpswz"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 48A2A62E4B for ; Tue, 7 Apr 2026 17:35:18 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 9E4F6596 for ; Tue, 7 Apr 2026 17:33:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576030; bh=e/qv440tTvCbF/J/ZNHuJhJUaMF+UoqbLP8keLi6D9I=; h=From:To:Subject:Date:In-Reply-To:References:From; b=gySEpswzUJz9z+8EYBxTfyCFM1U/FzYsA7l3ROtu7IDSP8raVsq0PeC5jgEHsAmLG B9BahZgYA8diZrQnzK/YvvUbkTe3aJFZ91UXyk5HgkctxSsk1G2RGSlQYrrbkHmWL/ 02uPSSo4B4byER8VglNwhTq2gMsNywb6/H/Jp+J8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 36/42] libcamera: global_configuration: Drop Option type Date: Tue, 7 Apr 2026 18:34:21 +0300 Message-ID: <20260407153427.1825999-37-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 GlobalConfiguration::Option type was introduced to decouple the configuration option storage from its users, and make changes to the underlying implementation easier. While the type could indeed be changed to map to a different class, the API would need to remain the same. The Option type therefore brings little value. Drop it. Signed-off-by: Laurent Pinchart --- include/libcamera/internal/global_configuration.h | 4 +--- src/libcamera/global_configuration.cpp | 10 +--------- src/libcamera/pipeline/simple/simple.cpp | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 2c0bfadb4676..5eb646e33fc2 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -22,12 +22,10 @@ namespace libcamera { class GlobalConfiguration { public: - using Option = const ValueNode &; - GlobalConfiguration(); unsigned int version() const; - Option configuration() const; + const ValueNode &configuration() const; template std::optional option( diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index 4d154c026e44..cd9cc25e43fd 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -55,14 +55,6 @@ LOG_DEFINE_CATEGORY(Configuration) * options, or configuration() to access the whole configuration. */ -/** - * \typedef GlobalConfiguration::Option - * \brief Type representing a configuration option - * - * All code outside GlobalConfiguration must use this type declaration and not - * the underlying type. - */ - /** * \brief Initialize the global configuration */ @@ -158,7 +150,7 @@ unsigned int GlobalConfiguration::version() const * * \return The top-level configuration option */ -GlobalConfiguration::Option GlobalConfiguration::configuration() const +const ValueNode &GlobalConfiguration::configuration() const { return (*configuration_)["configuration"]; } diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 0aa5b1c5255a..c6fe12d65b18 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -1880,7 +1880,7 @@ bool SimplePipelineHandler::matchDevice(std::shared_ptr media, swIspEnabled_ = info.swIspEnabled; const GlobalConfiguration &configuration = cameraManager()->_d()->configuration(); - for (GlobalConfiguration::Option entry : + for (const ValueNode &entry : configuration.configuration()["pipelines"]["simple"]["supported_devices"] .asList()) { auto name = entry["driver"].get(); From patchwork Tue Apr 7 15:34:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26477 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 7BBFAC3333 for ; Tue, 7 Apr 2026 15:35:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6630362E4E; Tue, 7 Apr 2026 17:35:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rRf0lTMd"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9C5A862E45 for ; Tue, 7 Apr 2026 17:35:19 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 0F9AA596 for ; Tue, 7 Apr 2026 17:33:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576032; bh=eyyjC0HOfDmuFtWQQk39lbuP6N3eQG1/g+xSi+YPFJQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=rRf0lTMd21fy55USPtjKjPnB3UclmkDG9i8zpzkcpCj8GYA6cyDrD7zDwf32OEkOu mz1RiT5Pe8DkvWmH6oDKp3mp0AvHjjKvl8SMyHYHYgxIN/Fl/BYE/QZtvOboH6X+u+ PlqbPZIe6rc/wAUVP2DfnNlAX8Dk4cCmMN1kTHyY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 37/42] libcamera: global_configuration: Populate empty configuration Date: Tue, 7 Apr 2026 18:34:22 +0300 Message-ID: <20260407153427.1825999-38-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" If no configuration file can be parsed, populate an empty configuration. This will serve as a base to store the configuration options set through environment variables. Signed-off-by: Laurent Pinchart --- Changes since v1: - Move code to constructor to cover all loadFile() calls --- src/libcamera/global_configuration.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index cd9cc25e43fd..c87f56c2d822 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -61,6 +61,11 @@ LOG_DEFINE_CATEGORY(Configuration) GlobalConfiguration::GlobalConfiguration() { load(); + + if (configuration_->isEmpty()) { + configuration_->add("version", std::make_unique(1)); + configuration_->add("configuration", std::make_unique()); + } } void GlobalConfiguration::load() From patchwork Tue Apr 7 15:34:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26478 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 DBEF3C3334 for ; Tue, 7 Apr 2026 15:35:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 593A162E47; Tue, 7 Apr 2026 17:35:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DLrxzv+T"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EF6B062E14 for ; Tue, 7 Apr 2026 17:35:20 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 4FAB398A for ; Tue, 7 Apr 2026 17:33:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576033; bh=MnIPVoWWP9g5lEzczNBie5J9pYbFbgwC7JLmpCwbYQw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=DLrxzv+T8w9MPYIcJlBpn9K4GYYb+LHtPP4wEH+BklMbm7uNJFvmE/1SGgOR1cG79 TPr6tRChnDFukOHniLkmVKDQvyHxmVDoWhM4UtE9d2o83nb78plPcAI1r5Z/Xt0ayM cVhJfsh0UAmj6AoknT0OOqbhGthJfV5xSGEdjCI4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 38/42] libcamera: global_configuration: Override options with environment variables Date: Tue, 7 Apr 2026 18:34:23 +0300 Message-ID: <20260407153427.1825999-39-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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" libcamera supports several environment variables that can override configuration options. This requires components that use such overrides to call the special envOption() and envListOption() functions, spreading knowledge of the overrides through the code base. This will hinder future enhancements to the global configuration, such as implementing per-camera options that will override pipeline handler-level options. To prepare for that, move handling of the environment variables to the GlobalConfiguration class. Signed-off-by: Laurent Pinchart --- Changes since v1: - Avoid copy in range-based loop - Drop unneeded constructor - Erase existing node before overriding - Move override code to constructor --- .../libcamera/internal/global_configuration.h | 7 - src/libcamera/camera_manager.cpp | 4 +- src/libcamera/global_configuration.cpp | 180 +++++++++++------- src/libcamera/ipa_manager.cpp | 9 +- src/libcamera/ipa_proxy.cpp | 6 +- src/libcamera/software_isp/software_isp.cpp | 4 +- 6 files changed, 124 insertions(+), 86 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 5eb646e33fc2..0d2ccb8a1808 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -42,13 +42,6 @@ public: std::optional> listOption( const std::initializer_list confPath) const; - std::optional envOption( - const char *const envVariable, - const std::initializer_list confPath) const; - std::optional> envListOption( - const char *const envVariable, - const std::initializer_list confPath, - const std::string delimiter = ":") const; private: void load(); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index f774bd84291b..0dd4e0c590a1 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -114,9 +114,7 @@ void CameraManager::Private::createPipelineHandlers() * there is no configuration file. */ const auto pipesList = - configuration().envListOption("LIBCAMERA_PIPELINES_MATCH_LIST", - { "pipelines_match_list" }, - ","); + configuration().listOption({ "pipelines_match_list" }); if (pipesList.has_value()) { /* * When a list of preferred pipelines is defined, iterate diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index c87f56c2d822..75c07949e1ad 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -7,6 +7,7 @@ #include "libcamera/internal/global_configuration.h" +#include #include #include #include @@ -30,6 +31,99 @@ const std::vector globalConfigurationFiles = { std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml", }; +class EnvironmentProcessor +{ +public: + virtual ~EnvironmentProcessor() = default; + + virtual void process(ValueNode &node, const char *env) = 0; +}; + +/* A processor that sets a fixed value. */ +template +class EnvironmentFixedProcessor : public EnvironmentProcessor +{ +public: + EnvironmentFixedProcessor(const T &value) + : value_(value) + { + } + + void process(ValueNode &node, [[maybe_unused]] const char *env) override + { + node.set(value_); + } + +private: + T value_; +}; + +/* + * A processor that parses the environment variable as a list of strings with a + * custom delimiter. + */ +class EnvironmentListProcessor : public EnvironmentProcessor +{ +public: + EnvironmentListProcessor(const char *delimiter) + : delimiter_(delimiter) + { + } + + void process(ValueNode &node, const char *env) override + { + for (auto &&value : utils::split(env, delimiter_)) + node.add(std::make_unique(std::move(value))); + } + +private: + const std::string delimiter_; +}; + +/* A processor that copies the value of the environment variable. */ +class EnvironmentValueProcessor : public EnvironmentProcessor +{ +public: + void process(ValueNode &node, const char *env) override + { + node.set(std::string{ env }); + } +}; + +struct EnvironmentOverride { + const char *variable; + std::initializer_list path; + std::unique_ptr processor; +}; + +const std::array environmentOverrides{ { + { + "LIBCAMERA_IPA_CONFIG_PATH", + { "ipa", "config_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_IPA_FORCE_ISOLATION", + { "ipa", "force_isolation" }, + std::make_unique>(true), + }, { + "LIBCAMERA_IPA_MODULE_PATH", + { "ipa", "module_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_IPA_PROXY_PATH", + { "ipa", "proxy_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_PIPELINES_MATCH_LIST", + { "pipelines_match_list" }, + std::make_unique(","), + }, { + "LIBCAMERA_SOFTISP_MODE", + { "software_isp", "mode" }, + std::make_unique(), + }, +} }; + } /* namespace */ LOG_DEFINE_CATEGORY(Configuration) @@ -50,9 +144,9 @@ LOG_DEFINE_CATEGORY(Configuration) * reported and no configuration file is used. This is to prevent libcamera from * using an unintended configuration file. * - * The configuration can be accessed using the provided helpers, namely - * option(), envOption(), listOption() and envListOption() to access individual - * options, or configuration() to access the whole configuration. + * The configuration can be accessed using the provided helpers, namely option() + * and listOption() to access individual options, or configuration() to access + * the whole configuration. */ /** @@ -66,6 +160,25 @@ GlobalConfiguration::GlobalConfiguration() configuration_->add("version", std::make_unique(1)); configuration_->add("configuration", std::make_unique()); } + + /* Process environment variables that override configuration options. */ + ValueNode *cfg = configuration_->at("configuration"); + + for (const EnvironmentOverride &envOverride : environmentOverrides) { + const char *envValue = utils::secure_getenv(envOverride.variable); + if (!envValue || !envValue[0]) + continue; + + std::unique_ptr node = std::make_unique(); + envOverride.processor->process(*node.get(), envValue); + + cfg->erase(envOverride.path); + + if (!cfg->add(envOverride.path, std::move(node))) + LOG(Configuration, Error) + << "Failed to override " + << utils::join(envOverride.path, "/"); + } } void GlobalConfiguration::load() @@ -188,65 +301,4 @@ std::optional> GlobalConfiguration::listOption( return c->get>(); } -/** - * \brief Retrieve the value of environment variable with a fallback on the configuration file - * \param[in] envVariable Environment variable to get the value from - * \param[in] confPath The sequence of YAML section names to fall back on when - * \a envVariable is unavailable - * - * This helper looks first at the given environment variable and if it is - * defined then it returns its value (even if it is empty). Otherwise it looks - * for \a confPath the same way as in GlobalConfiguration::option. Only string - * values are supported. - * - * \note Support for using environment variables to configure libcamera behavior - * is provided here mostly for backward compatibility reasons. Introducing new - * configuration environment variables is discouraged. - * - * \return The value retrieved from the given environment if it is set, - * otherwise the value from the configuration file if it exists, or no value if - * it does not - */ -std::optional GlobalConfiguration::envOption( - const char *envVariable, - const std::initializer_list confPath) const -{ - const char *envValue = utils::secure_getenv(envVariable); - if (envValue) - return std::optional{ std::string{ envValue } }; - return option(confPath); -} - -/** - * \brief Retrieve the value of the configuration option from a file or environment - * \param[in] envVariable Environment variable to get the value from - * \param[in] confPath The same as in GlobalConfiguration::option - * \param[in] delimiter Items separator in the environment variable - * - * This helper looks first at the given environment variable and if it is - * defined (even if it is empty) then it splits its value by semicolons and - * returns the resulting list of strings. Otherwise it looks for \a confPath the - * same way as in GlobalConfiguration::option, value of which must be a list of - * strings. - * - * \note Support for using environment variables to configure libcamera behavior - * is provided here mostly for backward compatibility reasons. Introducing new - * configuration environment variables is discouraged. - * - * \return A vector of strings retrieved from the given environment option or - * configuration file or no value if not found; the vector may be empty - */ -std::optional> GlobalConfiguration::envListOption( - const char *const envVariable, - const std::initializer_list confPath, - const std::string delimiter) const -{ - const char *envValue = utils::secure_getenv(envVariable); - if (envValue) { - auto items = utils::split(envValue, delimiter); - return std::vector(items.begin(), items.end()); - } - return listOption(confPath); -} - } /* namespace libcamera */ diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index dd1f483beec3..a351f4f7b581 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -114,18 +114,15 @@ IPAManager::IPAManager(const CameraManager &cm) if (!pubKey_.isValid()) LOG(IPAManager, Warning) << "Public key not valid"; - char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); - forceIsolation_ = (force && force[0] != '\0') || - (!force && configuration.option({ "ipa", "force_isolation" }) - .value_or(false)); + forceIsolation_ = configuration.option({ "ipa", "force_isolation" }) + .value_or(false); #endif unsigned int ipaCount = 0; /* User-specified paths take precedence. */ const auto modulePaths = - configuration.envListOption( - "LIBCAMERA_IPA_MODULE_PATH", { "ipa", "module_paths" }) + configuration.listOption({ "ipa", "module_paths" }) .value_or(std::vector()); for (const auto &dir : modulePaths) { if (dir.empty()) diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index 6c8780a012d5..bc8ff090fa86 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -124,11 +124,9 @@ IPAProxy::IPAProxy(IPAModule *ipam, const CameraManager &cm) { const GlobalConfiguration &configuration = cm._d()->configuration(); - configPaths_ = configuration.envListOption("LIBCAMERA_IPA_CONFIG_PATH", - { "ipa", "config_paths" }) + configPaths_ = configuration.listOption({ "ipa", "config_paths" }) .value_or(std::vector()); - execPaths_ = configuration.envListOption("LIBCAMERA_IPA_PROXY_PATH", - { "ipa", "proxy_paths" }) + execPaths_ = configuration.listOption({ "ipa", "proxy_paths" }) .value_or(std::vector()); } diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index cd0e9d06a1e1..d227bd8e325f 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -106,11 +106,11 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, #if HAVE_DEBAYER_EGL const GlobalConfiguration &configuration = cm._d()->configuration(); - std::optional softISPMode = configuration.envOption("LIBCAMERA_SOFTISP_MODE", { "software_isp", "mode" }); + std::optional softISPMode = configuration.option({ "software_isp", "mode" }); if (softISPMode) { if (softISPMode != "gpu" && softISPMode != "cpu") { LOG(SoftwareIsp, Error) - << "Invalid LIBCAMERA_SOFTISP_MODE \"" + << "Invalid software ISP mode \"" << softISPMode.value() << "\", must be \"cpu\" or \"gpu\""; return; From patchwork Tue Apr 7 15:34:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26479 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 4784FC3336 for ; Tue, 7 Apr 2026 15:35:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 85E8862E60; Tue, 7 Apr 2026 17:35:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hfDtJpMl"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 486A562E4E for ; Tue, 7 Apr 2026 17:35:22 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B8FEFA30 for ; Tue, 7 Apr 2026 17:33:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576034; bh=Lvb3KXE/lpfnUutVj2gy5wzANdSj3CHJbjQl4uHP0TQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=hfDtJpMlWR9id+6sTTOTacntY3oRde4KncmM9j3XCpCQcMvaY0Je8+ZeytQ6CVEPg G0OyWhe3c3uQNIdlmZpgJ7a+I4GYfBof3wZCwhPQ9AfWMwF7IUui6M63/JKtFg7yqy /XyM5dEM4Z2Pi+D6tY6FJh8qcO15917dQIHGLV5w= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 39/42] Documentation: Rename runtime configuration title Date: Tue, 7 Apr 2026 18:34:24 +0300 Message-ID: <20260407153427.1825999-40-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 Documentation/runtime_configuration.rst file has a title that mentions "variables" to refer to environment variables. Spell it out fully for clarity. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Documentation/runtime_configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst index a95de2f43ab0..2cdffb335a66 100644 --- a/Documentation/runtime_configuration.rst +++ b/Documentation/runtime_configuration.rst @@ -89,8 +89,8 @@ Configuration file example mode: gpu threads: 2 -List of variables and configuration options -------------------------------------------- +List of environment variables and configuration options +------------------------------------------------------- LIBCAMERA_LOG_FILE The custom destination for log output. From patchwork Tue Apr 7 15:34:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26480 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 8D555C3338 for ; Tue, 7 Apr 2026 15:35:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8A19462E54; Tue, 7 Apr 2026 17:35:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oZsRSL18"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A20A262E4B for ; Tue, 7 Apr 2026 17:35:23 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 14F57A30 for ; Tue, 7 Apr 2026 17:33:56 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576036; bh=9nJ0NsUAHPNV3NlLO4CsROHL12q8t3QrN/q11w5QQAE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=oZsRSL18299UqO5y9TRGtXpC2jmlw3fLgzDrxfRdsuo6S/bYKZfmNkCKrCpMBQBUL Mp/1NtC2/zR2lf6TIBeMaACAmaBgzI/UJjob5pUK7O0tSsVC0ZSLK3m0c5gWiULFFe nFoBoA4TF1exF+Nice1aoXS/elEWYH9Ja/D41svE= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 40/42] libcamera: software_isp: Rename "measure" option to "benchmark" Date: Tue, 7 Apr 2026 18:34:25 +0300 Message-ID: <20260407153427.1825999-41-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 software ISP "measure" configuration option doesn't clearly indicate what it measures. Rename it to "benchmark" to match the name of the class that handles it and make it self-explicit. While at it, improve the documentation slightly by replacing "per-frame time measurement" with "performance measurement". Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Documentation/runtime_configuration.rst | 10 +++++----- src/libcamera/software_isp/benchmark.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst index 2cdffb335a66..fa7fb34f8eff 100644 --- a/Documentation/runtime_configuration.rst +++ b/Documentation/runtime_configuration.rst @@ -48,7 +48,7 @@ file structure: software_isp: # true/false software_isp: copy_input_buffer: # true/false - measure: + benchmark: skip: # non-negative integer, frames to skip initially number: # non-negative integer, frames to measure mode: # cpu/gpu @@ -83,7 +83,7 @@ Configuration file example software_isp: true software_isp: copy_input_buffer: false - measure: + benchmark: skip: 50 number: 30 mode: gpu @@ -166,13 +166,13 @@ software_isp.copy_input_buffer Example value: ``false`` -software_isp.measure.skip, software_isp.measure.number - Define per-frame time measurement parameters in software ISP. `skip` +software_isp.benchmark.skip, software_isp.benchmark.number + Define performance measurement parameters for the software ISP. `skip` defines how many initial frames are skipped before starting the measurement; `number` defines how many frames then participate in the measurement. - Set `software_isp.measure.number` to 0 to disable the measurement. + Set `software_isp.benchmark.number` to 0 to disable the measurement. Example `skip` value: ``50`` diff --git a/src/libcamera/software_isp/benchmark.cpp b/src/libcamera/software_isp/benchmark.cpp index 96f0ae004563..22cc8cfb984e 100644 --- a/src/libcamera/software_isp/benchmark.cpp +++ b/src/libcamera/software_isp/benchmark.cpp @@ -35,11 +35,11 @@ Benchmark::Benchmark(const CameraManager &cm, const std::string &name) const GlobalConfiguration &configuration = cm._d()->configuration(); skipBeforeMeasure_ = configuration.option( - { "software_isp", "measure", "skip" }) - .value_or(skipBeforeMeasure_); - framesToMeasure_ = configuration.option( - { "software_isp", "measure", "number" }) - .value_or(framesToMeasure_); + { "software_isp", "benchmark", "skip" }) + .value_or(skipBeforeMeasure_); + framesToMeasure_ = configuration.option( + { "software_isp", "benchmark", "number" }) + .value_or(framesToMeasure_); } Benchmark::~Benchmark() From patchwork Tue Apr 7 15:34:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26481 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 1311BC32F9 for ; Tue, 7 Apr 2026 15:35:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A6AB162E56; Tue, 7 Apr 2026 17:35:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="R0U5YpSW"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EB97262E57 for ; Tue, 7 Apr 2026 17:35:24 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6D842A30 for ; Tue, 7 Apr 2026 17:33:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576037; bh=cz9oRoSTp2JnMN7xrp9ulGjuDga103W7o8UPUbdTr6k=; h=From:To:Subject:Date:In-Reply-To:References:From; b=R0U5YpSWUKbzr4aaLHfbeLjD+8qCPUIOSuZZeerVfCnA/Ih3WkK0RpbP7cnIDKq1p PIXvBvwbg/nJbvlaKgz6eHner5PdU2qTh3+O3DOp5I/ttUAWOrOrtUqArnUtAf1Eyz bMYPbrdCi5rNJjY9VGQqdai0hqUksHGn3jLaNGNQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 41/42] pipeline: simple: Rename supported_devices configuration option to devices Date: Tue, 7 Apr 2026 18:34:26 +0300 Message-ID: <20260407153427.1825999-42-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 pipelines.simple.supported_devices configuration entry list per-device options for the simple pipeline handler. Rename "supported_devices" to "devices" to indicate its role more clearly. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Documentation/runtime_configuration.rst | 6 +++--- src/libcamera/pipeline/simple/simple.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst index fa7fb34f8eff..9dfa61e72ce8 100644 --- a/Documentation/runtime_configuration.rst +++ b/Documentation/runtime_configuration.rst @@ -43,7 +43,7 @@ file structure: - ... # pipeline name pipelines: simple: - supported_devices: + devices: - driver: # driver name, e.g. `mxc-isi` software_isp: # true/false software_isp: @@ -78,7 +78,7 @@ Configuration file example - simple pipelines: simple: - supported_devices: + devices: - driver: mxc-isi software_isp: true software_isp: @@ -150,7 +150,7 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode Example value: ``gpu`` -pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp +pipelines.simple.devices.driver, pipelines.simple.devices.software_isp Override whether software ISP is enabled for the given driver. Example `driver` value: ``mxc-isi`` diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index c6fe12d65b18..9ae220fc8860 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -1881,7 +1881,7 @@ bool SimplePipelineHandler::matchDevice(std::shared_ptr media, swIspEnabled_ = info.swIspEnabled; const GlobalConfiguration &configuration = cameraManager()->_d()->configuration(); for (const ValueNode &entry : - configuration.configuration()["pipelines"]["simple"]["supported_devices"] + configuration.configuration()["pipelines"]["simple"]["devices"] .asList()) { auto name = entry["driver"].get(); if (name == info.driver) { From patchwork Tue Apr 7 15:34:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26482 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 B930BC333B for ; Tue, 7 Apr 2026 15:35:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4784262E4E; Tue, 7 Apr 2026 17:35:31 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TAV5TUIu"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 445C562E4B for ; Tue, 7 Apr 2026 17:35:26 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id B3DA8A30 for ; Tue, 7 Apr 2026 17:33:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1775576038; bh=CwffBtgKl52nMibZH2Q5tRr7wzYqm8nuDXpofXWzxk4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=TAV5TUIuiCkoCRCoRsam7ztGpMpeytZVfcag/02srXQQPskWGDG00u2+v067RlRmr B9Sb64cLJpYxu/HFRIH/U2Vb/IUxGcAilnBY+BFx+RzS/VUaBs2xxW4IJpOo1PVZU/ 4kxwJ6FHATMQrV0i2mUCADWVOLNsknNpr/wo8658= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 42/42] pipeline: simple: Turn devices configuration option into dictionary Date: Tue, 7 Apr 2026 18:34:27 +0300 Message-ID: <20260407153427.1825999-43-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.com> References: <20260407153427.1825999-1-laurent.pinchart@ideasonboard.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 pipelines.simple.devices configuration option contains a list of devices, each of them being a dictionary with a "driver" element that acts as a unique key to index the list. Turn it into a YAML dictionary to ensure uniqueness of the key, and simplify access in the pipeline handler. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- Changes since v1: - Print override message only when overriding --- This simplification will preclude indexing the list of devices with a compound key. For instance, if we were to extend the simple pipeline handler to match not only on a driver name but on a (driver, driver version) pair, the current configuration file structure would allow expressing this with pipelines: simple: devices: - driver: mxc-isi driver_version: 1 software_isp: true - driver: mxc-isi driver_version: 2 software_isp: false The new structure would conceptually allow us to write pipelines: simple: devices: ? driver: mxc-isi driver_version: 1 : software_isp: true ? driver: mxc-isi driver_version: 2 : software_isp: false For those not familiar with the explicit syntax for mappings, this would translate to the following Python data if dict objects were hashable in Python: { 'pipelines': { 'simple': { 'devices': { { 'driver': 'mxc-isi', 'driver_version': 1 }: { 'software_isp': True, }, { 'driver': 'mxc-isi', 'driver_version': 2 }: { 'software_isp': False, }, }, }, }, } Yes, YAML can use complex data as keys in mappings. I would really not want to have to support that in libcamera though. Opinions will be appreciated. --- Documentation/runtime_configuration.rst | 6 +++--- src/libcamera/pipeline/simple/simple.cpp | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst index 9dfa61e72ce8..6fc53131433f 100644 --- a/Documentation/runtime_configuration.rst +++ b/Documentation/runtime_configuration.rst @@ -44,7 +44,7 @@ file structure: pipelines: simple: devices: - - driver: # driver name, e.g. `mxc-isi` + # driver name, e.g. `mxc-isi`: software_isp: # true/false software_isp: copy_input_buffer: # true/false @@ -79,7 +79,7 @@ Configuration file example pipelines: simple: devices: - - driver: mxc-isi + mxc-isi: software_isp: true software_isp: copy_input_buffer: false @@ -150,7 +150,7 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode Example value: ``gpu`` -pipelines.simple.devices.driver, pipelines.simple.devices.software_isp +pipelines.simple.devices..software_isp Override whether software ISP is enabled for the given driver. Example `driver` value: ``mxc-isi`` diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 9ae220fc8860..b10c7fc4a25e 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -1879,18 +1879,17 @@ bool SimplePipelineHandler::matchDevice(std::shared_ptr media, } swIspEnabled_ = info.swIspEnabled; + const GlobalConfiguration &configuration = cameraManager()->_d()->configuration(); - for (const ValueNode &entry : - configuration.configuration()["pipelines"]["simple"]["devices"] - .asList()) { - auto name = entry["driver"].get(); - if (name == info.driver) { - swIspEnabled_ = entry["software_isp"].get().value_or(swIspEnabled_); - LOG(SimplePipeline, Debug) - << "Configuration file overrides software ISP for " - << info.driver << " to " << swIspEnabled_; - break; - } + const ValueNode &cfg = + configuration.configuration()["pipelines"]["simple"]["devices"][info.driver]; + + if (auto enable = cfg["software_isp"].get()) { + swIspEnabled_ = *enable; + + LOG(SimplePipeline, Debug) + << "Configuration file overrides software ISP for " + << info.driver << " to " << swIspEnabled_; } /* Locate the sensors. */