From patchwork Thu Apr 23 23:00:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26523 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 6E6C1BDCB5 for ; Thu, 23 Apr 2026 23:01:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 02DF262F5E; Fri, 24 Apr 2026 01:01:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bdTGOPZy"; 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 AEF5C62EAA for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id B8D7134; Fri, 24 Apr 2026 00:59:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985162; bh=sKNd9y42pqTvjlTDTe3/j20UzQr4dKfSyUqd6SGOC4g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bdTGOPZyqfQmBxxBjvbDPl/Wi7lRrCLC5asUFH+2uKFD1xca2SLZNWyGb85xq4o3o EMep/xrf607KBIrNMtX3c2FTK8EJ14Uu1Z02cbZkXdgl2Ao7/NpxxTjgtgmK2qTloM TzejCWDa1sOWfdeTUkY0uWZGBaxBO/NVr9M6yju0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi Subject: [PATCH v3 01/37] libcamera: utils: Add overloaded visitor helpers Date: Fri, 24 Apr 2026 02:00:23 +0300 Message-ID: <20260423230059.3180987-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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" From: Jacopo Mondi std::visit() allows quite elegant type-matching implementation of the visitor pattern. The 'overloaded' type helpers allow to define a hierarchy of overloaded operator() implementations which can be used by std::visit(). Currently only the Virtual pipeline handler uses this type-matching implementation of std::visit(). To prepare to add another user in the Mali C55 pipeline handler move the 'overloaded' helper type to libcamera::utils for easier re-use. Signed-off-by: Jacopo Mondi Reviewed-by: Barnabás Pőcze Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- Changes since v8 of "[PATCH v8 0/8] libcamera: mali-c55: Add support for memory-to-memory": - Small improvements to documentation - Drop documentation of deduction guide --- include/libcamera/base/utils.h | 9 +++++++ src/libcamera/base/utils.cpp | 30 ++++++++++++++++++++++ src/libcamera/pipeline/virtual/virtual.cpp | 10 ++------ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h index 7083b7ce9ce9..b33a4c644a87 100644 --- a/include/libcamera/base/utils.h +++ b/include/libcamera/base/utils.h @@ -37,6 +37,15 @@ namespace libcamera { namespace utils { +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +#ifndef __DOXYGEN__ +template +overloaded(Ts...) -> overloaded; +#endif + const char *basename(const char *path); char *secure_getenv(const char *name); diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp index 42a516097be2..4ab2bd863e11 100644 --- a/src/libcamera/base/utils.cpp +++ b/src/libcamera/base/utils.cpp @@ -23,6 +23,36 @@ namespace libcamera { namespace utils { +/** + * \struct overloaded + * \brief Helper type for type-matching std::visit() implementations + * \tparam Ts... Template arguments pack of visitors + * + * Expand the template argument pack \a Ts... to provide overloaded \a + * operator() to support type-matching implementations of the visitor design + * pattern using std::visit(). + * + * An example is provided by the C++ standard library documentation in the form + * of: + * + * \code{.cpp} + * template struct overloaded : Ts... { using Ts::operator()...; }; + * template overloaded(Ts...) -> overloaded; + * + * using var_t = std::variant; + * std::vector vec = {10, 15l, 1.5, "hello"}; + * + * for (auto& v: vec) { + * std::visit(overloaded { + * [](auto arg) { std::cout << arg << ' '; }, + * [](double arg) { std::cout << std::fixed << arg << ' '; }, + * [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }, + * }, v); + * \endcode + * + * Use this helper to implement type-matching visitors using std::visit(). + */ + /** * \brief Strip the directory prefix from the path * \param[in] path The path to process diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index efd800ebe3d6..e8ef7e524ccf 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -57,13 +58,6 @@ uint64_t currentTimestamp() } /* namespace */ -template -struct overloaded : Ts... { - using Ts::operator()...; -}; -template -overloaded(Ts...) -> overloaded; - class VirtualCameraConfiguration : public CameraConfiguration { public: @@ -428,7 +422,7 @@ bool PipelineHandlerVirtual::initFrameGenerator(Camera *camera) { auto data = cameraData(camera); auto &frame = data->config_.frame; - std::visit(overloaded{ + std::visit(utils::overloaded{ [&](TestPattern &testPattern) { for (auto &streamConfig : data->streamConfigs_) { if (testPattern == TestPattern::DiagonalLines) From patchwork Thu Apr 23 23:00: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: 26524 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 A1E0BBDCB5 for ; Thu, 23 Apr 2026 23:01:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C406062F60; Fri, 24 Apr 2026 01:01:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="v+or/6WT"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A79762F57 for ; Fri, 24 Apr 2026 01:01:03 +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 ESMTPSA id 1AF7434 for ; Fri, 24 Apr 2026 00:59:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985164; bh=vqb+V5KiW+iQzA+wKfrOttyEJip+8YUkGI2wXdO0r/4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=v+or/6WT7In/fLhwVIQJaZ5Ds3iq+pPf9rvoYz0vEckNyxL1FOJDJQl2vzXp+V1sy qQ3uMlV2WCfPKvY8yOxWvoGQDve3242ganNthB5UyUhLeNbescixSvtTNNNgLM4xAx hJl7rcl98oq4w1gWgSs/U8watV6StHVxSi8fvU2w= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 02/37] libcamera: yaml_parser: Use std::make_unique<> Date: Fri, 24 Apr 2026 02:00:24 +0300 Message-ID: <20260423230059.3180987-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Thu Apr 23 23:00: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: 26525 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 37C7EBDCB5 for ; Thu, 23 Apr 2026 23:01:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B373062F72; Fri, 24 Apr 2026 01:01:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aahURiow"; 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 EB2A462F5D for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 6FD0E9A6 for ; Fri, 24 Apr 2026 00:59:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985165; bh=t1nl6sy8MFRvVdFtecv66RVa3fw0XbtCuJV0u3f75g4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=aahURiowuKva5O9wQpOh5PDgwbHFgWGtfOxGNoGKA2sDpNkmHmicfgsafXkuORhdS oxGcv8FqPyHl62We6avC1ewRsEmMBX2t6arcd0tb/vKY6vf0QNDD6NkR8EiYNC2HmS dXC7g5mNq4OCK5uJGAYOW9hc0pnBZPJU6tPKooeU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 03/37] libcamera: yaml_parser: Rename Container to ValueContainer Date: Fri, 24 Apr 2026 02:00:25 +0300 Message-ID: <20260423230059.3180987-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00: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: 26526 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 0EABFBDCB5 for ; Thu, 23 Apr 2026 23:01:13 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8737562F78; Fri, 24 Apr 2026 01:01:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="QxZcohKU"; 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 5E42F62F5B for ; Fri, 24 Apr 2026 01:01:06 +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 ESMTPSA id D1070C75 for ; Fri, 24 Apr 2026 00:59:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985167; bh=c38nK1Oj2KhzIFQI3q3+8mQLnra9FItGsozs44qxrpY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=QxZcohKU7AsiSyohjyRY4sMiuXyx+L0AJxG4VrnOVgHaj982ve9OjakMDRpkAJIwK BwzuZo2JosnZAASAM5fdWxkM3dR38kwa3gUE62rZhaqunyI4wW3H2Hnu5iimYfumbM /OsrjE7R+i5bSNyLerKOOGablYOlLfddz8JJ87jM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 04/37] libcamera: yaml_parser: Rename Getter to Accessor Date: Fri, 24 Apr 2026 02:00:26 +0300 Message-ID: <20260423230059.3180987-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00: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: 26527 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 7ADEBBDCB5 for ; Thu, 23 Apr 2026 23:01:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A682062F6C; Fri, 24 Apr 2026 01:01:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GnWyvzkt"; 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 B20C362F63 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 2614C802 for ; Fri, 24 Apr 2026 00:59:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985168; bh=HZQlD3dBdcnKelvMoq/bvh8D856nOgSCZh2Dia3Tu/A=; h=From:To:Subject:Date:In-Reply-To:References:From; b=GnWyvzktCoir+INNTWwnXL8m0ylKoAuROkFI8nStPdfWta0iY11cwTpG+sCrmACvp iMi6oB9WC7JYADKaq8l0CtR9SlIQJUa6+rSL1o6gGxoOEUMZQ0/0azgYeQwL0GCd3Q Rgdx6Us5Ccy2Q/kzaJIFXCVR9M9ELkEwWf4BKnas= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 05/37] libcamera: yaml_parser: Replace getList() with get() specializations Date: Fri, 24 Apr 2026 02:00:27 +0300 Message-ID: <20260423230059.3180987-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- 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 Thu Apr 23 23:00:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26528 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 00344BDCB5 for ; Thu, 23 Apr 2026 23:01:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0637B62F86; Fri, 24 Apr 2026 01:01:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="nheJTvKT"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3626562F5D for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id A4ACF9A6 for ; Fri, 24 Apr 2026 00:59:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985169; bh=TZbqWbmHjLgu+HQLVtGYpdUZlq/uo4VB5e+cAOBAzcw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=nheJTvKTO83Ke8Y64vIXsR51lLcDySFyowPDYO8/xT1cNn9cDw7ds3RxjWhgOedqb 1ROugWPj7SajKJfYDGAFofiTiSP0E5oPEkApXE6jJlhZVcg1M0xk8Ck2AQRWwhuK2B d7k9iDchn+rxUM2CnpeuBZ1wheUfXPXIOo8HVPFY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 06/37] libcamera: yaml_parser: Add function to set a YamlObject value Date: Fri, 24 Apr 2026 02:00:28 +0300 Message-ID: <20260423230059.3180987-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- Changes since v2: - Drop std::forward() 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..8c84f560ff0f 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(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 Thu Apr 23 23:00:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26529 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 4ABA1BDCB5 for ; Thu, 23 Apr 2026 23:01:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 463A962F72; Fri, 24 Apr 2026 01:01:16 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="VAT97Ofa"; 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 7DD9862F6E for ; Fri, 24 Apr 2026 01:01:10 +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 ESMTPSA id F2E02802 for ; Fri, 24 Apr 2026 00:59:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985171; bh=8Dvn0nleUiwPSWalrMfXsahKemTcaEBSCwQKPDOQBbY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=VAT97Ofa4TafhyiUZj60p+nP3LnrruxbPuZCAJ2ruVSZbN0UKEwjYjrr3VDiamAfZ gDEP7rk4WRQbaISS+EWTZFLj+oOWGExuJ31z+xKStfqqUop7KwZOaV3ArrGiF6DMpP QytRp2pKtVTnATaiBuSysQFMHuX2lCgsrW1oH3UE= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 07/37] libcamera: yaml_parser: Add functions to add children Date: Fri, 24 Apr 2026 02:00:29 +0300 Message-ID: <20260423230059.3180987-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- Changes since v2: - Fix typo in documentation 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 8c84f560ff0f..5b2e743ee41a 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 exists, 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 Thu Apr 23 23:00:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26530 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 9870ABDCB5 for ; Thu, 23 Apr 2026 23:01:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4D3E762F6C; Fri, 24 Apr 2026 01:01:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="jetyM5Tt"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0562362F75 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 70F39802 for ; Fri, 24 Apr 2026 00:59:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985172; bh=ya4+6nvLWoRfd0CjZ0AeFaSu2cS6SHqdKpqSECSwjCI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=jetyM5TtIK302feil+OwXUfdiUNsGpA++2th7jO1brepRSAJ7Mt/pKBalqxImjFRU ZOU6izLmBoETwAFcSUdUVVllOiVfn8jtdcH6V9LaaZDd1XWWUbndpb4MmWoNfem+k+ ef6hkMsM6NHhe3/tI7EdlpKCuAjUimSMC9GE1ez8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 08/37] libcamera: yaml_parser: Un-friend YamlParserContext from YamlObject Date: Fri, 24 Apr 2026 02:00:30 +0300 Message-ID: <20260423230059.3180987-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- 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 5b2e743ee41a..b1439bc8124d 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 Thu Apr 23 23:00:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26531 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 5F32FBDCB5 for ; Thu, 23 Apr 2026 23:01:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 00F1162F97; Fri, 24 Apr 2026 01:01:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TQtX4sT9"; 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 5045D62F6B for ; Fri, 24 Apr 2026 01:01:13 +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 ESMTPSA id BB10C802 for ; Fri, 24 Apr 2026 00:59:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985173; bh=UZgZhlQwgVg3fgwm9dX9iwtTkaGYqG3RmFMKUCvmRw4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=TQtX4sT9nnwVgImBQt32yctAW9ZauCuaA0kD5RwGNDZTerDLufqjtxBjxlNbt2b96 Xyh1CBC5q+NjOPQ7zrVKk8QtnqY8s6zRDjBljGqx6ZWaxOBw5MVNxwvYxgNJ/s+8ql ywz9jRm7oss8VOcErmjuyUwIczslKwRQkZ0NTizM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 09/37] libcamera: yaml_parser: Move Size handling to geometry.cpp Date: Fri, 24 Apr 2026 02:00:31 +0300 Message-ID: <20260423230059.3180987-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 b1439bc8124d..c5f3af24a7df 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 Thu Apr 23 23:00:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26532 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 08325BDCB5 for ; Thu, 23 Apr 2026 23:01:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 97C4162FA2; Fri, 24 Apr 2026 01:01:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="QUUfL50n"; 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 CD35362F85 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 3AB9C9A6 for ; Fri, 24 Apr 2026 00:59:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985175; bh=3Q12LBa5A4kPOu6nf/m0F1bJN4roLA2MsY6e5BriA0c=; h=From:To:Subject:Date:In-Reply-To:References:From; b=QUUfL50nE1nrWvwp3FF/0dueXmgOAS5rAo7m4/GTd8ET4X40DlOW7tWfcMjrwj0xG doS9MNEHhEuL25xMwDqAVpJuU9/Ky9UQaVE6eJ2gq1v4SmXap/C0ERzAUtjVuCHJyD S4Sn827nO5PFrfz8/7OUWJyNzEMfbhCInholNKSo= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 10/37] libcamera: yaml_parser: Drop unneeded \fn Doxygen commands Date: Fri, 24 Apr 2026 02:00:32 +0300 Message-ID: <20260423230059.3180987-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 c5f3af24a7df..347875769a44 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 Thu Apr 23 23:00:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26533 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 CF493BDCB5 for ; Thu, 23 Apr 2026 23:01:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7390062F7B; Fri, 24 Apr 2026 01:01:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="V5dEttNT"; 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 42A7D62F68 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 815D19A6 for ; Fri, 24 Apr 2026 00:59:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985176; bh=TuRTtdVVD7q2H7ylr8pTdsNx6oZ5ulxYmJbhGC1pCiM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=V5dEttNTtz3bZljZ3F5J21x97Bl/nwyOzCTY22qloMQAqMvrVbE5JPn3sbMLZFq5G uKyEPIfrOU0WsXTrNGLVZMahPwt7k2DAngieJ0b0M38Zg3iVi9UAryZep7R5egpv7s E6BFZ30eKbJ33l0NIAiNml5DproOaGVrQ2QyuO6k= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 11/37] libcamera: yaml_parser: Split YamlObject from YamlParser Date: Fri, 24 Apr 2026 02:00:33 +0300 Message-ID: <20260423230059.3180987-12-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- .../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..804f4a5b407c --- /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) 2026, 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 e8ef7e524ccf..3e8fdf496788 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -37,7 +37,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..94f99374f415 --- /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(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 exists, 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 347875769a44..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(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 exists, 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 Thu Apr 23 23:00:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26534 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 F1289BDCB5 for ; Thu, 23 Apr 2026 23:01:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 92F2462F98; Fri, 24 Apr 2026 01:01:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HKSzzQ++"; 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 84CDF62F66 for ; Fri, 24 Apr 2026 01:01:17 +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 ESMTPSA id 0F1999A6 for ; Fri, 24 Apr 2026 00:59:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985178; bh=asQ9NgzxwnjXGxAtGy989ZdcvEr9DNUnMK40g2QR++o=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HKSzzQ++AHFHAOf/+OM3yAyqFCL1ooif7cUdm1eKfWMfWv9uf4uo1Xrw0GS+bdz47 0UYYf90FJrdxmz3RV8Zs3WkglLU/gCbIRZHOi/NmoP+cwPoQtbyhCFHtNU+CA8c8b7 q5Zqig25x8S5o8SIKGd/+VxrUFwS6cmgOIOdBtN4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 12/37] libcamera: yaml_object: Miscellaneous documentation improvements Date: Fri, 24 Apr 2026 02:00:34 +0300 Message-ID: <20260423230059.3180987-13-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 94f99374f415..6ba4e1a94fb1 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 Thu Apr 23 23:00:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26535 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 C5C2FBDCB5 for ; Thu, 23 Apr 2026 23:01:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 72D4362FAD; Fri, 24 Apr 2026 01:01:31 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="tZEI9Ti9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 67B5962F86 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 58D5A9A6 for ; Fri, 24 Apr 2026 00:59:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985180; bh=yOingvf30CHryv/P/hLS/qJ4dNkEgug/fAxGOBtIHFc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=tZEI9Ti9DKqVjUBygRnvfDLNqkbDf49D3uAoDUlABazjCdkFwQWcSldfCD8YKdVkd KqMvNbmKDuKgHsr/oFiYGoe8wKFSY1EQ+udHrr5SuBLDIjELanAMiuaU9ckv+ufFxY M80/nwE/m4SExwxVhmME6M3wyyRaLdi1gtzG0Ld4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 13/37] libcamera: Rename YamlObject to ValueNode Date: Fri, 24 Apr 2026 02:00:35 +0300 Message-ID: <20260423230059.3180987-14-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 --- Changes since v2: - Fix typo in documentation --- .../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 804f4a5b407c..d4533df1a643 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) 2026, 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 b93e5c0b1974..185e06f918f4 100644 --- a/src/ipa/rpi/controller/rpi/decompand.cpp +++ b/src/ipa/rpi/controller/rpi/decompand.cpp @@ -24,7 +24,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 fdb4d733adda..1a53de245dac 100644 --- a/src/ipa/rpi/controller/rpi/decompand.h +++ b/src/ipa/rpi/controller/rpi/decompand.h @@ -18,7 +18,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 e7ddbc5a052b..9606459fc2e8 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -449,7 +449,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 0b2f03d8d719..6791281380ef 100644 --- a/src/libcamera/pipeline/virtual/README.md +++ b/src/libcamera/pipeline/virtual/README.md @@ -51,7 +51,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 3e8fdf496788..81d2dddab815 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -37,7 +37,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..2d1642c2dd06 --- /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) 2026, 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(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 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 exists, 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 6ba4e1a94fb1..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(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 exists, 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 Thu Apr 23 23:00:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26536 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 942ABC32F6 for ; Thu, 23 Apr 2026 23:01:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 412D362FA8; Fri, 24 Apr 2026 01:01:32 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UbjP7Jwh"; 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 E8DC962F75 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 6EF69802 for ; Fri, 24 Apr 2026 00:59:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985181; bh=W3M2DdCXe1Dp/v21zwb9TIWjYy/jlfmZhUfU/4yzz4c=; h=From:To:Subject:Date:In-Reply-To:References:From; b=UbjP7Jwhj2HVxu8C73SYjGobojfeYSxVr6/OJZLLKAUvZhbhCGU9QLEETv8XVPbYn e4SkGoY1xuVFwM1zKiUfz5URccrKmJOVP7OfDVb7fEKX+UOD5n6gLcHcDZA0NGQ+yI CFwHns08cVMouCFoP8RGzQM64jRiSeW0+AXL1UC8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 14/37] libcamera: value_node: Add constructor with value Date: Fri, 24 Apr 2026 02:00:36 +0300 Message-ID: <20260423230059.3180987-15-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 d4533df1a643..bcf2c8ecc90e 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 2d1642c2dd06..324419f02bb6 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 Thu Apr 23 23:00:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26537 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 73952BDCB5 for ; Thu, 23 Apr 2026 23:01:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E23A162FB2; Fri, 24 Apr 2026 01:01:32 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RZtQ5+Ug"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 49CA362F6C for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id C6ADFC75 for ; Fri, 24 Apr 2026 00:59:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985182; bh=UmYueAuOZ1ly90EENXMtXnv/ouRFeu+FKHxQbiR8nko=; h=From:To:Subject:Date:In-Reply-To:References:From; b=RZtQ5+UgmbsTHWulNoPCz3CkKPH9mg67YpkIb+LILKhGx3ipW1V7iMPRGlVTukjaK uvgXrQ8V4JrT3+O17T9EOPAv8UKGB6YW2fs52sLK7jdosZd2ybWt7O8yrVD1fIKpeG WcBv63xZJTF5J49bsdhsOMbHrU7M/swY+gjCEUdU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 15/37] libcamera: value_node: Rework templates to prepare for mutable views Date: Fri, 24 Apr 2026 02:00:37 +0300 Message-ID: <20260423230059.3180987-16-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 bcf2c8ecc90e..16e6660e8329 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 Thu Apr 23 23:00:38 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26538 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 36B37BDCB5 for ; Thu, 23 Apr 2026 23:01:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A9B6662FB5; Fri, 24 Apr 2026 01:01:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="u7leya8D"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9666462F86 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 11B2CC75 for ; Fri, 24 Apr 2026 00:59:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985184; bh=271LkudYLoFrF9u1x3Js8ZS6P6JtAxU6W1j3htfU7r8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=u7leya8DYkqDz3nHNWCwqg2+LWMBet7fVFHnoPjqCIi7y6CXAxRMv7kz7wit528A6 Rv/LZupmOFbiGoQOCG5NjBrsySaxNbDAnpZYewPOVEhW9EybsYOTIKwv3H7fQhO0mi LSWCzCWW/ykBEaoIF71/I1UG5s1e3SZz8cR7k54c= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 16/37] libcamera: value_node: Add mutable adapters Date: Fri, 24 Apr 2026 02:00:38 +0300 Message-ID: <20260423230059.3180987-17-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 16e6660e8329..b51d2f4b610b 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 324419f02bb6..e2dd455d3f1e 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 Thu Apr 23 23:00:39 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26539 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 18570BDCB5 for ; Thu, 23 Apr 2026 23:01:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 82A5262FB8; Fri, 24 Apr 2026 01:01:34 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rEI3mo7y"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2F69962FA0 for ; Fri, 24 Apr 2026 01:01:25 +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 ESMTPSA id 9E9C6C75 for ; Fri, 24 Apr 2026 00:59:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985185; bh=eaiIlizXOeEKWM9qFFxqa7THLDeDOPecz1G2qG/TvNE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=rEI3mo7ynt6UMkYtwqWVQZNIqeT5q+LxAJJ+h6iezDh0xOJxWjMlHUwQZGl0bqbit 7t0hRdgW5jY6hr+DljY2xYx0zc74TjDtO+OiFnfo8WCqKibp39TGtQzYsutLW6CmvO 14sCdodM7xEV6w4V6PYQtbggeVvlh+tyE/bp/Rhs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 17/37] libcamera: value_node: Add mutable children accessors Date: Fri, 24 Apr 2026 02:00:39 +0300 Message-ID: <20260423230059.3180987-18-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 b51d2f4b610b..da68a74c5e14 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 e2dd455d3f1e..fbf9ac470359 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 Thu Apr 23 23:00:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26540 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 C652EBDCB5 for ; Thu, 23 Apr 2026 23:01:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7A3D162FA6; Fri, 24 Apr 2026 01:01:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DqByOHhE"; 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 AE09562FA0 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 3D88E9A6 for ; Fri, 24 Apr 2026 00:59:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985187; bh=PidSzAhxtWhuPeaj3OS2O7D4o3JT1bQ8YjDp+WSXyqM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=DqByOHhEXCdc+k4isSF7ip9DpCobCeb3R/7rqrCuAFeEJ8GF8lIDhCjacQU3dAzIU MZ3VCL5lsieQUVfIsNO67bmydCXDgTIh7kPblI70QsZbGh/pxymoPnUXooJLtJONF+ m903eCRE4hj06bwS6ONqxd2odWmBKmkzOIMVKvZM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 18/37] libcamera: value_node: Support adding nested children in one operation Date: Fri, 24 Apr 2026 02:00:40 +0300 Message-ID: <20260423230059.3180987-19-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Barnabás Pőcze --- Changes since v2: - Do not move child if node->add() fails 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 | 58 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index da68a74c5e14..578413199df1 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 fbf9ac470359..3b4041b7bbf0 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,58 @@ 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(); + + node = node->add(std::string{ name }, + obj ? std::move(obj) : std::move(child)); + 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 Thu Apr 23 23:00:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26541 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 6AD58BDCB5 for ; Thu, 23 Apr 2026 23:01:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2159E62FBC; Fri, 24 Apr 2026 01:01:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hdhWFbCs"; 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 F2DF062FA2 for ; Fri, 24 Apr 2026 01:01:27 +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 ESMTPSA id 7E4819A6 for ; Fri, 24 Apr 2026 00:59:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985188; bh=jYA4ZrmdMuN4fMJyT8ozMO+KYOftW5mwSAAbsLE8jcw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=hdhWFbCsX4W34PtnAo+x+v5RCyuZ97R92Gg9IbUoESiH0YsXl1UYgWrWGh8I3CXTz 34zRjwRdyiZ3tlbEn6GQCjeA16VN30yRcFpXhSEXv8GhxHoSXewfeHTAVvdpNAEMSv g9VJxy60EQxas3DzV2GXMR5ojgLDYe1gTywl2ClU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 19/37] libcamera: value_node: Support looking up descendant node by path Date: Fri, 24 Apr 2026 02:00:41 +0300 Message-ID: <20260423230059.3180987-20-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 578413199df1..ccae69e25a82 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 3b4041b7bbf0..6ab11009fccc 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 Thu Apr 23 23:00:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26542 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 C9785BDCB5 for ; Thu, 23 Apr 2026 23:01:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 524D962FD5; Fri, 24 Apr 2026 01:01:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="tvMHLga3"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4675D62F7B for ; Fri, 24 Apr 2026 01:01:29 +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 ESMTPSA id BCE6D802 for ; Fri, 24 Apr 2026 00:59:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985189; bh=WzSSponFlqQsxWhUEDvIaRQGV4bNbY+du6Wbsgii0aA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=tvMHLga3g98UUM5VeQPPDl56phhlNWUo/+lIxUQIxOzI1DaC5oP36d+kEjU8r77jl ejyShoXFTdh/u711GExDh53Bn+8dc91KPJfezR8lFHJtXiypczzaJkkMG9VB8vrFQj Z8yHcTAK26SWjhlA6xowSbK981Zp6pD18pQaTKj8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 20/37] libcamera: value_node: Add functions to erase child nodes Date: Fri, 24 Apr 2026 02:00:42 +0300 Message-ID: <20260423230059.3180987-21-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- Changes since v2: - Use std::string_view() for first overload - Add todo comment --- include/libcamera/internal/value_node.h | 3 ++ src/libcamera/value_node.cpp | 54 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h index ccae69e25a82..cab14943c03f 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(std::string_view 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 6ab11009fccc..2c875db366e7 100644 --- a/src/libcamera/value_node.cpp +++ b/src/libcamera/value_node.cpp @@ -620,4 +620,58 @@ 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(std::string_view key) +{ + auto node = dictionary_.find(key); + if (node == dictionary_.end()) + return; + + /* \todo Not an ideal algorithm */ + for (auto iter = list_.begin(); iter != list_.end(); ++iter) { + if (iter->value.get() != node->second) + continue; + + list_.erase(iter); + break; + } + + dictionary_.erase(node); +} + +/** + * \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(name); + return; + } + + auto iter = node->dictionary_.find(name); + if (iter == node->dictionary_.end()) + return; + + node = iter->second; + } +} + } /* namespace libcamera */ From patchwork Thu Apr 23 23:00:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26543 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 AE8ADBDCB5 for ; Thu, 23 Apr 2026 23:01:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1D05E62F75; Fri, 24 Apr 2026 01:01:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="GzkPvzSB"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8C37D62F7B for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 09D48802 for ; Fri, 24 Apr 2026 00:59:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985191; bh=hvL4+04xk3K2ZACY0uey9xClIel8AEr7z8Z9nj0qExk=; h=From:To:Subject:Date:In-Reply-To:References:From; b=GzkPvzSBaRMygMTT/5D4zYAlGVxyR4XqgScrvwuM5SvR3ELIYO5Yen/zTLnW5/VEx r7/bhB2ZIb4ZWnNLUdhvavTyZlRw+0nAT8yN9El6aQEbIyUacDxZfyxBmB4nG0+C2x G1nkqbIwbl2odeuBJCGFpcxCPV7S4doqz4IMy3LY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 21/37] test: Add ValueNode unit test Date: Fri, 24 Apr 2026 02:00:43 +0300 Message-ID: <20260423230059.3180987-22-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 unit test for the ValueNode class. The tests focus on the class itself, without considering that is currently only used when parsing YAML files. This duplicates some of the tests of the YamlParser class, which will be dropped from the corresponding unit test in a subsequent change. Signed-off-by: Laurent Pinchart Reviewed-by: Isaac Scott --- Changes since v2: - Add commit message - Drop 'overloaded' definition --- test/meson.build | 1 + test/value-node.cpp | 558 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 559 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..3b6466e75b13 --- /dev/null +++ b/test/value-node.cpp @@ -0,0 +1,558 @@ +/* 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; + +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(utils::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 Thu Apr 23 23:00:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26544 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 7FEE7BDCB5 for ; Thu, 23 Apr 2026 23:01:48 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EDA1362FD5; Fri, 24 Apr 2026 01:01:47 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UxayQAlJ"; 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 D856F62FAF for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 55088802 for ; Fri, 24 Apr 2026 00:59:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985192; bh=mG+SiwmyGP1pkyoj5nifYfDoJLIwV8/UWHUkoaNZgPM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=UxayQAlJwGVAhPSjgPC5Xoh9ZarFlZhlrzmwXN9iMH1srA5r/eJWMzaYIZ+YruvoN /JOIk1vTkVyKmAa3O9OmMt+9Ko+m6JgTEoNy1DUCp8F17TNbR4VTJr0bB9ucMsS2th IQzge+nY+uaX62cpeXSSQ2bqL5MW7nvVhPTp3Svs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 22/37] test: yaml-parser: Simplify test Date: Fri, 24 Apr 2026 02:00:44 +0300 Message-ID: <20260423230059.3180987-23-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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" Most of the tests in the YamlParser unit test cover the ValueNode class. They are now implemented in the ValueNode unit test. Drop them, and only keep the tests that related to YAML parsing. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze --- 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 Thu Apr 23 23:00:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26545 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 1FADDBDCB5 for ; Thu, 23 Apr 2026 23:01:50 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B389E62FC0; Fri, 24 Apr 2026 01:01:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wVchW+iv"; 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 39E4162FB4 for ; Fri, 24 Apr 2026 01:01:33 +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 ESMTPSA id B07929A6 for ; Fri, 24 Apr 2026 00:59:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985193; bh=qN/E1WOQoRc87dPE4+En60WJoPjE9JopEEcXDStN1ms=; h=From:To:Subject:Date:In-Reply-To:References:From; b=wVchW+ivDaEeBOhKrmEGVWzO7Mjy50POgc2S7o4va2jRg8Hvz46SHJ1mbGQAers1P Tf/4KqZVrqhCTgR+BrQTWspqgY+zaEE44LhHmZEoy1iGrPoA0jRgPIxG8l17j9bHg+ XFkDOnX9oODkA23sWIzwQe+QaVk7ax9xdAGcoU08= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 23/37] test: yaml-parser: Standardize on explicitly qualifying std namespace Date: Fri, 24 Apr 2026 02:00:45 +0300 Message-ID: <20260423230059.3180987-24-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Barnabás Pőcze --- 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 Thu Apr 23 23:00: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: 26546 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 EC3E7BDCB5 for ; Thu, 23 Apr 2026 23:01:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 75F0862FDD; Fri, 24 Apr 2026 01:01:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DIZjaTW0"; 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 7C93962FB7 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id F330BC77 for ; Fri, 24 Apr 2026 00:59:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985195; bh=NLKSu4ahEffnyIzmiQhApix/r2n0nFFWD7S65XMZhVQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=DIZjaTW0qbPd01GFlUdnkE+F7Bx0C54g/wS7Gxo1LwENALmxM49zOdHlvV01TK8JU rR/NFnlXp3d6wvn5n75mXpEkKkR3JkrHBhqvxem/Acm0pV1q6DHMsuBRAU31yH3VDS QAsWwotQ4divHbwI0LPEm861kylJuMJMM4/yrJjs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 24/37] test: yaml-parser: Fix typos in error messages Date: Fri, 24 Apr 2026 02:00:46 +0300 Message-ID: <20260423230059.3180987-25-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 unit test contains a few typos in error messages. Fix them. Signed-off-by: Laurent Pinchart Reviewed-by: Barnabás Pőcze Reviewed-by: Isaac Scott --- test/yaml-parser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp index e8fd9ad61cd0..a4b26c17e1f9 100644 --- a/test/yaml-parser.cpp +++ b/test/yaml-parser.cpp @@ -71,13 +71,13 @@ protected: /* Test parsing invalid YAML file. */ File file{ invalidYamlFile_ }; if (!file.open(File::OpenModeFlag::ReadOnly)) { - std::cerr << "Fail to open invalid YAML file" << std::endl; + std::cerr << "Failed to open invalid YAML file" << std::endl; return TestFail; } std::unique_ptr root = YamlParser::parse(file); if (root) { - std::cerr << "Invalid YAML file parse successfully" << std::endl; + std::cerr << "Invalid YAML file parsed successfully" << std::endl; return TestFail; } @@ -199,14 +199,14 @@ protected: auto &level1Obj = (*root)["level1"]; if (!level1Obj.isDictionary()) { - std::cerr << "level1 object fail to parse as Dictionary" << std::endl; + std::cerr << "level1 object failed to parse as Dictionary" << std::endl; return TestFail; } auto &level2Obj = level1Obj["level2"]; if (!level2Obj.isList() || level2Obj.size() != 2) { - std::cerr << "level2 object should be a 2 elements list" << std::endl; + std::cerr << "level2 object should be a 2 element list" << std::endl; return TestFail; } @@ -215,7 +215,7 @@ protected: 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 object failed to parse as integer list" << std::endl; return TestFail; } @@ -231,7 +231,7 @@ protected: !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 object failed to parse as dictionary" << std::endl; return TestFail; } From patchwork Thu Apr 23 23:00:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26547 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 C31D1BDCB5 for ; Thu, 23 Apr 2026 23:01:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 520E362FD5; Fri, 24 Apr 2026 01:01:53 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mK5IG2ry"; 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 050FA62FAC for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 8C3EF1283 for ; Fri, 24 Apr 2026 00:59:56 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985196; bh=WqWLJCGJml0Nk3UlPb6N/1Ev9Wt3aIz9ZnwJotPZztg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=mK5IG2ryqnDxL/uphQh/dtNiTU8zwaQKeEcfpxHyac4AfOY8al5xfPjvQ38baxk3t WNtLvI/ILRg2fQRkkiSl+2KgvTP5IDwferOcbTqj1+1bnoggIdsh1Ptd57vmb1ywD+ DxTs5MaUoA6SYWHEwXxHLc6P37Do5iKvrIL/H2oQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 25/37] test: yaml-parser: Replace "object" with "node" Date: Fri, 24 Apr 2026 02:00:47 +0300 Message-ID: <20260423230059.3180987-26-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Barnabás Pőcze Reviewed-by: Isaac Scott --- 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 a4b26c17e1f9..307e4ee26c68 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 failed to parse as Dictionary" << std::endl; + if (!level1Node.isDictionary()) { + std::cerr << "level1 node failed 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 element list" << std::endl; + if (!level2Node.isList() || level2Node.size() != 2) { + std::cerr << "level2 node should be a 2 element 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 failed to parse as integer list" << std::endl; + std::cerr << "The first element of level2 node failed 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 failed to parse as dictionary" << std::endl; + std::cerr << "The second element of level2 node failed to parse as dictionary" << std::endl; return TestFail; } From patchwork Thu Apr 23 23:00:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26548 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 83A94BDCB5 for ; Thu, 23 Apr 2026 23:01:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 08F7462FE1; Fri, 24 Apr 2026 01:01:55 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="CAbZ3bD/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5FC1C62FAC for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id D547B802 for ; Fri, 24 Apr 2026 00:59:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985198; bh=T8eboT/u2TPJdCqWQ9XsuhmdWtSEX4+87Y9EHhTRqsU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=CAbZ3bD/+DF7S6dPmz86lb8Vf+7AAlewzdzcfmZuLmq5v1kdmio9XA9FxuwfWTCIR HXLpNUPZ9egsLqNi2Aus4ej2WIhZ4gOgMrtJ2tx1OZpXW1XrFG6aEVOjmlavZLJDr2 52eNX2TH1ugOEF1yHtf6GLmNBFFlH97Mt30nGGxk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 26/37] libcamera: Pass CameraManager around instead of GlobalConfiguration Date: Fri, 24 Apr 2026 02:00:48 +0300 Message-ID: <20260423230059.3180987-27-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 39a0ea6ea39b..8c58775d5afc 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 644f6604eb94..fcd281f4cd69 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 Thu Apr 23 23:00: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: 26549 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 1EE98BDCB5 for ; Thu, 23 Apr 2026 23:01:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C1BA262FD2; Fri, 24 Apr 2026 01:01:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Tc97YTuc"; 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 9E84062FAC for ; Fri, 24 Apr 2026 01:01:38 +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 ESMTPSA id 1F61C802 for ; Fri, 24 Apr 2026 00:59:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985199; bh=dyPDIvJk1E5Hosf9owqHzIF7jsx1fcOwbkt0YhCtCdE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Tc97YTucx4myaDS24ulkKMK5pprbiK3MYC0xltPEQjp5GwtwnJ4747/FovqitJv9G bcWODOFZQ2Klu0R9PeNQ8F9v44aJRPNJKoqoOr2lSo4zcQ7RFmg6o1nyTu04q3V6Rc p3A5yMzfems5Z1UEaLHlkyubhAcVpAuztpTR8O+0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 27/37] libcamera: global_configuration: Reorder functions Date: Fri, 24 Apr 2026 02:00:49 +0300 Message-ID: <20260423230059.3180987-28-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- .../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 Thu Apr 23 23:00: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: 26550 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 CAB85BDCB5 for ; Thu, 23 Apr 2026 23:01:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7A66562FC5; Fri, 24 Apr 2026 01:01:57 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NLIvyShh"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DF0AC62FAC for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 5F43B802 for ; Fri, 24 Apr 2026 01:00:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985200; bh=H17CFpc7lt+X2xsRg7UVhGV+7ZOtaknrN2oERFj/RQw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=NLIvyShhwrBy72D62YwZCoIbpwl1jHTH4IgyCjIFY2JThH4A8B0FbbAr1nfbU4sRd JUcywPoUCB9Tiov6NaKcTLW+bIYt7kZ4z9jgEGh24u6cbmhq5oQKBPu3Et4BqDk1xT xMldQsXixWuaWMnFsKfEdxmmUAZw9LJLa3IidZAU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 28/37] libcamera: global_configuration: Add missing include and comment Date: Fri, 24 Apr 2026 02:00:50 +0300 Message-ID: <20260423230059.3180987-29-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00: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: 26551 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 D3B46BDCB5 for ; Thu, 23 Apr 2026 23:01:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8BA2262FC7; Fri, 24 Apr 2026 01:01:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="L2tFQ5yi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4E50462FAC for ; Fri, 24 Apr 2026 01:01:41 +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 ESMTPSA id C3B8DC77 for ; Fri, 24 Apr 2026 01:00:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985202; bh=ZDT0L58+KUXTZt85a32dO6ABbE1M5IR2fOkXve5C13E=; h=From:To:Subject:Date:In-Reply-To:References:From; b=L2tFQ5yifUPuqazdRz2nCPEJAqmfx523R9fJZZFj7PyDoqKbPDWKZMif5R5Tb/Rrg VGbnL+BTnkwftA6ybmwlhvHfgj3HywXUBGyfbwuUXee8BwyRsaC3o+tNUXOhs5pJJ8 Y5ce2Usc8zZVRljLHMO5xlJtjWnniXV1ukDBL1fc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 29/37] libcamera: global_configuration: Rename yamlConfiguration_ Date: Fri, 24 Apr 2026 02:00:51 +0300 Message-ID: <20260423230059.3180987-30-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00: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: 26552 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 8E8C3BDCB5 for ; Thu, 23 Apr 2026 23:02:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 12DA362FED; Fri, 24 Apr 2026 01:02:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kveVbI6P"; 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 9B33B62FBB for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 14738C77 for ; Fri, 24 Apr 2026 01:00:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985203; bh=efAQ9q8uv7zfxGOrEWH93/N/ngyHjpsFqaehqSZQZnI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kveVbI6PtQhmHZN4d7tR0kRT771cVaQFiZ72TZVGBQLUoTMHTkOKHfmt0Msmslo4o nGvHZvnnh9EblSUEF8opzrjeI3vmF4qXQ2UcL9FNIck+h1LlZb94phOWdD093YMguH RSxowU9X60p9FE/Z1pxqC8gPotdsJ+7RFFpFVnnw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 30/37] libcamera: global_configuration: Rename Configuration to Option Date: Fri, 24 Apr 2026 02:00:52 +0300 Message-ID: <20260423230059.3180987-31-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00: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: 26553 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 57A78BDCB5 for ; Thu, 23 Apr 2026 23:02:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E367B62FD2; Fri, 24 Apr 2026 01:02:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HE2ctQaQ"; 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 EC7ED62FD4 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 77AC3103F for ; Fri, 24 Apr 2026 01:00:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985204; bh=F+MFMICu/EPDXmepeY81IjCYxIkD50idCDxacjaI5y8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HE2ctQaQDFEm7/JDVTtwH50PUKQbgnk7UjfHFmAxq597x0j4LGtnrGq6r+lW+NJi+ CBFVRaa7G26xg9IZoU0NbvFVAdrG2r+W6rfa7NqUUwVNJWA2hG4VndwpNMVqf0MdRH 7wXY3edSH+P6bcE+xx8fNSmjYqvq2Yr++H+rBZuM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 31/37] libcamera: global_configuration: Drop Option type Date: Fri, 24 Apr 2026 02:00:53 +0300 Message-ID: <20260423230059.3180987-32-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Barnabás Pőcze Reviewed-by: Isaac Scott --- Changes since v2: - Drop note from GlobalConfiguration::version() documentation --- include/libcamera/internal/global_configuration.h | 4 +--- src/libcamera/global_configuration.cpp | 15 ++------------- src/libcamera/pipeline/simple/simple.cpp | 2 +- 3 files changed, 4 insertions(+), 17 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..a70562614b12 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 */ @@ -153,12 +145,9 @@ unsigned int GlobalConfiguration::version() const * 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 top-level configuration option + * \return The global configuration */ -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 Thu Apr 23 23:00: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: 26554 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 4E0EFBDCB5 for ; Thu, 23 Apr 2026 23:02:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B574062FB7; Fri, 24 Apr 2026 01:02:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="cjghgjJn"; 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 5240362FD1 for ; Fri, 24 Apr 2026 01:01:45 +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 ESMTPSA id C15ED103F for ; Fri, 24 Apr 2026 01:00:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985205; bh=3SF+tBB3vleQvw6gacY2li+u7UTo/rNJT8YeOKI+U+k=; h=From:To:Subject:Date:In-Reply-To:References:From; b=cjghgjJnVWGlyAjrkXAz+3Ek3bvGTT3ZOo7NN0WhPyITqMMlfVoKm/9xwDMBOgYW3 cK2Qxx1S/gXVmOfaFyr+0oYdbsMXfUZwM4UUF01Kh1CJcNxqXVBvj6ejlyQl35NVJL loXgvegeoCgQzblEM8/0rVukNfpXvLOcqSdoEGqU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 32/37] libcamera: global_configuration: Populate empty configuration Date: Fri, 24 Apr 2026 02:00:54 +0300 Message-ID: <20260423230059.3180987-33-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Barnabás Pőcze Reviewed-by: Isaac Scott --- 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 a70562614b12..62b9762d3e31 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 Thu Apr 23 23:00: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: 26555 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 231FCBDCB5 for ; Thu, 23 Apr 2026 23:02:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A34EA62FDD; Fri, 24 Apr 2026 01:02:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aO9tfmWP"; 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 D876362F6C for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 5185B19A8 for ; Fri, 24 Apr 2026 01:00:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985207; bh=45pUlmggSMulllW/YOeKF1iAAsL4nW4JplvthcCldwg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=aO9tfmWPL8a9IJBWJr6YxQw/zN5Fq6zYlU86JHqRJe2vu3dLuveEjFy/ul6vGZ5a5 CHeCy7nfvwXg2MSHTjpcbfY/6VIVyxHcEC4PaH6sBw9CxvgM9fO5Pzaxz/B2XVKwHN /zTtiBNEtq4SHYR2RFINiyDBeMz/gC6PMnls+Q84= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 33/37] libcamera: global_configuration: Override options with environment variables Date: Fri, 24 Apr 2026 02:00:55 +0300 Message-ID: <20260423230059.3180987-34-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott Reviewed-by: Barnabás Pőcze --- 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 62b9762d3e31..46a285c3c4ea 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() @@ -185,65 +298,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 Thu Apr 23 23:00:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26556 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 DC2B7BDCB5 for ; Thu, 23 Apr 2026 23:02:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8CBE362FE2; Fri, 24 Apr 2026 01:02:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="e0Mx0EQI"; 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 37D5062FD9 for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id AEABB1A4A for ; Fri, 24 Apr 2026 01:00:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985208; bh=GkdyjVIOyzu54fTsbz2vMeCOv7Q7SPdvSM8BMRCzbGQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=e0Mx0EQIRcMJk7/tY9mYHZVW7ktPrNv8I3X5hWlZRAqGldNZDjrALhfybFMIJry/Q cmHftwNi/UAFztIDOLlp1Mb8n69tB9RRslNxfHBcQ7LZGQqJl6R6NiYyUU89taT9TO a2VSbF1w+nBtzvH1DT7t5C1FUYQ/iHkb+tAOZ61c= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 34/37] Documentation: Rename runtime configuration title Date: Fri, 24 Apr 2026 02:00:56 +0300 Message-ID: <20260423230059.3180987-35-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26557 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 BC638BDCB5 for ; Thu, 23 Apr 2026 23:02:07 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 613BD62FEA; Fri, 24 Apr 2026 01:02:07 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UdOgjiYl"; 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 7DF4262FDA for ; Fri, 24 Apr 2026 01:01: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 ESMTPSA id 062141A4A for ; Fri, 24 Apr 2026 01:00:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985210; bh=/gVnENFzXG3dl4/tzf6ezmch6NTH4Oh9HOUdLsW9/JU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=UdOgjiYlM7dbyZI02rvLhpAs9IlDJw1L4EP1zsQeyLhlZ1rgq1BBBMHc4fwzbUqbp NYqCkw2o2u8JCmde6Rys64f2sOT40aW8UBQenyox4I/CpObNFcuJT9xwPo8cF/f0pa ID5zcJ1yI8LDn+3mVC0yvbst/IjO6hFoDJl/5wrE= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 35/37] libcamera: software_isp: Rename "measure" option to "benchmark" Date: Fri, 24 Apr 2026 02:00:57 +0300 Message-ID: <20260423230059.3180987-36-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00:58 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26558 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 96D53BDCB5 for ; Thu, 23 Apr 2026 23:02:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 41B3362FED; Fri, 24 Apr 2026 01:02:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZNzHmJYh"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EA48062FDC for ; Fri, 24 Apr 2026 01:01:50 +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 ESMTPSA id 782F01283 for ; Fri, 24 Apr 2026 01:00:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985211; bh=5b5Us//VC9ngZ8EFKZcwDDPkvfzdu56EO8WwJv/8lRI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ZNzHmJYhQ9mSUjVpujSKOfJmpaMuP0s0VI3qAlQ8gNtDTWn31uatbZAF2C4t9S8cx v/MldPANLFBdOAD9f+aNGQFrtdC+1QtdWaFOIwy0lTaqoGMCwSGZlJOCmfMRPQhefP Aqi1f98zF61nVnjS8RvrsEUJM2G1ouz0MB89sw1w= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 36/37] pipeline: simple: Rename supported_devices configuration option to devices Date: Fri, 24 Apr 2026 02:00:58 +0300 Message-ID: <20260423230059.3180987-37-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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 Thu Apr 23 23:00:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26559 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 696A7BDCB5 for ; Thu, 23 Apr 2026 23:02:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 118A962FE5; Fri, 24 Apr 2026 01:02:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="S0UQc4iw"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3B32662FC2 for ; Fri, 24 Apr 2026 01:01:52 +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 ESMTPSA id B29F61A4A for ; Fri, 24 Apr 2026 01:00:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985212; bh=p2cC2HRZib4lYgISO2fuyANTT9PKHDu0z0H6ABaNSVQ=; h=From:To:Subject:Date:In-Reply-To:References:From; b=S0UQc4iwtLJTa8alVw14cnAYoZJCOeE8nSuMVmADZvc0t/5sqVMU6BB5mt3deH1Ey 9JeO3WK1RCyDQeRUvYBUudw4GEpN3uhs2E0PI423c8s/VPGssGmRBf9ownIJBTML1d EC7CW4xqCVGAZgtDHKgqXrzJRMxpcqUBbcOy7xGg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 37/37] pipeline: simple: Turn devices configuration option into dictionary Date: Fri, 24 Apr 2026 02:00:59 +0300 Message-ID: <20260423230059.3180987-38-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-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 Reviewed-by: Isaac Scott --- 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. */