[{"id":25809,"web_url":"https://patchwork.libcamera.org/comment/25809/","msgid":"<20221117092950.3buz4y6allxfdtnx@uno.localdomain>","date":"2022-11-17T09:29:50","subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Xavier\n\nOn Tue, Nov 15, 2022 at 01:59:33PM +0100, Xavier Roumegue wrote:\n> Declare a converter Abstract Base Class intended to provide generic\n> interfaces to hardware offering size and format conversion services on\n> streams. This is mainly based on the public interfaces of the current\n> converter class implementation found in the simple pipeline handler.\n>\n> The main change is the introduction of loadConfiguration() function\n> which can be used by the concrete implementation to load hardware\n> specific runtime parameters defined by the application.\n>\n> Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n> ---\n>  include/libcamera/internal/converter.h | 109 ++++++++\n>  include/libcamera/internal/meson.build |   1 +\n>  src/libcamera/converter.cpp            | 331 +++++++++++++++++++++++++\n>  src/libcamera/meson.build              |   1 +\n>  4 files changed, 442 insertions(+)\n>  create mode 100644 include/libcamera/internal/converter.h\n>  create mode 100644 src/libcamera/converter.cpp\n>\n> diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h\n> new file mode 100644\n> index 00000000..19e8c7c5\n> --- /dev/null\n> +++ b/include/libcamera/internal/converter.h\n> @@ -0,0 +1,109 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Laurent Pinchart\n> + * Copyright 2022 NXP\n> + *\n> + * converter.h - Generic stream converter interface\n\nAs commented in v1, please align with this with the .cpp file\n\nIf not other comments, it can be fixed when applying\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n  j\n\n\n> + */\n> +\n> +#pragma once\n> +\n> +#include <initializer_list>\n> +#include <map>\n> +#include <memory>\n> +#include <string>\n> +#include <tuple>\n> +#include <vector>\n> +\n> +#include <libcamera/base/class.h>\n> +#include <libcamera/base/signal.h>\n> +\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/pixel_format.h>\n> +\n> +namespace libcamera {\n> +\n> +class FrameBuffer;\n> +class MediaDevice;\n> +class Size;\n> +class SizeRange;\n> +struct StreamConfiguration;\n> +\n> +class Converter\n> +{\n> +public:\n> +\tConverter(MediaDevice *media);\n> +\tvirtual ~Converter();\n> +\n> +\tvirtual int loadConfiguration(const std::string &filename) = 0;\n> +\n> +\tvirtual bool isValid() const = 0;\n> +\n> +\tvirtual std::vector<PixelFormat> formats(PixelFormat input) = 0;\n> +\tvirtual SizeRange sizes(const Size &input) = 0;\n> +\n> +\tvirtual std::tuple<unsigned int, unsigned int>\n> +\tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;\n> +\n> +\tvirtual int configure(const StreamConfiguration &inputCfg,\n> +\t\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;\n> +\tvirtual int exportBuffers(unsigned int output, unsigned int count,\n> +\t\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;\n> +\n> +\tvirtual int start() = 0;\n> +\tvirtual void stop() = 0;\n> +\n> +\tvirtual int queueBuffers(FrameBuffer *input,\n> +\t\t\t\t const std::map<unsigned int, FrameBuffer *> &outputs) = 0;\n> +\n> +\tSignal<FrameBuffer *> inputBufferReady;\n> +\tSignal<FrameBuffer *> outputBufferReady;\n> +\n> +\tconst std::string &deviceNode() const { return deviceNode_; };\n> +\n> +private:\n> +\tstd::string deviceNode_;\n> +};\n> +\n> +class ConverterFactoryBase\n> +{\n> +public:\n> +\tConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases);\n> +\tvirtual ~ConverterFactoryBase() = default;\n> +\n> +\tconst std::vector<std::string> &aliases() const { return aliases_; }\n> +\n> +\tstatic std::unique_ptr<Converter> create(MediaDevice *media);\n> +\tstatic std::vector<ConverterFactoryBase *> &factories();\n> +\tstatic std::vector<std::string> names();\n> +\n> +private:\n> +\tLIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase)\n> +\n> +\tstatic void registerType(ConverterFactoryBase *factory);\n> +\n> +\tvirtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0;\n> +\n> +\tstd::string name_;\n> +\tstd::vector<std::string> aliases_;\n> +};\n> +\n> +template<typename _Converter>\n> +class ConverterFactory : public ConverterFactoryBase\n> +{\n> +public:\n> +\tConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n> +\t\t: ConverterFactoryBase(name, aliases)\n> +\t{\n> +\t}\n> +\n> +\tstd::unique_ptr<Converter> createInstance(MediaDevice *media) const override\n> +\t{\n> +\t\treturn std::make_unique<_Converter>(media);\n> +\t}\n> +};\n> +\n> +#define REGISTER_CONVERTER(name, converter, ...) \\\n> +\tstatic ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ });\n> +\n> +} /* namespace libcamera */\n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index 7a780d48..8f50d755 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -19,6 +19,7 @@ libcamera_internal_headers = files([\n>      'camera_sensor_properties.h',\n>      'control_serializer.h',\n>      'control_validator.h',\n> +    'converter.h',\n>      'delayed_controls.h',\n>      'device_enumerator.h',\n>      'device_enumerator_sysfs.h',\n> diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp\n> new file mode 100644\n> index 00000000..e370038f\n> --- /dev/null\n> +++ b/src/libcamera/converter.cpp\n> @@ -0,0 +1,331 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright 2022 NXP\n> + *\n> + * converter.cpp - Generic Format converter interface\n> + */\n> +\n> +#include \"libcamera/internal/converter.h\"\n> +\n> +#include <algorithm>\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +#include \"libcamera/internal/media_device.h\"\n> +\n> +/**\n> + * \\file internal/converter.h\n> + * \\brief Abstract converter\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(Converter)\n> +\n> +/**\n> + * \\class Converter\n> + * \\brief Abstract Base Class for converter\n> + *\n> + * The Converter class is an Abstract Base Class defining the interfaces of\n> + * converter implementations.\n> + *\n> + * Converters offer scaling and pixel format conversion services on a input\n> + * stream. The converter can output multiple streams with individual conversion\n> + * parameters from the same input stream.\n> + */\n> +\n> +/**\n> + * \\brief Construct a Converter instance\n> + * \\param[in] media The media device implementing the converter\n> + *\n> + * This seeks for the entity implementing data streaming function in the media\n> + * graph entities and use its device node as converter device node.\n> + */\n> +Converter::Converter(MediaDevice *media)\n> +{\n> +\tconst std::vector<MediaEntity *> &entities = media->entities();\n> +\tauto it = std::find_if(entities.begin(), entities.end(),\n> +\t\t\t       [](MediaEntity *entity) {\n> +\t\t\t\t       return entity->function() == MEDIA_ENT_F_IO_V4L;\n> +\t\t\t       });\n> +\tif (it == entities.end()) {\n> +\t\tLOG(Converter, Error)\n> +\t\t\t<< \"No entity suitable for implementing a converter in \"\n> +\t\t\t<< media->driver() << \" entities list.\";\n> +\t\treturn;\n> +\t}\n> +\n> +\tdeviceNode_ = (*it)->deviceNode();\n> +}\n> +\n> +Converter::~Converter()\n> +{\n> +}\n> +\n> +/**\n> + * \\fn Converter::loadConfiguration()\n> + * \\brief Load converter configuration from file\n> + * \\param[in] filename The file name path\n> + *\n> + * Load converter dependent configuration parameters to apply on the hardware.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +\n> +/**\n> + * \\fn Converter::isValid()\n> + * \\brief Check if the converter configuration is valid\n> + * \\return True is the converter is valid, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn Converter::formats()\n> + * \\brief Retrieve the list of supported pixel formats for an input pixel format\n> + * \\param[in] input Input pixel format to retrieve output pixel format list for\n> + * \\return The list of supported output pixel formats\n> + */\n> +\n> +/**\n> + * \\fn Converter::sizes()\n> + * \\brief Retrieve the range of minimum and maximum output sizes for an input size\n> + * \\param[in] input Input stream size to retrieve range for\n> + * \\return A range of output image sizes\n> + */\n> +\n> +/**\n> + * \\fn Converter::strideAndFrameSize()\n> + * \\brief Retrieve the output stride and frame size for an input configutation\n> + * \\param[in] pixelFormat Input stream pixel format\n> + * \\param[in] size Input stream size\n> + * \\return A tuple indicating the stride and frame size or an empty tuple on error\n> + */\n> +\n> +/**\n> + * \\fn Converter::configure()\n> + * \\brief Configure a set of output stream conversion from an input stream\n> + * \\param[in] inputCfg Input stream configuration\n> + * \\param[out] outputCfgs A list of output stream configurations\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +\n> +/**\n> + * \\fn Converter::exportBuffers()\n> + * \\brief Export buffers from the converter device\n> + * \\param[in] output Output stream index exporting the buffers\n> + * \\param[in] count Number of buffers to allocate\n> + * \\param[out] buffers Vector to store allocated buffers\n> + *\n> + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the\n> + * output stream indicated by the \\a output index.\n> + *\n> + * \\return The number of allocated buffers on success or a negative error code\n> + * otherwise\n> + */\n> +\n> +/**\n> + * \\fn Converter::start()\n> + * \\brief Start the converter streaming operation\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +\n> +/**\n> + * \\fn Converter::stop()\n> + * \\brief Stop the converter streaming operation\n> + */\n> +\n> +/**\n> + * \\fn Converter::queueBuffers()\n> + * \\brief Queue buffers to converter device\n> + * \\param[in] input The frame buffer to apply the conversion\n> + * \\param[out] outputs The container holding the output stream indexes and\n> + * their respective frame buffer outputs.\n> + *\n> + * This function queues the \\a input frame buffer on the output streams of the\n> + * \\a outputs map key and retrieve the output frame buffer indicated by the\n> + * buffer map value.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +\n> +/**\n> + * \\var Converter::inputBufferReady\n> + * \\brief A signal emitted when the input frame buffer completes\n> + */\n> +\n> +/**\n> + * \\var Converter::outputBufferReady\n> + * \\brief A signal emitted when the output frame buffer completes\n> + */\n> +\n> +/**\n> + * \\fn Converter::deviceNode()\n> + * \\brief The converter device node attribute accessor\n> + * \\return The converter device node string\n> + */\n> +\n> +/**\n> + * \\class ConverterFactoryBase\n> + * \\brief Base class for converter factories\n> + *\n> + * The ConverterFactoryBase class is the base of all specializations of the\n> + * ConverterFactory class template. It implements the factory registration,\n> + * maintains a registry of factories, and provides access to the registered\n> + * factories.\n> + */\n> +\n> +/**\n> + * \\brief Construct a converter factory base\n> + * \\param[in] name Name of the converter class\n> + * \\param[in] aliases Name aliases of the converter class\n> + *\n> + * Creating an instance of the factory base registers it with the global list of\n> + * factories, accessible through the factories() function.\n> + *\n> + * The factory \\a name is used as unique identifier. If the converter\n> + * implementation fully relies on a generic framework, the name should be the\n> + * same as the framework. Otherwise, if the implementation is specialized, the\n> + * factory name should match the driver name implementing the function.\n> + *\n> + * The factory \\a aliases holds a list of driver names implementing a generic\n> + * subsystem without any personalizations.\n> + */\n> +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases)\n> +\t: name_(name), aliases_(aliases)\n> +{\n> +\tregisterType(this);\n> +}\n> +\n> +/**\n> + * \\fn ConverterFactoryBase::aliases()\n> + * \\return The names aliases\n> + */\n> +\n> +/**\n> + * \\brief Create an instance of the converter corresponding to a named factory\n> + * \\param[in] media Name of the factory\n> + *\n> + * \\return A unique pointer to a new instance of the converter subclass\n> + * corresponding to the named factory or one of its alias. Otherwise a null\n> + * pointer if no such factory exists\n> + */\n> +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media)\n> +{\n> +\tconst std::vector<ConverterFactoryBase *> &factories =\n> +\t\tConverterFactoryBase::factories();\n> +\n> +\tfor (const ConverterFactoryBase *factory : factories) {\n> +\t\tconst std::vector<std::string> &aliases = factory->aliases();\n> +\t\tauto it = std::find(aliases.begin(), aliases.end(), media->driver());\n> +\n> +\t\tif (it == aliases.end() && media->driver() != factory->name_)\n> +\t\t\tcontinue;\n> +\n> +\t\tLOG(Converter, Debug)\n> +\t\t\t<< \"Creating converter from \"\n> +\t\t\t<< factory->name_ << \" factory with \"\n> +\t\t\t<< (it == aliases.end() ? \"no\" : media->driver()) << \" alias.\";\n> +\n> +\t\treturn factory->createInstance(media);\n> +\t}\n> +\n> +\treturn nullptr;\n> +}\n> +\n> +/**\n> + * \\brief Add a converter class to the registry\n> + * \\param[in] factory Factory to use to construct the converter class\n> + *\n> + * The caller is responsible to guarantee the uniqueness of the converter name.\n> + */\n> +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory)\n> +{\n> +\tstd::vector<ConverterFactoryBase *> &factories =\n> +\t\tConverterFactoryBase::factories();\n> +\n> +\tfactories.push_back(factory);\n> +}\n> +\n> +/**\n> + * \\brief Retrieve the list of all converter factory names\n> + * \\return The list of all converter factory names\n> + */\n> +std::vector<std::string> ConverterFactoryBase::names()\n> +{\n> +\tstd::vector<std::string> list;\n> +\n> +\tstd::vector<ConverterFactoryBase *> &factories =\n> +\t\tConverterFactoryBase::factories();\n> +\n> +\tfor (ConverterFactoryBase *factory : factories) {\n> +\t\tlist.push_back(factory->name_);\n> +\t\tfor (auto alias : factory->aliases())\n> +\t\t\tlist.push_back(alias);\n> +\t}\n> +\n> +\treturn list;\n> +}\n> +\n> +/**\n> + * \\brief Retrieve the list of all converter factories\n> + * \\return The list of converter factories\n> + */\n> +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories()\n> +{\n> +\t/*\n> +\t * The static factories map is defined inside the function to ensure\n> +\t * it gets initialized on first use, without any dependency on link\n> +\t * order.\n> +\t */\n> +\tstatic std::vector<ConverterFactoryBase *> factories;\n> +\treturn factories;\n> +}\n> +\n> +/**\n> + * \\var ConverterFactoryBase::name_\n> + * \\brief The name of the factory\n> + */\n> +\n> +/**\n> + * \\var ConverterFactoryBase::aliases_\n> + * \\brief The list holding the factory aliases\n> + */\n> +\n> +/**\n> + * \\class ConverterFactory\n> + * \\brief Registration of ConverterFactory classes and creation of instances\n> + * \\param _Converter The converter class type for this factory\n> + *\n> + * To facilitate discovery and instantiation of Converter classes, the\n> + * ConverterFactory class implements auto-registration of converter helpers.\n> + * Each Converter subclass shall register itself using the REGISTER_CONVERTER()\n> + * macro, which will create a corresponding instance of a ConverterFactory\n> + * subclass and register it with the static list of factories.\n> + */\n> +\n> +/**\n> + * \\fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n> + * \\brief Construct a converter factory\n> + * \\details \\copydetails ConverterFactoryBase::ConverterFactoryBase\n> + *\n> + */\n> +\n> +/**\n> + * \\fn ConverterFactory::createInstance() const\n> + * \\brief Create an instance of the Converter corresponding to the factory\n> + * \\param[in] media Media device pointer\n> + * \\return A unique pointer to a newly constructed instance of the Converter\n> + * subclass corresponding to the factory\n> + */\n> +\n> +/**\n> + * \\def REGISTER_CONVERTER\n> + * \\brief Register a converter with the Converter factory\n> + * \\param[in] name Converter name used to register the class\n> + * \\param[in] converter Class name of Converter derived class to register\n> + * \\param[in] ... Optional list of alias names\n> + *\n> + * Register a Converter subclass with the factory and make it available to try\n> + * and match converters.\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 0494e808..e9d0324e 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -13,6 +13,7 @@ libcamera_sources = files([\n>      'controls.cpp',\n>      'control_serializer.cpp',\n>      'control_validator.cpp',\n> +    'converter.cpp',\n>      'delayed_controls.cpp',\n>      'device_enumerator.cpp',\n>      'device_enumerator_sysfs.cpp',\n> --\n> 2.38.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 A4449BE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 17 Nov 2022 09:29:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0F3196309B;\n\tThu, 17 Nov 2022 10:29:54 +0100 (CET)","from relay11.mail.gandi.net (relay11.mail.gandi.net\n\t[IPv6:2001:4b98:dc4:8::231])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B21BE61F34\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 17 Nov 2022 10:29:52 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby mail.gandi.net (Postfix) with ESMTPSA id 8FABC100005;\n\tThu, 17 Nov 2022 09:29:51 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1668677394;\n\tbh=vmxjt9tI0gBWkyt/KU/bMLI3amfWoalDChXgeRLrhro=;\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=yoo/M8urfoXkcu4pKgSoD7hMVfPKmzJyJh4z5UtuSzLE3o+KTk/Iy3bAApXJ/BFXl\n\tSUuE2pJlOHZ+UjDLPDDoneUh1nFhUWkh9FJUk2SBOFD8Xti+Tp8JZPltzdWX88ZgKw\n\tAXAaYg2ebB5GjwqqTnyGLGyL0hTa0qoOqTLuPeHSsiwkhY5RguEZYar3EWrht8VhNH\n\tHr9pJ1BJgFV00Xgjphg+FrS8YFSe01KX3F/+pU58Hg+353NxaOzAZbDCsxz6Xm5K9n\n\tyRptRnSn4ia5HJFFkvZ0mwW1OBunCLo4bIJE1U+JeXYYZhWm6Ie+afR88dpBoewOtq\n\tbIPp2MjiaMSXA==","Date":"Thu, 17 Nov 2022 10:29:50 +0100","To":"Xavier Roumegue <xavier.roumegue@oss.nxp.com>","Message-ID":"<20221117092950.3buz4y6allxfdtnx@uno.localdomain>","References":"<20221115125935.1196138-1-xavier.roumegue@oss.nxp.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20221115125935.1196138-1-xavier.roumegue@oss.nxp.com>","Subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","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":"Jacopo Mondi via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":25817,"web_url":"https://patchwork.libcamera.org/comment/25817/","msgid":"<5f89c616-dde5-a304-f06f-5c22a03aefb3@oss.nxp.com>","date":"2022-11-17T17:50:25","subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","submitter":{"id":107,"url":"https://patchwork.libcamera.org/api/people/107/","name":"Xavier Roumegue","email":"xavier.roumegue@oss.nxp.com"},"content":"Hi Jacopo,\n\nOn 11/17/22 10:29, Jacopo Mondi wrote:\n> Hi Xavier\n> \n> On Tue, Nov 15, 2022 at 01:59:33PM +0100, Xavier Roumegue wrote:\n>> Declare a converter Abstract Base Class intended to provide generic\n>> interfaces to hardware offering size and format conversion services on\n>> streams. This is mainly based on the public interfaces of the current\n>> converter class implementation found in the simple pipeline handler.\n>>\n>> The main change is the introduction of loadConfiguration() function\n>> which can be used by the concrete implementation to load hardware\n>> specific runtime parameters defined by the application.\n>>\n>> Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n>> ---\n>>   include/libcamera/internal/converter.h | 109 ++++++++\n>>   include/libcamera/internal/meson.build |   1 +\n>>   src/libcamera/converter.cpp            | 331 +++++++++++++++++++++++++\n>>   src/libcamera/meson.build              |   1 +\n>>   4 files changed, 442 insertions(+)\n>>   create mode 100644 include/libcamera/internal/converter.h\n>>   create mode 100644 src/libcamera/converter.cpp\n>>\n>> diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h\n>> new file mode 100644\n>> index 00000000..19e8c7c5\n>> --- /dev/null\n>> +++ b/include/libcamera/internal/converter.h\n>> @@ -0,0 +1,109 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2020, Laurent Pinchart\n>> + * Copyright 2022 NXP\n>> + *\n>> + * converter.h - Generic stream converter interface\n> \n> As commented in v1, please align with this with the .cpp file\nMy bad.. I missed the stream/converter mismatch. I will fix it on v3 :(\nSorry for that\n\nRegards,\n  Xavier\n> \n> If not other comments, it can be fixed when applying\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> \n> Thanks\n>    j\n> \n> \n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <initializer_list>\n>> +#include <map>\n>> +#include <memory>\n>> +#include <string>\n>> +#include <tuple>\n>> +#include <vector>\n>> +\n>> +#include <libcamera/base/class.h>\n>> +#include <libcamera/base/signal.h>\n>> +\n>> +#include <libcamera/geometry.h>\n>> +#include <libcamera/pixel_format.h>\n>> +\n>> +namespace libcamera {\n>> +\n>> +class FrameBuffer;\n>> +class MediaDevice;\n>> +class Size;\n>> +class SizeRange;\n>> +struct StreamConfiguration;\n>> +\n>> +class Converter\n>> +{\n>> +public:\n>> +\tConverter(MediaDevice *media);\n>> +\tvirtual ~Converter();\n>> +\n>> +\tvirtual int loadConfiguration(const std::string &filename) = 0;\n>> +\n>> +\tvirtual bool isValid() const = 0;\n>> +\n>> +\tvirtual std::vector<PixelFormat> formats(PixelFormat input) = 0;\n>> +\tvirtual SizeRange sizes(const Size &input) = 0;\n>> +\n>> +\tvirtual std::tuple<unsigned int, unsigned int>\n>> +\tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;\n>> +\n>> +\tvirtual int configure(const StreamConfiguration &inputCfg,\n>> +\t\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;\n>> +\tvirtual int exportBuffers(unsigned int output, unsigned int count,\n>> +\t\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;\n>> +\n>> +\tvirtual int start() = 0;\n>> +\tvirtual void stop() = 0;\n>> +\n>> +\tvirtual int queueBuffers(FrameBuffer *input,\n>> +\t\t\t\t const std::map<unsigned int, FrameBuffer *> &outputs) = 0;\n>> +\n>> +\tSignal<FrameBuffer *> inputBufferReady;\n>> +\tSignal<FrameBuffer *> outputBufferReady;\n>> +\n>> +\tconst std::string &deviceNode() const { return deviceNode_; };\n>> +\n>> +private:\n>> +\tstd::string deviceNode_;\n>> +};\n>> +\n>> +class ConverterFactoryBase\n>> +{\n>> +public:\n>> +\tConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases);\n>> +\tvirtual ~ConverterFactoryBase() = default;\n>> +\n>> +\tconst std::vector<std::string> &aliases() const { return aliases_; }\n>> +\n>> +\tstatic std::unique_ptr<Converter> create(MediaDevice *media);\n>> +\tstatic std::vector<ConverterFactoryBase *> &factories();\n>> +\tstatic std::vector<std::string> names();\n>> +\n>> +private:\n>> +\tLIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase)\n>> +\n>> +\tstatic void registerType(ConverterFactoryBase *factory);\n>> +\n>> +\tvirtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0;\n>> +\n>> +\tstd::string name_;\n>> +\tstd::vector<std::string> aliases_;\n>> +};\n>> +\n>> +template<typename _Converter>\n>> +class ConverterFactory : public ConverterFactoryBase\n>> +{\n>> +public:\n>> +\tConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n>> +\t\t: ConverterFactoryBase(name, aliases)\n>> +\t{\n>> +\t}\n>> +\n>> +\tstd::unique_ptr<Converter> createInstance(MediaDevice *media) const override\n>> +\t{\n>> +\t\treturn std::make_unique<_Converter>(media);\n>> +\t}\n>> +};\n>> +\n>> +#define REGISTER_CONVERTER(name, converter, ...) \\\n>> +\tstatic ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ });\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n>> index 7a780d48..8f50d755 100644\n>> --- a/include/libcamera/internal/meson.build\n>> +++ b/include/libcamera/internal/meson.build\n>> @@ -19,6 +19,7 @@ libcamera_internal_headers = files([\n>>       'camera_sensor_properties.h',\n>>       'control_serializer.h',\n>>       'control_validator.h',\n>> +    'converter.h',\n>>       'delayed_controls.h',\n>>       'device_enumerator.h',\n>>       'device_enumerator_sysfs.h',\n>> diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp\n>> new file mode 100644\n>> index 00000000..e370038f\n>> --- /dev/null\n>> +++ b/src/libcamera/converter.cpp\n>> @@ -0,0 +1,331 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright 2022 NXP\n>> + *\n>> + * converter.cpp - Generic Format converter interface\n>> + */\n>> +\n>> +#include \"libcamera/internal/converter.h\"\n>> +\n>> +#include <algorithm>\n>> +\n>> +#include <libcamera/base/log.h>\n>> +\n>> +#include \"libcamera/internal/media_device.h\"\n>> +\n>> +/**\n>> + * \\file internal/converter.h\n>> + * \\brief Abstract converter\n>> + */\n>> +\n>> +namespace libcamera {\n>> +\n>> +LOG_DEFINE_CATEGORY(Converter)\n>> +\n>> +/**\n>> + * \\class Converter\n>> + * \\brief Abstract Base Class for converter\n>> + *\n>> + * The Converter class is an Abstract Base Class defining the interfaces of\n>> + * converter implementations.\n>> + *\n>> + * Converters offer scaling and pixel format conversion services on a input\n>> + * stream. The converter can output multiple streams with individual conversion\n>> + * parameters from the same input stream.\n>> + */\n>> +\n>> +/**\n>> + * \\brief Construct a Converter instance\n>> + * \\param[in] media The media device implementing the converter\n>> + *\n>> + * This seeks for the entity implementing data streaming function in the media\n>> + * graph entities and use its device node as converter device node.\n>> + */\n>> +Converter::Converter(MediaDevice *media)\n>> +{\n>> +\tconst std::vector<MediaEntity *> &entities = media->entities();\n>> +\tauto it = std::find_if(entities.begin(), entities.end(),\n>> +\t\t\t       [](MediaEntity *entity) {\n>> +\t\t\t\t       return entity->function() == MEDIA_ENT_F_IO_V4L;\n>> +\t\t\t       });\n>> +\tif (it == entities.end()) {\n>> +\t\tLOG(Converter, Error)\n>> +\t\t\t<< \"No entity suitable for implementing a converter in \"\n>> +\t\t\t<< media->driver() << \" entities list.\";\n>> +\t\treturn;\n>> +\t}\n>> +\n>> +\tdeviceNode_ = (*it)->deviceNode();\n>> +}\n>> +\n>> +Converter::~Converter()\n>> +{\n>> +}\n>> +\n>> +/**\n>> + * \\fn Converter::loadConfiguration()\n>> + * \\brief Load converter configuration from file\n>> + * \\param[in] filename The file name path\n>> + *\n>> + * Load converter dependent configuration parameters to apply on the hardware.\n>> + *\n>> + * \\return 0 on success or a negative error code otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::isValid()\n>> + * \\brief Check if the converter configuration is valid\n>> + * \\return True is the converter is valid, false otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::formats()\n>> + * \\brief Retrieve the list of supported pixel formats for an input pixel format\n>> + * \\param[in] input Input pixel format to retrieve output pixel format list for\n>> + * \\return The list of supported output pixel formats\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::sizes()\n>> + * \\brief Retrieve the range of minimum and maximum output sizes for an input size\n>> + * \\param[in] input Input stream size to retrieve range for\n>> + * \\return A range of output image sizes\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::strideAndFrameSize()\n>> + * \\brief Retrieve the output stride and frame size for an input configutation\n>> + * \\param[in] pixelFormat Input stream pixel format\n>> + * \\param[in] size Input stream size\n>> + * \\return A tuple indicating the stride and frame size or an empty tuple on error\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::configure()\n>> + * \\brief Configure a set of output stream conversion from an input stream\n>> + * \\param[in] inputCfg Input stream configuration\n>> + * \\param[out] outputCfgs A list of output stream configurations\n>> + * \\return 0 on success or a negative error code otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::exportBuffers()\n>> + * \\brief Export buffers from the converter device\n>> + * \\param[in] output Output stream index exporting the buffers\n>> + * \\param[in] count Number of buffers to allocate\n>> + * \\param[out] buffers Vector to store allocated buffers\n>> + *\n>> + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the\n>> + * output stream indicated by the \\a output index.\n>> + *\n>> + * \\return The number of allocated buffers on success or a negative error code\n>> + * otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::start()\n>> + * \\brief Start the converter streaming operation\n>> + * \\return 0 on success or a negative error code otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::stop()\n>> + * \\brief Stop the converter streaming operation\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::queueBuffers()\n>> + * \\brief Queue buffers to converter device\n>> + * \\param[in] input The frame buffer to apply the conversion\n>> + * \\param[out] outputs The container holding the output stream indexes and\n>> + * their respective frame buffer outputs.\n>> + *\n>> + * This function queues the \\a input frame buffer on the output streams of the\n>> + * \\a outputs map key and retrieve the output frame buffer indicated by the\n>> + * buffer map value.\n>> + *\n>> + * \\return 0 on success or a negative error code otherwise\n>> + */\n>> +\n>> +/**\n>> + * \\var Converter::inputBufferReady\n>> + * \\brief A signal emitted when the input frame buffer completes\n>> + */\n>> +\n>> +/**\n>> + * \\var Converter::outputBufferReady\n>> + * \\brief A signal emitted when the output frame buffer completes\n>> + */\n>> +\n>> +/**\n>> + * \\fn Converter::deviceNode()\n>> + * \\brief The converter device node attribute accessor\n>> + * \\return The converter device node string\n>> + */\n>> +\n>> +/**\n>> + * \\class ConverterFactoryBase\n>> + * \\brief Base class for converter factories\n>> + *\n>> + * The ConverterFactoryBase class is the base of all specializations of the\n>> + * ConverterFactory class template. It implements the factory registration,\n>> + * maintains a registry of factories, and provides access to the registered\n>> + * factories.\n>> + */\n>> +\n>> +/**\n>> + * \\brief Construct a converter factory base\n>> + * \\param[in] name Name of the converter class\n>> + * \\param[in] aliases Name aliases of the converter class\n>> + *\n>> + * Creating an instance of the factory base registers it with the global list of\n>> + * factories, accessible through the factories() function.\n>> + *\n>> + * The factory \\a name is used as unique identifier. If the converter\n>> + * implementation fully relies on a generic framework, the name should be the\n>> + * same as the framework. Otherwise, if the implementation is specialized, the\n>> + * factory name should match the driver name implementing the function.\n>> + *\n>> + * The factory \\a aliases holds a list of driver names implementing a generic\n>> + * subsystem without any personalizations.\n>> + */\n>> +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases)\n>> +\t: name_(name), aliases_(aliases)\n>> +{\n>> +\tregisterType(this);\n>> +}\n>> +\n>> +/**\n>> + * \\fn ConverterFactoryBase::aliases()\n>> + * \\return The names aliases\n>> + */\n>> +\n>> +/**\n>> + * \\brief Create an instance of the converter corresponding to a named factory\n>> + * \\param[in] media Name of the factory\n>> + *\n>> + * \\return A unique pointer to a new instance of the converter subclass\n>> + * corresponding to the named factory or one of its alias. Otherwise a null\n>> + * pointer if no such factory exists\n>> + */\n>> +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media)\n>> +{\n>> +\tconst std::vector<ConverterFactoryBase *> &factories =\n>> +\t\tConverterFactoryBase::factories();\n>> +\n>> +\tfor (const ConverterFactoryBase *factory : factories) {\n>> +\t\tconst std::vector<std::string> &aliases = factory->aliases();\n>> +\t\tauto it = std::find(aliases.begin(), aliases.end(), media->driver());\n>> +\n>> +\t\tif (it == aliases.end() && media->driver() != factory->name_)\n>> +\t\t\tcontinue;\n>> +\n>> +\t\tLOG(Converter, Debug)\n>> +\t\t\t<< \"Creating converter from \"\n>> +\t\t\t<< factory->name_ << \" factory with \"\n>> +\t\t\t<< (it == aliases.end() ? \"no\" : media->driver()) << \" alias.\";\n>> +\n>> +\t\treturn factory->createInstance(media);\n>> +\t}\n>> +\n>> +\treturn nullptr;\n>> +}\n>> +\n>> +/**\n>> + * \\brief Add a converter class to the registry\n>> + * \\param[in] factory Factory to use to construct the converter class\n>> + *\n>> + * The caller is responsible to guarantee the uniqueness of the converter name.\n>> + */\n>> +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory)\n>> +{\n>> +\tstd::vector<ConverterFactoryBase *> &factories =\n>> +\t\tConverterFactoryBase::factories();\n>> +\n>> +\tfactories.push_back(factory);\n>> +}\n>> +\n>> +/**\n>> + * \\brief Retrieve the list of all converter factory names\n>> + * \\return The list of all converter factory names\n>> + */\n>> +std::vector<std::string> ConverterFactoryBase::names()\n>> +{\n>> +\tstd::vector<std::string> list;\n>> +\n>> +\tstd::vector<ConverterFactoryBase *> &factories =\n>> +\t\tConverterFactoryBase::factories();\n>> +\n>> +\tfor (ConverterFactoryBase *factory : factories) {\n>> +\t\tlist.push_back(factory->name_);\n>> +\t\tfor (auto alias : factory->aliases())\n>> +\t\t\tlist.push_back(alias);\n>> +\t}\n>> +\n>> +\treturn list;\n>> +}\n>> +\n>> +/**\n>> + * \\brief Retrieve the list of all converter factories\n>> + * \\return The list of converter factories\n>> + */\n>> +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories()\n>> +{\n>> +\t/*\n>> +\t * The static factories map is defined inside the function to ensure\n>> +\t * it gets initialized on first use, without any dependency on link\n>> +\t * order.\n>> +\t */\n>> +\tstatic std::vector<ConverterFactoryBase *> factories;\n>> +\treturn factories;\n>> +}\n>> +\n>> +/**\n>> + * \\var ConverterFactoryBase::name_\n>> + * \\brief The name of the factory\n>> + */\n>> +\n>> +/**\n>> + * \\var ConverterFactoryBase::aliases_\n>> + * \\brief The list holding the factory aliases\n>> + */\n>> +\n>> +/**\n>> + * \\class ConverterFactory\n>> + * \\brief Registration of ConverterFactory classes and creation of instances\n>> + * \\param _Converter The converter class type for this factory\n>> + *\n>> + * To facilitate discovery and instantiation of Converter classes, the\n>> + * ConverterFactory class implements auto-registration of converter helpers.\n>> + * Each Converter subclass shall register itself using the REGISTER_CONVERTER()\n>> + * macro, which will create a corresponding instance of a ConverterFactory\n>> + * subclass and register it with the static list of factories.\n>> + */\n>> +\n>> +/**\n>> + * \\fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n>> + * \\brief Construct a converter factory\n>> + * \\details \\copydetails ConverterFactoryBase::ConverterFactoryBase\n>> + *\n>> + */\n>> +\n>> +/**\n>> + * \\fn ConverterFactory::createInstance() const\n>> + * \\brief Create an instance of the Converter corresponding to the factory\n>> + * \\param[in] media Media device pointer\n>> + * \\return A unique pointer to a newly constructed instance of the Converter\n>> + * subclass corresponding to the factory\n>> + */\n>> +\n>> +/**\n>> + * \\def REGISTER_CONVERTER\n>> + * \\brief Register a converter with the Converter factory\n>> + * \\param[in] name Converter name used to register the class\n>> + * \\param[in] converter Class name of Converter derived class to register\n>> + * \\param[in] ... Optional list of alias names\n>> + *\n>> + * Register a Converter subclass with the factory and make it available to try\n>> + * and match converters.\n>> + */\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n>> index 0494e808..e9d0324e 100644\n>> --- a/src/libcamera/meson.build\n>> +++ b/src/libcamera/meson.build\n>> @@ -13,6 +13,7 @@ libcamera_sources = files([\n>>       'controls.cpp',\n>>       'control_serializer.cpp',\n>>       'control_validator.cpp',\n>> +    'converter.cpp',\n>>       'delayed_controls.cpp',\n>>       'device_enumerator.cpp',\n>>       'device_enumerator_sysfs.cpp',\n>> --\n>> 2.38.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 23E1EBD16B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 17 Nov 2022 17:50:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9305C6308F;\n\tThu, 17 Nov 2022 18:50:33 +0100 (CET)","from EUR04-HE1-obe.outbound.protection.outlook.com\n\t(mail-he1eur04on061b.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f400:fe0d::61b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 426B361F31\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 17 Nov 2022 18:50:31 +0100 (CET)","from PAXPR04MB8703.eurprd04.prod.outlook.com\n\t(2603:10a6:102:21e::22)\n\tby PAXPR04MB8576.eurprd04.prod.outlook.com (2603:10a6:102:217::18)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5813.13;\n\tThu, 17 Nov 2022 17:50:28 +0000","from PAXPR04MB8703.eurprd04.prod.outlook.com\n\t([fe80::14d3:8e4:cf07:810d]) by\n\tPAXPR04MB8703.eurprd04.prod.outlook.com\n\t([fe80::14d3:8e4:cf07:810d%3]) with mapi id 15.20.5813.020;\n\tThu, 17 Nov 2022 17:50:27 +0000"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1668707433;\n\tbh=XOjEqpqxieWjkCXHokNwWK3f1TOw+6klkhWtLo3pjAo=;\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=v1M4UeGDjGvRNXfuGXahNqFz5+J18kHg4XqYeBT2fFlaQB7eSTN81hnIa8DnkbJGe\n\tqczzoZe/UMsJfxT3JdEwD2ddRLuV9Si7zfH+Ua8gr1kmElApN3S3UMOSUJ9fCNMeWi\n\tPrKQUK7c48vKyKyukH6meVoXIkVXOkNFJ4TpSMcXABo4GkkrJBVRAv1dARhM8lmQpR\n\tR62IDjrCAlVsobnxJB1/LF+hWOowokfGwWobxvzu0WPnbIFwdsgB5QhcxKJ8dq13xU\n\tO+Ta9mnd0YAfaxNLIo+gU1IQrciqqMNBeBpv3oEKTytWbV3IbxRhbwsqVU56uj09RM\n\tgzrr5x2GAn8AQ==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=NXP1.onmicrosoft.com;\n\ts=selector2-NXP1-onmicrosoft-com;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n\tbh=JEoBrnzUXe0wCh90CScoMrLNXUlchfY+ryMh1rcGiqk=;\n\tb=V5T4BC3mz9iQLZ6ZlMLLNrltVQyZRBaDVMpqpXHE5E/0GnV3tVOU1Lh0f+OSrYB9jFRgDhkkhsbZwsOop4oicudVmZ7+Jsg9rMOaGHv/ZHQraJ0r2X33EKq3kkusQxNIR9gjD7IcnUt68jvAJJ0cjEi4TdojfCSrrWQ1MG2u1SM="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=NXP1.onmicrosoft.com\n\theader.i=@NXP1.onmicrosoft.com\n\theader.b=\"V5T4BC3m\"; dkim-atps=neutral","dkim=none (message not signed)\n\theader.d=none;dmarc=none action=none header.from=oss.nxp.com;"],"ARC-Seal":"i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n\tb=IAD1CeW/GMZG3W9hfdEyK1EJwkUnY6NachkxMSShNUNQEkoCFxMgTwIEPUwdNyRsHo6CimKPWItZsPyMI/8+Q5XxoIbfJRN9bG3JEyEhG1jKbSp0c6l4HmYy7exDFtD5ECh8MNwE7AO3OUJumpj1ifD2j+uvPAJ58Wz4F8Djw52IcgMXtLmqqf223d2fEL+E/r/KXGB692eM36UPFfYa3zcRJtnwHk4npZHrgiPH542qAPfXCLvuO1ReWuAgcWlbFWmHjWxvEb08Sysk7+hSQuB71w18CcsqDN+hF8nZr/SttQKr7emYhevZFYhB6I2XMnJZrQ4NBjHIiHExqgtXxA==","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n\ts=arcselector9901;\n\th=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n\tbh=JEoBrnzUXe0wCh90CScoMrLNXUlchfY+ryMh1rcGiqk=;\n\tb=iXxjOQD9bU+3P9qhQuq/0UjnqJt1Di5FMRKJIqmeEcvM2qgTwLdirRm0jZKGUD5FJYUdc8o+IEGyOamJcrBbvtgk6OUjn+3F2cPSZEvVT5mmf0rmenBr79wuR5uPs+QYtqne8QSISBkSEy0P4oqvGdRb8LJVoNbNIQXl8oN2zaS6YsASC0AH7TYT+l6clZ9A1fJNubgXQfF+UY5coYdaGs9cUT1yM3570eQRlgDjtVdjC+30lVVFU3Hwy0CO9dD/9f57RdmtSNLN3Oaf+seAUPK9Y9ApoqevYV1xOsv311p7ZhmgFeQLUjzxxC0660YqFBCr2HDezXlJmmeLnWL6LQ==","ARC-Authentication-Results":"i=1; mx.microsoft.com 1; spf=pass\n\tsmtp.mailfrom=oss.nxp.com;\n\tdmarc=pass action=none header.from=oss.nxp.com; \n\tdkim=pass header.d=oss.nxp.com; arc=none","Message-ID":"<5f89c616-dde5-a304-f06f-5c22a03aefb3@oss.nxp.com>","Date":"Thu, 17 Nov 2022 18:50:25 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101\n\tThunderbird/102.4.1","Content-Language":"en-US","To":"Jacopo Mondi <jacopo@jmondi.org>","References":"<20221115125935.1196138-1-xavier.roumegue@oss.nxp.com>\n\t<20221117092950.3buz4y6allxfdtnx@uno.localdomain>","In-Reply-To":"<20221117092950.3buz4y6allxfdtnx@uno.localdomain>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-ClientProxiedBy":"AM0PR10CA0036.EURPRD10.PROD.OUTLOOK.COM\n\t(2603:10a6:20b:150::16) To PAXPR04MB8703.eurprd04.prod.outlook.com\n\t(2603:10a6:102:21e::22)","MIME-Version":"1.0","X-MS-Exchange-MessageSentRepresentingType":"1","X-MS-PublicTrafficType":"Email","X-MS-TrafficTypeDiagnostic":"PAXPR04MB8703:EE_|PAXPR04MB8576:EE_","X-MS-Office365-Filtering-Correlation-Id":"8c90c3e5-e7f9-4846-117f-08dac8c4363e","X-MS-Exchange-SharedMailbox-RoutingAgent-Processed":"True","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"BCL:0;","X-Microsoft-Antispam-Message-Info":"4AEbAuzzdM3uW7lKNG95JLaRggwWjw43pA4mqNjn7uj6IGst1CYZzUl384VmN79JXmF1ulVlnWp6EWL895pnQ6mOmkj6s+JUlY+saJnY3uz7IhuY1FfIou0YTjd97s8SPlUjhan/3/1oJy46KvvcY1VCZ46JKt0fajVw40qqma7X5CXe9IGrvOE3P/3BwUJ/BBcAgoUb/kqIXF7JYNU+mNsVpgLpXycXLBc/jYt/Q+J0T4wtoR+bK1msDTE8uJYVjCpJWNyCHnb6vE24ngpIRFfAS0DPaz2bxmylJ3FiRrTtdtw1QNVBC/3IA6TFcGpYh5UlRrfLKKaT91JQqabJuehcrbo+WNSZLS2JgdcXk/YjKQ8W4qGsAR13+2br2MDUF6dOKkuAVVp8OVB64iIgmGaWhIhSZ4oToaun0XHKBJu2sKUYWPNf/LdXNdDCW9fYpmZVUXnL77NIv0E2Hi8GPtnw6DvZyPc8gkpjW3wMJp5vzNNQhkQKtlHS6oDe93UXZ8b4QNJy3UWYp+CUCBYXz33pzv4N3GRFGRGiectaGU2HEhZB4DB/iJyJCMxUHFto4y2IBqXOV80SUh05trSxQPU8xJrU8QzJNv2MSzTPDxuHObuOkCy9IUqFK6RM55MsS8zt7o8u69lstcaVXyt2X9o4vlKyvmxbRn2j/p3xM5k9wQ8H69YkEwspZPVfTe+hEm35/qmLDVtM2v5y3Fe7t+8O6wvn63i0+L+CcNpuw+1qVtNGqRbF+hxS6j68pjpZILSed/ue/v7dshJIGeNrgw==","X-Forefront-Antispam-Report":"CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n\tIPV:NLI; SFV:NSPM; H:PAXPR04MB8703.eurprd04.prod.outlook.com; PTR:;\n\tCAT:NONE; \n\tSFS:(13230022)(4636009)(39860400002)(366004)(396003)(346002)(136003)(376002)(451199015)(66899015)(478600001)(6486002)(31686004)(41300700001)(2616005)(26005)(4326008)(66556008)(66476007)(8936002)(8676002)(66946007)(6512007)(5660300002)(30864003)(186003)(55236004)(53546011)(52116002)(6506007)(6916009)(316002)(83380400001)(2906002)(86362001)(31696002)(38350700002)(38100700002)(41533002)(43740500002)(45980500001);\n\tDIR:OUT; SFP:1101; ","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"=?utf-8?q?Jomq6IFs9OhlnUGTiEz5YCoFl?=\n\t=?utf-8?q?mLYYu2No/0dIWmQowzVNqFjqGERCJHTrcE+gVnwbA2VSCegj1zphEqh7?=\n\t=?utf-8?q?TLQDxcYoqkrJmL2p2YUmcB8vFanMQAYF/wWnx1FGuWcDokeI3CJDhL8d?=\n\t=?utf-8?q?qGG3+8p1eUPrWVvfnEKP3q7r/Hp/0UWndNqz3uRp3zxJ0TmKx32UMZ9s?=\n\t=?utf-8?q?F03h8kMhyn7STTHxG72cZnE6uNZsmx8OMjcCgXKOxTnhYU3tAhQC4UGh?=\n\t=?utf-8?q?ZWqVLBvRK7CH3GQ52WgQR+DudpKotiHuN9BS7q7CBTDlNJql1fWL7zwq?=\n\t=?utf-8?q?G7jHKrHV0cSfFiKGpYPRS/5IuMrhh+Bzn/vBuET9uiz4kYlC7xNiPQtJ?=\n\t=?utf-8?q?FFwJEZmH+kTA4nmxMyGw9ugjZGYD7LE1iYMkc9TevqiwN7M9eduSfjJ/?=\n\t=?utf-8?q?jUeHr06oI/cW9R1F1xrLAR3mMKJYVr+NqArGhjiTx93AtiAGPGnXV0UQ?=\n\t=?utf-8?q?2qfuuhHDmtQFQBwRNJuru7e9r3mQE5bBYLXRZWJ4HCpnbvxztE8SEM+j?=\n\t=?utf-8?q?NfF0aS89aGdhQOziZ/ZVhd2EnyFSYWmNTNhFeN4uyy1ya8zrC6HmkCoJ?=\n\t=?utf-8?q?VxS45OAdTR7ejApBbrZMzC6dvPgTWDcRU0kxITL1wUj9Ap+oyGOUVMqf?=\n\t=?utf-8?q?EHcNT2MY/0r5PthzLr/QtGiccKZdWj3Z9r0eG8GzW6+gOW2FNL5yp0pp?=\n\t=?utf-8?q?0yyL7OGZB52f+xb9fHmhLAEq3uCQXWaclpkxO/9vhJ6mzMUW4FEOxUMT?=\n\t=?utf-8?q?3lDYEaN9FT/cWaBybDIFWbl1z9cdFVAPNqNsDjRCxkU/JlVW0DSyCBZ3?=\n\t=?utf-8?q?sFougqCjAV0+FNeDc75Ea1DSDn+nFfNl8WsBM66yhBumxZaCnEC8GGBK?=\n\t=?utf-8?q?5O8JfNntPLIQJOPxXJTuVrlU2gy48JvEAgIxGYx0HJM17eTxpFzxGcHj?=\n\t=?utf-8?q?V7VFSgKNZuiEKXnaRHvBmbjjQh8M75q4hR75E9MGLf957oGLknI6RZxw?=\n\t=?utf-8?q?oHaOdh9kTe/DHLPA2Mn4xsHfAfl3YIdvEWlbcY0nKoSckR6OZIcN4JJc?=\n\t=?utf-8?q?Q3XBg+Ieb+uInEAxffeBCpZBSZ8xlHRJI1qHf/tJ+fDTiK15hoPu3cDD?=\n\t=?utf-8?q?p6NxXpUvU9v0MZYILUgtz4zvTcOLgnnYGpmIO5R580timZIUqbOhj1Gj?=\n\t=?utf-8?q?fO/DBwLgjLXtZ93WHkiKbHLZwwV80U1DrLBsYIlF1eld1wSUHaF05FiJ?=\n\t=?utf-8?q?EXfYTBLB7BW2140XjedpAG2MsSb1H91ckq2Z3U3Vuh63o+y9yU28Es7m?=\n\t=?utf-8?q?vfQRAYC5QlWpipjSj9pvRFmq0zlPtopzWhDuEpGvhS5/Jt+XVLrM61Xf?=\n\t=?utf-8?q?BiB544iN/eZo+dQaJlcOqdqFycvQVqt486c8wNEvMEZObNNU0SbAFK9Z?=\n\t=?utf-8?q?GmSGor5dAcx/DQXWauTNZqD13vo84jssDNfteNPS+/qCFadkK9PgwsKK?=\n\t=?utf-8?q?dfRJz5+n8FzKh7+8YyhphJeYPO1NaJHoCdH706vuukB+bQduYOy8RFWo?=\n\t=?utf-8?q?3Z3KwqUSG77KHBuDjiU/HlmV5/fbJfDHEYPnguL21r82BJDBXGC+VSDf?=\n\t=?utf-8?q?HYyhv3u9RlJmCoc01JwteGXE5lhUKzZTc27jLkDcyKgFn8xvGrf3ZNnG?=\n\t=?utf-8?q?I3UKAolLF8qiAH8d+uIx3N8hbWSBg=3D=3D?=","X-OriginatorOrg":"oss.nxp.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"8c90c3e5-e7f9-4846-117f-08dac8c4363e","X-MS-Exchange-CrossTenant-AuthSource":"PAXPR04MB8703.eurprd04.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"17 Nov 2022 17:50:27.8028\n\t(UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"686ea1d3-bc2b-4c6f-a92c-d99c5c301635","X-MS-Exchange-CrossTenant-MailboxType":"HOSTED","X-MS-Exchange-CrossTenant-UserPrincipalName":"I1kHT5aRFtU91895tJyHiNjXjffjkFnlp57kQKGnr+qUJAPBfpC/ubIRtMWa+Vz0VjE3lLeJ4XeW6MLeX5WRYA==","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"PAXPR04MB8576","Subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","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":"\"Xavier Roumegue \\(OSS\\) via libcamera-devel\"\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"\"Xavier Roumegue \\(OSS\\)\" <xavier.roumegue@oss.nxp.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":25819,"web_url":"https://patchwork.libcamera.org/comment/25819/","msgid":"<20221117181825.vodk4jqbkeezpfnw@uno.localdomain>","date":"2022-11-17T18:18:25","subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Xavier\n\nOn Thu, Nov 17, 2022 at 06:50:25PM +0100, Xavier Roumegue (OSS) wrote:\n> Hi Jacopo,\n>\n> On 11/17/22 10:29, Jacopo Mondi wrote:\n> > Hi Xavier\n> >\n> > On Tue, Nov 15, 2022 at 01:59:33PM +0100, Xavier Roumegue wrote:\n> > > Declare a converter Abstract Base Class intended to provide generic\n> > > interfaces to hardware offering size and format conversion services on\n> > > streams. This is mainly based on the public interfaces of the current\n> > > converter class implementation found in the simple pipeline handler.\n> > >\n> > > The main change is the introduction of loadConfiguration() function\n> > > which can be used by the concrete implementation to load hardware\n> > > specific runtime parameters defined by the application.\n> > >\n> > > Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n> > > ---\n> > >   include/libcamera/internal/converter.h | 109 ++++++++\n> > >   include/libcamera/internal/meson.build |   1 +\n> > >   src/libcamera/converter.cpp            | 331 +++++++++++++++++++++++++\n> > >   src/libcamera/meson.build              |   1 +\n> > >   4 files changed, 442 insertions(+)\n> > >   create mode 100644 include/libcamera/internal/converter.h\n> > >   create mode 100644 src/libcamera/converter.cpp\n> > >\n> > > diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h\n> > > new file mode 100644\n> > > index 00000000..19e8c7c5\n> > > --- /dev/null\n> > > +++ b/include/libcamera/internal/converter.h\n> > > @@ -0,0 +1,109 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2020, Laurent Pinchart\n> > > + * Copyright 2022 NXP\n> > > + *\n> > > + * converter.h - Generic stream converter interface\n> >\n> > As commented in v1, please align with this with the .cpp file\n> My bad.. I missed the stream/converter mismatch. I will fix it on v3 :(\n> Sorry for that\n\nNo need to resend just for this tiny bit :)\n\n>\n> Regards,\n>  Xavier\n> >\n> > If not other comments, it can be fixed when applying\n> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> >\n> > Thanks\n> >    j\n> >\n> >\n> > > + */\n> > > +\n> > > +#pragma once\n> > > +\n> > > +#include <initializer_list>\n> > > +#include <map>\n> > > +#include <memory>\n> > > +#include <string>\n> > > +#include <tuple>\n> > > +#include <vector>\n> > > +\n> > > +#include <libcamera/base/class.h>\n> > > +#include <libcamera/base/signal.h>\n> > > +\n> > > +#include <libcamera/geometry.h>\n> > > +#include <libcamera/pixel_format.h>\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +class FrameBuffer;\n> > > +class MediaDevice;\n> > > +class Size;\n> > > +class SizeRange;\n> > > +struct StreamConfiguration;\n> > > +\n> > > +class Converter\n> > > +{\n> > > +public:\n> > > +\tConverter(MediaDevice *media);\n> > > +\tvirtual ~Converter();\n> > > +\n> > > +\tvirtual int loadConfiguration(const std::string &filename) = 0;\n> > > +\n> > > +\tvirtual bool isValid() const = 0;\n> > > +\n> > > +\tvirtual std::vector<PixelFormat> formats(PixelFormat input) = 0;\n> > > +\tvirtual SizeRange sizes(const Size &input) = 0;\n> > > +\n> > > +\tvirtual std::tuple<unsigned int, unsigned int>\n> > > +\tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;\n> > > +\n> > > +\tvirtual int configure(const StreamConfiguration &inputCfg,\n> > > +\t\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;\n> > > +\tvirtual int exportBuffers(unsigned int output, unsigned int count,\n> > > +\t\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;\n> > > +\n> > > +\tvirtual int start() = 0;\n> > > +\tvirtual void stop() = 0;\n> > > +\n> > > +\tvirtual int queueBuffers(FrameBuffer *input,\n> > > +\t\t\t\t const std::map<unsigned int, FrameBuffer *> &outputs) = 0;\n> > > +\n> > > +\tSignal<FrameBuffer *> inputBufferReady;\n> > > +\tSignal<FrameBuffer *> outputBufferReady;\n> > > +\n> > > +\tconst std::string &deviceNode() const { return deviceNode_; };\n> > > +\n> > > +private:\n> > > +\tstd::string deviceNode_;\n> > > +};\n> > > +\n> > > +class ConverterFactoryBase\n> > > +{\n> > > +public:\n> > > +\tConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases);\n> > > +\tvirtual ~ConverterFactoryBase() = default;\n> > > +\n> > > +\tconst std::vector<std::string> &aliases() const { return aliases_; }\n> > > +\n> > > +\tstatic std::unique_ptr<Converter> create(MediaDevice *media);\n> > > +\tstatic std::vector<ConverterFactoryBase *> &factories();\n> > > +\tstatic std::vector<std::string> names();\n> > > +\n> > > +private:\n> > > +\tLIBCAMERA_DISABLE_COPY_AND_MOVE(ConverterFactoryBase)\n> > > +\n> > > +\tstatic void registerType(ConverterFactoryBase *factory);\n> > > +\n> > > +\tvirtual std::unique_ptr<Converter> createInstance(MediaDevice *media) const = 0;\n> > > +\n> > > +\tstd::string name_;\n> > > +\tstd::vector<std::string> aliases_;\n> > > +};\n> > > +\n> > > +template<typename _Converter>\n> > > +class ConverterFactory : public ConverterFactoryBase\n> > > +{\n> > > +public:\n> > > +\tConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n> > > +\t\t: ConverterFactoryBase(name, aliases)\n> > > +\t{\n> > > +\t}\n> > > +\n> > > +\tstd::unique_ptr<Converter> createInstance(MediaDevice *media) const override\n> > > +\t{\n> > > +\t\treturn std::make_unique<_Converter>(media);\n> > > +\t}\n> > > +};\n> > > +\n> > > +#define REGISTER_CONVERTER(name, converter, ...) \\\n> > > +\tstatic ConverterFactory<converter> global_##converter##Factory(name, { __VA_ARGS__ });\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> > > index 7a780d48..8f50d755 100644\n> > > --- a/include/libcamera/internal/meson.build\n> > > +++ b/include/libcamera/internal/meson.build\n> > > @@ -19,6 +19,7 @@ libcamera_internal_headers = files([\n> > >       'camera_sensor_properties.h',\n> > >       'control_serializer.h',\n> > >       'control_validator.h',\n> > > +    'converter.h',\n> > >       'delayed_controls.h',\n> > >       'device_enumerator.h',\n> > >       'device_enumerator_sysfs.h',\n> > > diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp\n> > > new file mode 100644\n> > > index 00000000..e370038f\n> > > --- /dev/null\n> > > +++ b/src/libcamera/converter.cpp\n> > > @@ -0,0 +1,331 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright 2022 NXP\n> > > + *\n> > > + * converter.cpp - Generic Format converter interface\n> > > + */\n> > > +\n> > > +#include \"libcamera/internal/converter.h\"\n> > > +\n> > > +#include <algorithm>\n> > > +\n> > > +#include <libcamera/base/log.h>\n> > > +\n> > > +#include \"libcamera/internal/media_device.h\"\n> > > +\n> > > +/**\n> > > + * \\file internal/converter.h\n> > > + * \\brief Abstract converter\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DEFINE_CATEGORY(Converter)\n> > > +\n> > > +/**\n> > > + * \\class Converter\n> > > + * \\brief Abstract Base Class for converter\n> > > + *\n> > > + * The Converter class is an Abstract Base Class defining the interfaces of\n> > > + * converter implementations.\n> > > + *\n> > > + * Converters offer scaling and pixel format conversion services on a input\n> > > + * stream. The converter can output multiple streams with individual conversion\n> > > + * parameters from the same input stream.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Construct a Converter instance\n> > > + * \\param[in] media The media device implementing the converter\n> > > + *\n> > > + * This seeks for the entity implementing data streaming function in the media\n> > > + * graph entities and use its device node as converter device node.\n> > > + */\n> > > +Converter::Converter(MediaDevice *media)\n> > > +{\n> > > +\tconst std::vector<MediaEntity *> &entities = media->entities();\n> > > +\tauto it = std::find_if(entities.begin(), entities.end(),\n> > > +\t\t\t       [](MediaEntity *entity) {\n> > > +\t\t\t\t       return entity->function() == MEDIA_ENT_F_IO_V4L;\n> > > +\t\t\t       });\n> > > +\tif (it == entities.end()) {\n> > > +\t\tLOG(Converter, Error)\n> > > +\t\t\t<< \"No entity suitable for implementing a converter in \"\n> > > +\t\t\t<< media->driver() << \" entities list.\";\n> > > +\t\treturn;\n> > > +\t}\n> > > +\n> > > +\tdeviceNode_ = (*it)->deviceNode();\n> > > +}\n> > > +\n> > > +Converter::~Converter()\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn Converter::loadConfiguration()\n> > > + * \\brief Load converter configuration from file\n> > > + * \\param[in] filename The file name path\n> > > + *\n> > > + * Load converter dependent configuration parameters to apply on the hardware.\n> > > + *\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::isValid()\n> > > + * \\brief Check if the converter configuration is valid\n> > > + * \\return True is the converter is valid, false otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::formats()\n> > > + * \\brief Retrieve the list of supported pixel formats for an input pixel format\n> > > + * \\param[in] input Input pixel format to retrieve output pixel format list for\n> > > + * \\return The list of supported output pixel formats\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::sizes()\n> > > + * \\brief Retrieve the range of minimum and maximum output sizes for an input size\n> > > + * \\param[in] input Input stream size to retrieve range for\n> > > + * \\return A range of output image sizes\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::strideAndFrameSize()\n> > > + * \\brief Retrieve the output stride and frame size for an input configutation\n> > > + * \\param[in] pixelFormat Input stream pixel format\n> > > + * \\param[in] size Input stream size\n> > > + * \\return A tuple indicating the stride and frame size or an empty tuple on error\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::configure()\n> > > + * \\brief Configure a set of output stream conversion from an input stream\n> > > + * \\param[in] inputCfg Input stream configuration\n> > > + * \\param[out] outputCfgs A list of output stream configurations\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::exportBuffers()\n> > > + * \\brief Export buffers from the converter device\n> > > + * \\param[in] output Output stream index exporting the buffers\n> > > + * \\param[in] count Number of buffers to allocate\n> > > + * \\param[out] buffers Vector to store allocated buffers\n> > > + *\n> > > + * This function operates similarly as V4L2VideoDevice::exportBuffers() on the\n> > > + * output stream indicated by the \\a output index.\n> > > + *\n> > > + * \\return The number of allocated buffers on success or a negative error code\n> > > + * otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::start()\n> > > + * \\brief Start the converter streaming operation\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::stop()\n> > > + * \\brief Stop the converter streaming operation\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::queueBuffers()\n> > > + * \\brief Queue buffers to converter device\n> > > + * \\param[in] input The frame buffer to apply the conversion\n> > > + * \\param[out] outputs The container holding the output stream indexes and\n> > > + * their respective frame buffer outputs.\n> > > + *\n> > > + * This function queues the \\a input frame buffer on the output streams of the\n> > > + * \\a outputs map key and retrieve the output frame buffer indicated by the\n> > > + * buffer map value.\n> > > + *\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\var Converter::inputBufferReady\n> > > + * \\brief A signal emitted when the input frame buffer completes\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\var Converter::outputBufferReady\n> > > + * \\brief A signal emitted when the output frame buffer completes\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Converter::deviceNode()\n> > > + * \\brief The converter device node attribute accessor\n> > > + * \\return The converter device node string\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\class ConverterFactoryBase\n> > > + * \\brief Base class for converter factories\n> > > + *\n> > > + * The ConverterFactoryBase class is the base of all specializations of the\n> > > + * ConverterFactory class template. It implements the factory registration,\n> > > + * maintains a registry of factories, and provides access to the registered\n> > > + * factories.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Construct a converter factory base\n> > > + * \\param[in] name Name of the converter class\n> > > + * \\param[in] aliases Name aliases of the converter class\n> > > + *\n> > > + * Creating an instance of the factory base registers it with the global list of\n> > > + * factories, accessible through the factories() function.\n> > > + *\n> > > + * The factory \\a name is used as unique identifier. If the converter\n> > > + * implementation fully relies on a generic framework, the name should be the\n> > > + * same as the framework. Otherwise, if the implementation is specialized, the\n> > > + * factory name should match the driver name implementing the function.\n> > > + *\n> > > + * The factory \\a aliases holds a list of driver names implementing a generic\n> > > + * subsystem without any personalizations.\n> > > + */\n> > > +ConverterFactoryBase::ConverterFactoryBase(const std::string name, std::initializer_list<std::string> aliases)\n> > > +\t: name_(name), aliases_(aliases)\n> > > +{\n> > > +\tregisterType(this);\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn ConverterFactoryBase::aliases()\n> > > + * \\return The names aliases\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Create an instance of the converter corresponding to a named factory\n> > > + * \\param[in] media Name of the factory\n> > > + *\n> > > + * \\return A unique pointer to a new instance of the converter subclass\n> > > + * corresponding to the named factory or one of its alias. Otherwise a null\n> > > + * pointer if no such factory exists\n> > > + */\n> > > +std::unique_ptr<Converter> ConverterFactoryBase::create(MediaDevice *media)\n> > > +{\n> > > +\tconst std::vector<ConverterFactoryBase *> &factories =\n> > > +\t\tConverterFactoryBase::factories();\n> > > +\n> > > +\tfor (const ConverterFactoryBase *factory : factories) {\n> > > +\t\tconst std::vector<std::string> &aliases = factory->aliases();\n> > > +\t\tauto it = std::find(aliases.begin(), aliases.end(), media->driver());\n> > > +\n> > > +\t\tif (it == aliases.end() && media->driver() != factory->name_)\n> > > +\t\t\tcontinue;\n> > > +\n> > > +\t\tLOG(Converter, Debug)\n> > > +\t\t\t<< \"Creating converter from \"\n> > > +\t\t\t<< factory->name_ << \" factory with \"\n> > > +\t\t\t<< (it == aliases.end() ? \"no\" : media->driver()) << \" alias.\";\n> > > +\n> > > +\t\treturn factory->createInstance(media);\n> > > +\t}\n> > > +\n> > > +\treturn nullptr;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Add a converter class to the registry\n> > > + * \\param[in] factory Factory to use to construct the converter class\n> > > + *\n> > > + * The caller is responsible to guarantee the uniqueness of the converter name.\n> > > + */\n> > > +void ConverterFactoryBase::registerType(ConverterFactoryBase *factory)\n> > > +{\n> > > +\tstd::vector<ConverterFactoryBase *> &factories =\n> > > +\t\tConverterFactoryBase::factories();\n> > > +\n> > > +\tfactories.push_back(factory);\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Retrieve the list of all converter factory names\n> > > + * \\return The list of all converter factory names\n> > > + */\n> > > +std::vector<std::string> ConverterFactoryBase::names()\n> > > +{\n> > > +\tstd::vector<std::string> list;\n> > > +\n> > > +\tstd::vector<ConverterFactoryBase *> &factories =\n> > > +\t\tConverterFactoryBase::factories();\n> > > +\n> > > +\tfor (ConverterFactoryBase *factory : factories) {\n> > > +\t\tlist.push_back(factory->name_);\n> > > +\t\tfor (auto alias : factory->aliases())\n> > > +\t\t\tlist.push_back(alias);\n> > > +\t}\n> > > +\n> > > +\treturn list;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Retrieve the list of all converter factories\n> > > + * \\return The list of converter factories\n> > > + */\n> > > +std::vector<ConverterFactoryBase *> &ConverterFactoryBase::factories()\n> > > +{\n> > > +\t/*\n> > > +\t * The static factories map is defined inside the function to ensure\n> > > +\t * it gets initialized on first use, without any dependency on link\n> > > +\t * order.\n> > > +\t */\n> > > +\tstatic std::vector<ConverterFactoryBase *> factories;\n> > > +\treturn factories;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\var ConverterFactoryBase::name_\n> > > + * \\brief The name of the factory\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\var ConverterFactoryBase::aliases_\n> > > + * \\brief The list holding the factory aliases\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\class ConverterFactory\n> > > + * \\brief Registration of ConverterFactory classes and creation of instances\n> > > + * \\param _Converter The converter class type for this factory\n> > > + *\n> > > + * To facilitate discovery and instantiation of Converter classes, the\n> > > + * ConverterFactory class implements auto-registration of converter helpers.\n> > > + * Each Converter subclass shall register itself using the REGISTER_CONVERTER()\n> > > + * macro, which will create a corresponding instance of a ConverterFactory\n> > > + * subclass and register it with the static list of factories.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn ConverterFactory::ConverterFactory(const char *name, std::initializer_list<std::string> aliases)\n> > > + * \\brief Construct a converter factory\n> > > + * \\details \\copydetails ConverterFactoryBase::ConverterFactoryBase\n> > > + *\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn ConverterFactory::createInstance() const\n> > > + * \\brief Create an instance of the Converter corresponding to the factory\n> > > + * \\param[in] media Media device pointer\n> > > + * \\return A unique pointer to a newly constructed instance of the Converter\n> > > + * subclass corresponding to the factory\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\def REGISTER_CONVERTER\n> > > + * \\brief Register a converter with the Converter factory\n> > > + * \\param[in] name Converter name used to register the class\n> > > + * \\param[in] converter Class name of Converter derived class to register\n> > > + * \\param[in] ... Optional list of alias names\n> > > + *\n> > > + * Register a Converter subclass with the factory and make it available to try\n> > > + * and match converters.\n> > > + */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > index 0494e808..e9d0324e 100644\n> > > --- a/src/libcamera/meson.build\n> > > +++ b/src/libcamera/meson.build\n> > > @@ -13,6 +13,7 @@ libcamera_sources = files([\n> > >       'controls.cpp',\n> > >       'control_serializer.cpp',\n> > >       'control_validator.cpp',\n> > > +    'converter.cpp',\n> > >       'delayed_controls.cpp',\n> > >       'device_enumerator.cpp',\n> > >       'device_enumerator_sysfs.cpp',\n> > > --\n> > > 2.38.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 E7AC5BE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 17 Nov 2022 18:18:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B219E6308F;\n\tThu, 17 Nov 2022 19:18:27 +0100 (CET)","from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net\n\t[IPv6:2001:4b98:dc4:8::225])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F018A61F31\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 17 Nov 2022 19:18:26 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby mail.gandi.net (Postfix) with ESMTPSA id 4E0141C0002;\n\tThu, 17 Nov 2022 18:18:26 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1668709107;\n\tbh=g42Jy7fHJsr6hlDSjplBmDTyihF6Cts6U5Bausw+jkk=;\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=ZUVLC+7CNgnUMQnX2ZRepPWIwwtCSKo2sfFNW0c2yP/qVQ8Vpl34174L7PS5fpIdX\n\teGdOsy9xXRzdPptv5bv33sOF6TzDlL3iyf4J7DkOd3vtS9fuTa5DQ8vWyYdr7SwGKW\n\tNwx8pRYLxR9Q1pKw4xnSOj2WQJJeuklTVzQ8CJy0Pqsq8AmVgDlinw2IEt+UCLO/RV\n\tWQmCdUvDwDay+V9D37RB9nm41ca4Ksxb+crTkjyWpkkGPKFASYmHsb6n0Q5iCK1mVR\n\t6AUPuxqZs4nK88Onx6qhSZe9zb4MZW9t4Yn2UJYLLnR7RPTIiGj7hRMJk1a6kL4P5g\n\t/QQm6/0ml6QBg==","Date":"Thu, 17 Nov 2022 19:18:25 +0100","To":"\"Xavier Roumegue (OSS)\" <xavier.roumegue@oss.nxp.com>","Message-ID":"<20221117181825.vodk4jqbkeezpfnw@uno.localdomain>","References":"<20221115125935.1196138-1-xavier.roumegue@oss.nxp.com>\n\t<20221117092950.3buz4y6allxfdtnx@uno.localdomain>\n\t<5f89c616-dde5-a304-f06f-5c22a03aefb3@oss.nxp.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<5f89c616-dde5-a304-f06f-5c22a03aefb3@oss.nxp.com>","Subject":"Re: [libcamera-devel] [PATCH v2 1/2] libcamera: Declare generic\n\tconverter interface","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":"Jacopo Mondi via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]