[{"id":24080,"web_url":"https://patchwork.libcamera.org/comment/24080/","msgid":"<YtycBdv7vut70BfM@pendragon.ideasonboard.com>","date":"2022-07-24T01:10:29","subject":"Re: [libcamera-devel] [PATCH v2 1/5] libcamera: yaml_parser: Add\n\tgetList() function","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Florian,\n\nThank you for the patch.\n\nOn Fri, Jul 22, 2022 at 05:16:31PM +0200, Florian Sylvestre via libcamera-devel wrote:\n> Allow to retrieve a YAML list of any already supported types in a std::vector.\n> \n> Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> ---\n>  include/libcamera/internal/yaml_parser.h | 16 ++++++\n>  src/libcamera/yaml_parser.cpp            | 66 ++++++++++++++++++++++++\n>  test/yaml-parser.cpp                     |  6 +++\n>  3 files changed, 88 insertions(+)\n> \n> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> index 064cf443..39161bbb 100644\n> --- a/include/libcamera/internal/yaml_parser.h\n> +++ b/include/libcamera/internal/yaml_parser.h\n> @@ -167,6 +167,22 @@ public:\n>  #endif\n>  \tT get(const T &defaultValue, bool *ok = nullptr) const;\n>  \n> +#ifndef __DOXYGEN__\n> +\ttemplate<typename T,\n> +\t\t typename std::enable_if_t<\n> +\t\t\t std::is_same_v<bool, T> ||\n> +\t\t\t std::is_same_v<double, T> ||\n> +\t\t\t std::is_same_v<int16_t, T> ||\n> +\t\t\t std::is_same_v<uint16_t, T> ||\n> +\t\t\t std::is_same_v<int32_t, T> ||\n> +\t\t\t std::is_same_v<uint32_t, T> ||\n> +\t\t\t std::is_same_v<std::string, T> ||\n> +\t\t\t std::is_same_v<Size, T>> * = nullptr>\n> +#else\n> +\ttemplate<typename T>\n> +#endif\n> +\tstd::vector<T> getList(bool *ok = nullptr) const;\n> +\n>  \tDictAdapter asDict() const { return DictAdapter{ dictionary_ }; }\n>  \tListAdapter asList() const { return ListAdapter{ list_ }; }\n>  \n> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> index 5c45e44e..125a348e 100644\n> --- a/src/libcamera/yaml_parser.cpp\n> +++ b/src/libcamera/yaml_parser.cpp\n> @@ -325,6 +325,72 @@ Size YamlObject::get(const Size &defaultValue, bool *ok) const\n>  \n>  #endif /* __DOXYGEN__ */\n>  \n> +/**\n> + * \\fn template<typename T> YamlObject::getList<T>(bool *ok) const\n> + * \\brief Parse the YamlObject as a list of \\a T\n> + * \\param[out] ok The result of whether the parse succeeded\n> + *\n> + * This function parses the value of the YamlObject as a list of \\a T objects,\n> + * and returns the value as a \\a std::vector<T>. If parsing fails \\a ok is set\n> + * to false and a default empty vector is returned. Otherwise, the\n> + * YamlObject list of values is returned, and \\a ok is set to true.\n> + *\n> + * The \\a ok pointer is optional and can be a nullptr if the caller doesn't\n> + * need to know if parsing succeeded.\n> + *\n> + * \\return Value as a std::vector<T> type\n> + */\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +template<typename T,\n> +\t typename std::enable_if_t<\n> +\t\t std::is_same_v<bool, T> ||\n> +\t\t std::is_same_v<double, T> ||\n> +\t\t std::is_same_v<int16_t, T> ||\n> +\t\t std::is_same_v<uint16_t, T> ||\n> +\t\t std::is_same_v<int32_t, T> ||\n> +\t\t std::is_same_v<uint32_t, T> ||\n> +\t\t std::is_same_v<std::string, T> ||\n> +\t\t std::is_same_v<Size, T>> *>\n> +std::vector<T> YamlObject::getList(bool *ok) const\n> +{\n> +\tsetOk(ok, false);\n> +\n> +\tif (type_ != Type::List)\n> +\t\treturn {};\n> +\n> +\tstd::vector<T> value;\n> +\tvalue.reserve(list_.size());\n> +\n> +\t/*\n> +\t * Provide somme dummy defaultValue to get() function and rely on the\n> +\t * ok variable to know if the parsing was correct.\n> +\t */\n> +\tT defaultValue{};\n> +\n> +\tfor (const YamlObject &entry : asList()) {\n> +\t\tbool result;\n> +\t\tvalue.emplace_back(entry.get<T>(defaultValue, &result));\n> +\t\tif (!result)\n> +\t\t\treturn {};\n> +\t}\n> +\n> +\tsetOk(ok, true);\n> +\treturn value;\n> +}\n> +\n> +template std::vector<bool> YamlObject::getList<bool>(bool *ok) const;\n> +template std::vector<double> YamlObject::getList<double>(bool *ok) const;\n> +template std::vector<int16_t> YamlObject::getList<int16_t>(bool *ok) const;\n> +template std::vector<uint16_t> YamlObject::getList<uint16_t>(bool *ok) const;\n> +template std::vector<int32_t> YamlObject::getList<int32_t>(bool *ok) const;\n> +template std::vector<uint32_t> YamlObject::getList<uint32_t>(bool *ok) const;\n> +template std::vector<std::string> YamlObject::getList<std::string>(bool *ok) const;\n> +template std::vector<Size> YamlObject::getList<Size>(bool *ok) const;\n> +\n> +#endif /* __DOXYGEN__ */\n> +\n>  /**\n>   * \\fn YamlObject::asDict() const\n>   * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n> diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp\n> index 38f84823..bb96a11b 100644\n> --- a/test/yaml-parser.cpp\n> +++ b/test/yaml-parser.cpp\n> @@ -524,6 +524,12 @@ protected:\n>  \t\t\treturn TestFail;\n>  \t\t}\n>  \n> +\t\tstd::vector<uint16_t> values = firstElement.getList<uint16_t>();\n> +\t\tif (values.size() != 2 || values[0] != 1 || values[1] != 2) {\n> +\t\t\tcerr << \"getList() failed to return correct vector\" << std::endl;\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n>  \t\tauto &secondElement = level2Obj[1];\n>  \t\tif (!secondElement.isDictionary() ||\n>  \t\t    !secondElement.contains(\"one\") ||","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9C45EBE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 24 Jul 2022 01:10:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CE4DE63312;\n\tSun, 24 Jul 2022 03:10:34 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 87D83603EB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 24 Jul 2022 03:10:33 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id E1AF0835;\n\tSun, 24 Jul 2022 03:10:32 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1658625034;\n\tbh=OLTDf7S8Ifb9TuWYgmawUQcRct5sFqePLStU5PPYg9Q=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=dLEVUErcuaP1n5IfOuRGe8rMZFpf91KOFZt/tUw+iKHPmiUWRK+YVjZYd1kYTcz3O\n\tN1OJmpYjL0F5XrxFcPLta2Mx0Rk++D+qdk7WiVyKA7OkYp/431lAvfwN/798H58BpE\n\tow7H98GFes+t1ZG9P3ybVhS7grlGhTJ92QeOLmpWk45sluXp56FJBdGVTSPwNhVzqg\n\tHICvn1KrNHIe5sLJRsUO+y0SPFcpKQEulhr1Ey9ugB5dFLnr3GVtvWEXNBTfDzDpUr\n\tORcKHBQRKyVcl+Zgub4H7O417kHE7pKRyb/FV/qMPnMaaf7RcosJAVhhkp2VJobBBZ\n\t/cuNHsLEnjAdg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1658625033;\n\tbh=OLTDf7S8Ifb9TuWYgmawUQcRct5sFqePLStU5PPYg9Q=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=WustemMQWxUA7aiUQgE4JOtck3T6WXBK99+VnL0PZT93MFPItOK8Mawk84FfVw/cL\n\tPsGGl0YX8OJRkrxtQymUNW7IA7txrXjDLirovf3Xb3SFA+TFKZpFr7nWXf1RxWy/0K\n\tRciSpFEDXk38JaNjQfemCSDtbW20UKHuExkzomnA="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"WustemMQ\"; dkim-atps=neutral","Date":"Sun, 24 Jul 2022 04:10:29 +0300","To":"Florian Sylvestre <fsylvestre@baylibre.com>","Message-ID":"<YtycBdv7vut70BfM@pendragon.ideasonboard.com>","References":"<20220722151635.239221-1-fsylvestre@baylibre.com>\n\t<20220722151635.239221-2-fsylvestre@baylibre.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220722151635.239221-2-fsylvestre@baylibre.com>","Subject":"Re: [libcamera-devel] [PATCH v2 1/5] libcamera: yaml_parser: Add\n\tgetList() function","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24087,"web_url":"https://patchwork.libcamera.org/comment/24087/","msgid":"<20220725073904.GJ3984498@pyrite.rasen.tech>","date":"2022-07-25T07:39:04","subject":"Re: [libcamera-devel] [PATCH v2 1/5] libcamera: yaml_parser: Add\n\tgetList() function","submitter":{"id":97,"url":"https://patchwork.libcamera.org/api/people/97/","name":"Nicolas Dufresne via libcamera-devel","email":"libcamera-devel@lists.libcamera.org"},"content":"On Fri, Jul 22, 2022 at 05:16:31PM +0200, Florian Sylvestre via libcamera-devel wrote:\n> Allow to retrieve a YAML list of any already supported types in a std::vector.\n> \n> Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> ---\n>  include/libcamera/internal/yaml_parser.h | 16 ++++++\n>  src/libcamera/yaml_parser.cpp            | 66 ++++++++++++++++++++++++\n>  test/yaml-parser.cpp                     |  6 +++\n>  3 files changed, 88 insertions(+)\n> \n> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> index 064cf443..39161bbb 100644\n> --- a/include/libcamera/internal/yaml_parser.h\n> +++ b/include/libcamera/internal/yaml_parser.h\n> @@ -167,6 +167,22 @@ public:\n>  #endif\n>  \tT get(const T &defaultValue, bool *ok = nullptr) const;\n>  \n> +#ifndef __DOXYGEN__\n> +\ttemplate<typename T,\n> +\t\t typename std::enable_if_t<\n> +\t\t\t std::is_same_v<bool, T> ||\n> +\t\t\t std::is_same_v<double, T> ||\n> +\t\t\t std::is_same_v<int16_t, T> ||\n> +\t\t\t std::is_same_v<uint16_t, T> ||\n> +\t\t\t std::is_same_v<int32_t, T> ||\n> +\t\t\t std::is_same_v<uint32_t, T> ||\n> +\t\t\t std::is_same_v<std::string, T> ||\n> +\t\t\t std::is_same_v<Size, T>> * = nullptr>\n> +#else\n> +\ttemplate<typename T>\n> +#endif\n> +\tstd::vector<T> getList(bool *ok = nullptr) const;\n> +\n>  \tDictAdapter asDict() const { return DictAdapter{ dictionary_ }; }\n>  \tListAdapter asList() const { return ListAdapter{ list_ }; }\n>  \n> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> index 5c45e44e..125a348e 100644\n> --- a/src/libcamera/yaml_parser.cpp\n> +++ b/src/libcamera/yaml_parser.cpp\n> @@ -325,6 +325,72 @@ Size YamlObject::get(const Size &defaultValue, bool *ok) const\n>  \n>  #endif /* __DOXYGEN__ */\n>  \n> +/**\n> + * \\fn template<typename T> YamlObject::getList<T>(bool *ok) const\n> + * \\brief Parse the YamlObject as a list of \\a T\n> + * \\param[out] ok The result of whether the parse succeeded\n> + *\n> + * This function parses the value of the YamlObject as a list of \\a T objects,\n> + * and returns the value as a \\a std::vector<T>. If parsing fails \\a ok is set\n> + * to false and a default empty vector is returned. Otherwise, the\n> + * YamlObject list of values is returned, and \\a ok is set to true.\n> + *\n> + * The \\a ok pointer is optional and can be a nullptr if the caller doesn't\n> + * need to know if parsing succeeded.\n> + *\n> + * \\return Value as a std::vector<T> type\n> + */\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +template<typename T,\n> +\t typename std::enable_if_t<\n> +\t\t std::is_same_v<bool, T> ||\n> +\t\t std::is_same_v<double, T> ||\n> +\t\t std::is_same_v<int16_t, T> ||\n> +\t\t std::is_same_v<uint16_t, T> ||\n> +\t\t std::is_same_v<int32_t, T> ||\n> +\t\t std::is_same_v<uint32_t, T> ||\n> +\t\t std::is_same_v<std::string, T> ||\n> +\t\t std::is_same_v<Size, T>> *>\n> +std::vector<T> YamlObject::getList(bool *ok) const\n> +{\n> +\tsetOk(ok, false);\n> +\n> +\tif (type_ != Type::List)\n> +\t\treturn {};\n> +\n> +\tstd::vector<T> value;\n> +\tvalue.reserve(list_.size());\n> +\n> +\t/*\n> +\t * Provide somme dummy defaultValue to get() function and rely on the\n> +\t * ok variable to know if the parsing was correct.\n> +\t */\n> +\tT defaultValue{};\n> +\n> +\tfor (const YamlObject &entry : asList()) {\n> +\t\tbool result;\n> +\t\tvalue.emplace_back(entry.get<T>(defaultValue, &result));\n> +\t\tif (!result)\n> +\t\t\treturn {};\n> +\t}\n> +\n> +\tsetOk(ok, true);\n> +\treturn value;\n> +}\n> +\n> +template std::vector<bool> YamlObject::getList<bool>(bool *ok) const;\n> +template std::vector<double> YamlObject::getList<double>(bool *ok) const;\n> +template std::vector<int16_t> YamlObject::getList<int16_t>(bool *ok) const;\n> +template std::vector<uint16_t> YamlObject::getList<uint16_t>(bool *ok) const;\n> +template std::vector<int32_t> YamlObject::getList<int32_t>(bool *ok) const;\n> +template std::vector<uint32_t> YamlObject::getList<uint32_t>(bool *ok) const;\n> +template std::vector<std::string> YamlObject::getList<std::string>(bool *ok) const;\n> +template std::vector<Size> YamlObject::getList<Size>(bool *ok) const;\n> +\n> +#endif /* __DOXYGEN__ */\n> +\n>  /**\n>   * \\fn YamlObject::asDict() const\n>   * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n> diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp\n> index 38f84823..bb96a11b 100644\n> --- a/test/yaml-parser.cpp\n> +++ b/test/yaml-parser.cpp\n> @@ -524,6 +524,12 @@ protected:\n>  \t\t\treturn TestFail;\n>  \t\t}\n>  \n> +\t\tstd::vector<uint16_t> values = firstElement.getList<uint16_t>();\n> +\t\tif (values.size() != 2 || values[0] != 1 || values[1] != 2) {\n> +\t\t\tcerr << \"getList() failed to return correct vector\" << std::endl;\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n>  \t\tauto &secondElement = level2Obj[1];\n>  \t\tif (!secondElement.isDictionary() ||\n>  \t\t    !secondElement.contains(\"one\") ||\n> -- \n> 2.34.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C7C66C3275\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 25 Jul 2022 07:39:15 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D064E63312;\n\tMon, 25 Jul 2022 09:39:14 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C5B8963309\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 25 Jul 2022 09:39:12 +0200 (CEST)","from pyrite.rasen.tech (h175-177-042-159.catv02.itscom.jp\n\t[175.177.42.159])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 583C46DD;\n\tMon, 25 Jul 2022 09:39:11 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1658734754;\n\tbh=1agAC96FZk5Oq88RwANJuQe4NgLjwI1o00toIX3cqBE=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=yl2eurJYKZcfOKKnYO1JJRLoB/DqJ2kDlOUdNdzWO3HIxLUltpcqGLoYnqs/DSKg1\n\tFQzJ1Wgj3QdhzrPoexkGZI1ajmqhvqXS1thEgLB4esRtoMg3fzL9M2J1SI6YbeYeHj\n\ti57LN2iYIJQYZTANs5FQDMSNrKKwO8IqQdH3V+CTMWGYigwULSV/wbbEAuERCorHUN\n\tiyJLjYORXdDPxwnzwUUIYElASttAXs3j5P23GirgsDlvjMl17Ukz78OqG+cEdXWULp\n\tAIA26xhG19VPEBm9QVpTeHiOjgvE7IiSOBrabwOFzrs8g4SiYMgu9ohkBOnO73cctZ\n\t9C15il17bAP6A==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1658734752;\n\tbh=1agAC96FZk5Oq88RwANJuQe4NgLjwI1o00toIX3cqBE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=KTIQiuWrunF8a3ReHnJTKrFeyUpqr/WVmXZ2uVy6PzP85LVEXYXiFb5Y/LVtZn31U\n\tnmHWU9gPRPCmJyTDjLcaE/fUMudcTOWiWhkD0FRUvtbcCILZGK4TCNc59713NT7eCd\n\t5uyCyIM6XR2SfDQB0voZyZnOGbmUrmfq/GsOx7F8="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"KTIQiuWr\"; dkim-atps=neutral","Date":"Mon, 25 Jul 2022 16:39:04 +0900","To":"Florian Sylvestre <fsylvestre@baylibre.com>","Message-ID":"<20220725073904.GJ3984498@pyrite.rasen.tech>","References":"<20220722151635.239221-1-fsylvestre@baylibre.com>\n\t<20220722151635.239221-2-fsylvestre@baylibre.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20220722151635.239221-2-fsylvestre@baylibre.com>","Subject":"Re: [libcamera-devel] [PATCH v2 1/5] libcamera: yaml_parser: Add\n\tgetList() function","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Paul Elder via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"paul.elder@ideasonboard.com","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]