[{"id":25747,"web_url":"https://patchwork.libcamera.org/comment/25747/","msgid":"<20221109075446.ydkhn2olumea4ano@uno.localdomain>","date":"2022-11-09T07:54:46","subject":"Re: [libcamera-devel] [PATCH 2/2] libcamera: pipeline: simple:\n\tconverter: Use generic converter interface","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Xavier\n\nOn Mon, Oct 10, 2022 at 03:17:44PM +0200, Xavier Roumegue (OSS) wrote:\n> From: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n>\n> Move the simple converter implementation to a generic V4L2 M2M class\n> derived from the converter interface. This latter could be used by\n> other pipeline implementations and as base class for customized V4L2 M2M\n> converters.\n>\n> Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n> ---\n>  .../internal/converter/converter_v4l2_m2m.h   |  18 +--\n>  .../libcamera/internal/converter/meson.build  |   5 +\n>  include/libcamera/internal/meson.build        |   2 +\n>  .../converter_v4l2_m2m.cpp}                   | 149 +++++++++++-------\n>  src/libcamera/converter/meson.build           |   5 +\n>  src/libcamera/meson.build                     |   1 +\n>  src/libcamera/pipeline/simple/meson.build     |   1 -\n>  src/libcamera/pipeline/simple/simple.cpp      |   6 +-\n>  8 files changed, 119 insertions(+), 68 deletions(-)\n>  rename src/libcamera/pipeline/simple/converter.h => include/libcamera/internal/converter/converter_v4l2_m2m.h (83%)\n>  create mode 100644 include/libcamera/internal/converter/meson.build\n>  rename src/libcamera/{pipeline/simple/converter.cpp => converter/converter_v4l2_m2m.cpp} (69%)\n>  create mode 100644 src/libcamera/converter/meson.build\n>\n> diff --git a/src/libcamera/pipeline/simple/converter.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n> similarity index 83%\n> rename from src/libcamera/pipeline/simple/converter.h\n> rename to include/libcamera/internal/converter/converter_v4l2_m2m.h\n> index f0ebe2e0..ef31eeba 100644\n> --- a/src/libcamera/pipeline/simple/converter.h\n> +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n> @@ -1,8 +1,9 @@\n>  /* SPDX-License-Identifier: LGPL-2.1-or-later */\n>  /*\n>   * Copyright (C) 2020, Laurent Pinchart\n> + * Copyright 2022 NXP\n>   *\n> - * converter.h - Format converter for simple pipeline handler\n> + * converter_v4l2_m2m.h - V4l2 M2M Format converter interface\n>   */\n>\n>  #pragma once\n> @@ -19,6 +20,8 @@\n>  #include <libcamera/base/log.h>\n>  #include <libcamera/base/signal.h>\n>\n> +#include \"libcamera/internal/converter.h\"\n> +\n>  namespace libcamera {\n>\n>  class FrameBuffer;\n> @@ -28,11 +31,12 @@ class SizeRange;\n>  struct StreamConfiguration;\n>  class V4L2M2MDevice;\n>\n> -class SimpleConverter\n> +class V4L2M2MConverter : public Converter\n>  {\n>  public:\n> -\tSimpleConverter(MediaDevice *media);\n> +\tV4L2M2MConverter(MediaDevice *media);\n>\n> +\tint loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }\n\nHow do you envision this function to be called ?\nIf it is only your derived class that calls this, should this be part\nof the abstract base class ?\n\n>  \tbool isValid() const { return m2m_ != nullptr; }\n>\n>  \tstd::vector<PixelFormat> formats(PixelFormat input);\n> @@ -52,14 +56,11 @@ public:\n>  \tint queueBuffers(FrameBuffer *input,\n>  \t\t\t const std::map<unsigned int, FrameBuffer *> &outputs);\n>\n> -\tSignal<FrameBuffer *> inputBufferReady;\n> -\tSignal<FrameBuffer *> outputBufferReady;\n> -\n>  private:\n>  \tclass Stream : protected Loggable\n>  \t{\n>  \tpublic:\n> -\t\tStream(SimpleConverter *converter, unsigned int index);\n> +\t\tStream(V4L2M2MConverter *converter, unsigned int index);\n>\n>  \t\tbool isValid() const { return m2m_ != nullptr; }\n>\n> @@ -80,7 +81,7 @@ private:\n>  \t\tvoid captureBufferReady(FrameBuffer *buffer);\n>  \t\tvoid outputBufferReady(FrameBuffer *buffer);\n>\n> -\t\tSimpleConverter *converter_;\n> +\t\tV4L2M2MConverter *converter_;\n>  \t\tunsigned int index_;\n>  \t\tstd::unique_ptr<V4L2M2MDevice> m2m_;\n>\n> @@ -88,7 +89,6 @@ private:\n>  \t\tunsigned int outputBufferCount_;\n>  \t};\n>\n> -\tstd::string deviceNode_;\n>  \tstd::unique_ptr<V4L2M2MDevice> m2m_;\n>\n>  \tstd::vector<Stream> streams_;\n> diff --git a/include/libcamera/internal/converter/meson.build b/include/libcamera/internal/converter/meson.build\n> new file mode 100644\n> index 00000000..891e79e7\n> --- /dev/null\n> +++ b/include/libcamera/internal/converter/meson.build\n> @@ -0,0 +1,5 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +libcamera_internal_headers += files([\n> +    'converter_v4l2_m2m.h',\n> +])\n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index 8f50d755..b9db5a8c 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -45,3 +45,5 @@ libcamera_internal_headers = files([\n>      'v4l2_videodevice.h',\n>      'yaml_parser.h',\n>  ])\n> +\n> +subdir('converter')\n> diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp\n> similarity index 69%\n> rename from src/libcamera/pipeline/simple/converter.cpp\n> rename to src/libcamera/converter/converter_v4l2_m2m.cpp\n> index acaaa64c..5af054be 100644\n> --- a/src/libcamera/pipeline/simple/converter.cpp\n> +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp\n> @@ -1,12 +1,11 @@\n>  /* SPDX-License-Identifier: LGPL-2.1-or-later */\n>  /*\n>   * Copyright (C) 2020, Laurent Pinchart\n> + * Copyright 2022 NXP\n>   *\n> - * converter.cpp - Format converter for simple pipeline handler\n> + * converter_v4l2_m2m.cpp - V4L2 M2M Format converter\n>   */\n>\n> -#include \"converter.h\"\n> -\n>  #include <algorithm>\n>  #include <limits.h>\n>\n> @@ -20,19 +19,32 @@\n>\n>  #include \"libcamera/internal/media_device.h\"\n>  #include \"libcamera/internal/v4l2_videodevice.h\"\n> +#include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n\nShouldn't this be included first ?\n\n> +\n> +/**\n> + * \\file internal/converter/converter_v4l2_m2m.h\n> + * \\brief V4L2 M2M based converter\n> + */\n>\n>  namespace libcamera {\n>\n> -LOG_DECLARE_CATEGORY(SimplePipeline)\n> +LOG_DECLARE_CATEGORY(Converter)\n> +\n> +/**\n> + * \\class V4L2M2MConverter\n> + * \\brief V4L2 M2M based converter\n> + *\n> + * The V4L2 M2M converter implements the converter interface based on V4L2 M2M device.\n> + */\n>\n>  /* -----------------------------------------------------------------------------\n> - * SimpleConverter::Stream\n> + * V4L2M2MConverter::Stream\n>   */\n>\n> -SimpleConverter::Stream::Stream(SimpleConverter *converter, unsigned int index)\n> +V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, unsigned int index)\n>  \t: converter_(converter), index_(index)\n>  {\n> -\tm2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode_);\n> +\tm2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode());\n\nisValid() simply checks for m2m_ != nullptr.\nThe m2m video dev might fail at open() time.\nI would use a valid_ flag, to be set to true at the end of the\nconstructor\n\n>\n>  \tm2m_->output()->bufferReady.connect(this, &Stream::outputBufferReady);\n>  \tm2m_->capture()->bufferReady.connect(this, &Stream::captureBufferReady);\n> @@ -42,8 +54,8 @@ SimpleConverter::Stream::Stream(SimpleConverter *converter, unsigned int index)\n>  \t\tm2m_.reset();\n>  }\n>\n> -int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n> -\t\t\t\t       const StreamConfiguration &outputCfg)\n> +int V4L2M2MConverter::Stream::configure(const StreamConfiguration &inputCfg,\n> +\t\t\t\t\tconst StreamConfiguration &outputCfg)\n>  {\n>  \tV4L2PixelFormat videoFormat =\n>  \t\tm2m_->output()->toV4L2PixelFormat(inputCfg.pixelFormat);\n> @@ -56,14 +68,14 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>\n>  \tint ret = m2m_->output()->setFormat(&format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set input format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n>\n>  \tif (format.fourcc != videoFormat || format.size != inputCfg.size ||\n>  \t    format.planes[0].bpl != inputCfg.stride) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Input format not supported (requested \"\n>  \t\t\t<< inputCfg.size << \"-\" << videoFormat\n>  \t\t\t<< \", got \" << format << \")\";\n> @@ -78,13 +90,13 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>\n>  \tret = m2m_->capture()->setFormat(&format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set output format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n>\n>  \tif (format.fourcc != videoFormat || format.size != outputCfg.size) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Output format not supported\";\n>  \t\treturn -EINVAL;\n>  \t}\n> @@ -95,13 +107,13 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>  \treturn 0;\n>  }\n>\n> -int SimpleConverter::Stream::exportBuffers(unsigned int count,\n> -\t\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n> +int V4L2M2MConverter::Stream::exportBuffers(unsigned int count,\n> +\t\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>  {\n>  \treturn m2m_->capture()->exportBuffers(count, buffers);\n>  }\n>\n> -int SimpleConverter::Stream::start()\n> +int V4L2M2MConverter::Stream::start()\n>  {\n>  \tint ret = m2m_->output()->importBuffers(inputBufferCount_);\n>  \tif (ret < 0)\n> @@ -128,7 +140,7 @@ int SimpleConverter::Stream::start()\n>  \treturn 0;\n>  }\n>\n> -void SimpleConverter::Stream::stop()\n> +void V4L2M2MConverter::Stream::stop()\n>  {\n>  \tm2m_->capture()->streamOff();\n>  \tm2m_->output()->streamOff();\n> @@ -136,8 +148,7 @@ void SimpleConverter::Stream::stop()\n>  \tm2m_->output()->releaseBuffers();\n>  }\n>\n> -int SimpleConverter::Stream::queueBuffers(FrameBuffer *input,\n> -\t\t\t\t\t  FrameBuffer *output)\n> +int V4L2M2MConverter::Stream::queueBuffers(FrameBuffer *input, FrameBuffer *output)\n>  {\n>  \tint ret = m2m_->output()->queueBuffer(input);\n>  \tif (ret < 0)\n> @@ -150,12 +161,12 @@ int SimpleConverter::Stream::queueBuffers(FrameBuffer *input,\n>  \treturn 0;\n>  }\n>\n> -std::string SimpleConverter::Stream::logPrefix() const\n> +std::string V4L2M2MConverter::Stream::logPrefix() const\n>  {\n>  \treturn \"stream\" + std::to_string(index_);\n>  }\n>\n> -void SimpleConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n> +void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n>  {\n>  \tauto it = converter_->queue_.find(buffer);\n>  \tif (it == converter_->queue_.end())\n> @@ -167,32 +178,26 @@ void SimpleConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n>  \t}\n>  }\n>\n> -void SimpleConverter::Stream::captureBufferReady(FrameBuffer *buffer)\n> +void V4L2M2MConverter::Stream::captureBufferReady(FrameBuffer *buffer)\n>  {\n>  \tconverter_->outputBufferReady.emit(buffer);\n>  }\n>\n>  /* -----------------------------------------------------------------------------\n> - * SimpleConverter\n> + * V4L2M2MConverter\n>   */\n>\n> -SimpleConverter::SimpleConverter(MediaDevice *media)\n> +/**\n> + * \\brief Construct a V4L2M2MConverter instance\n> + * \\param[in] media The media device implementing the converter\n> + */\n> +V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)\n> +\t: Converter(media)\n>  {\n> -\t/*\n> -\t * Locate the video node. There's no need to validate the pipeline\n> -\t * further, the caller guarantees that this is a V4L2 mem2mem device.\n> -\t */\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> +\tif (deviceNode().empty())\n>  \t\treturn;\n>\n> -\tdeviceNode_ = (*it)->deviceNode();\n> -\n> -\tm2m_ = std::make_unique<V4L2M2MDevice>(deviceNode_);\n> +\tm2m_ = std::make_unique<V4L2M2MDevice>(deviceNode());\n>  \tint ret = m2m_->open();\n>  \tif (ret < 0) {\n>  \t\tm2m_.reset();\n> @@ -200,7 +205,18 @@ SimpleConverter::SimpleConverter(MediaDevice *media)\n>  \t}\n>  }\n>\n> -std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n> +/**\n> + * \\copydoc libcamera::Converter::loadConfiguration\n> + */\n> +\n> +/**\n> + * \\copydoc libcamera::Converter::isValid\n> + */\n> +\n> +/**\n> + * \\copydoc libcamera::Converter::formats\n> + */\n> +std::vector<PixelFormat> V4L2M2MConverter::formats(PixelFormat input)\n>  {\n>  \tif (!m2m_)\n>  \t\treturn {};\n> @@ -215,13 +231,13 @@ std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n>\n>  \tint ret = m2m_->output()->setFormat(&v4l2Format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>  \t\treturn {};\n>  \t}\n>\n>  \tif (v4l2Format.fourcc != m2m_->output()->toV4L2PixelFormat(input)) {\n> -\t\tLOG(SimplePipeline, Debug)\n> +\t\tLOG(Converter, Debug)\n>  \t\t\t<< \"Input format \" << input << \" not supported.\";\n>  \t\treturn {};\n>  \t}\n> @@ -237,7 +253,10 @@ std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n>  \treturn pixelFormats;\n>  }\n>\n> -SizeRange SimpleConverter::sizes(const Size &input)\n> +/**\n> + * \\copydoc libcamera::Converter::sizes\n> + */\n> +SizeRange V4L2M2MConverter::sizes(const Size &input)\n>  {\n>  \tif (!m2m_)\n>  \t\treturn {};\n> @@ -252,7 +271,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>\n>  \tint ret = m2m_->output()->setFormat(&format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>  \t\treturn {};\n>  \t}\n> @@ -262,7 +281,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>  \tformat.size = { 1, 1 };\n>  \tret = m2m_->capture()->setFormat(&format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>  \t\treturn {};\n>  \t}\n> @@ -272,7 +291,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>  \tformat.size = { UINT_MAX, UINT_MAX };\n>  \tret = m2m_->capture()->setFormat(&format);\n>  \tif (ret < 0) {\n> -\t\tLOG(SimplePipeline, Error)\n> +\t\tLOG(Converter, Error)\n>  \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>  \t\treturn {};\n>  \t}\n> @@ -282,9 +301,12 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>  \treturn sizes;\n>  }\n>\n> +/**\n> + * \\copydoc libcamera::Converter::strideAndFrameSize\n> + */\n>  std::tuple<unsigned int, unsigned int>\n> -SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n> -\t\t\t\t    const Size &size)\n> +V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n> +\t\t\t\t     const Size &size)\n>  {\n>  \tV4L2DeviceFormat format;\n>  \tformat.fourcc = m2m_->capture()->toV4L2PixelFormat(pixelFormat);\n> @@ -297,8 +319,11 @@ SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n>  \treturn std::make_tuple(format.planes[0].bpl, format.planes[0].size);\n>  }\n>\n> -int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n> -\t\t\t       const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)\n> +/**\n> + * \\copydoc libcamera::Converter::configure\n> + */\n> +int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,\n> +\t\t\t\tconst std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)\n>  {\n>  \tint ret = 0;\n>\n> @@ -309,7 +334,7 @@ int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n>  \t\tStream &stream = streams_.emplace_back(this, i);\n>\n>  \t\tif (!stream.isValid()) {\n> -\t\t\tLOG(SimplePipeline, Error)\n> +\t\t\tLOG(Converter, Error)\n>  \t\t\t\t<< \"Failed to create stream \" << i;\n>  \t\t\tret = -EINVAL;\n>  \t\t\tbreak;\n> @@ -328,8 +353,11 @@ int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n>  \treturn 0;\n>  }\n>\n> -int SimpleConverter::exportBuffers(unsigned int output, unsigned int count,\n> -\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n> +/**\n> + * \\copydoc libcamera::Converter::exportBuffers\n> + */\n> +int V4L2M2MConverter::exportBuffers(unsigned int output, unsigned int count,\n> +\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>  {\n>  \tif (output >= streams_.size())\n>  \t\treturn -EINVAL;\n> @@ -337,7 +365,10 @@ int SimpleConverter::exportBuffers(unsigned int output, unsigned int count,\n>  \treturn streams_[output].exportBuffers(count, buffers);\n>  }\n>\n> -int SimpleConverter::start()\n> +/**\n> + * \\copydoc libcamera::Converter::start\n> + */\n> +int V4L2M2MConverter::start()\n>  {\n>  \tint ret;\n>\n> @@ -352,14 +383,20 @@ int SimpleConverter::start()\n>  \treturn 0;\n>  }\n>\n> -void SimpleConverter::stop()\n> +/**\n> + * \\copydoc libcamera::Converter::stop\n> + */\n> +void V4L2M2MConverter::stop()\n>  {\n>  \tfor (Stream &stream : utils::reverse(streams_))\n>  \t\tstream.stop();\n>  }\n>\n> -int SimpleConverter::queueBuffers(FrameBuffer *input,\n> -\t\t\t\t  const std::map<unsigned int, FrameBuffer *> &outputs)\n> +/**\n> + * \\copydoc libcamera::Converter::queueBuffers\n> + */\n> +int V4L2M2MConverter::queueBuffers(FrameBuffer *input,\n> +\t\t\t\t   const std::map<unsigned int, FrameBuffer *> &outputs)\n>  {\n>  \tunsigned int mask = 0;\n>  \tint ret;\n> @@ -402,4 +439,6 @@ int SimpleConverter::queueBuffers(FrameBuffer *input,\n>  \treturn 0;\n>  }\n>\n> +REGISTER_CONVERTER(\"v4l2_m2m\", V4L2M2MConverter, \"pxp\")\n> +\n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/converter/meson.build b/src/libcamera/converter/meson.build\n> new file mode 100644\n> index 00000000..2aa72fe4\n> --- /dev/null\n> +++ b/src/libcamera/converter/meson.build\n> @@ -0,0 +1,5 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +libcamera_sources += files([\n> +        'converter_v4l2_m2m.cpp'\n> +])\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 3a9fc31f..d2f75741 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -62,6 +62,7 @@ libatomic = cc.find_library('atomic', required : false)\n>  libthreads = dependency('threads')\n>\n>  subdir('base')\n> +subdir('converter')\n>  subdir('ipa')\n>  subdir('pipeline')\n>  subdir('proxy')\n> diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build\n> index 9c99b32f..42b0896d 100644\n> --- a/src/libcamera/pipeline/simple/meson.build\n> +++ b/src/libcamera/pipeline/simple/meson.build\n> @@ -1,6 +1,5 @@\n>  # SPDX-License-Identifier: CC0-1.0\n>\n>  libcamera_sources += files([\n> -    'converter.cpp',\n>      'simple.cpp',\n>  ])\n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index 37b3e7ac..5cc6bc6a 100644\n> --- a/src/libcamera/pipeline/simple/simple.cpp\n> +++ b/src/libcamera/pipeline/simple/simple.cpp\n> @@ -30,13 +30,13 @@\n>\n>  #include \"libcamera/internal/camera.h\"\n>  #include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/converter.h\"\n>  #include \"libcamera/internal/device_enumerator.h\"\n>  #include \"libcamera/internal/media_device.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n>  #include \"libcamera/internal/v4l2_subdevice.h\"\n>  #include \"libcamera/internal/v4l2_videodevice.h\"\n>\n> -#include \"converter.h\"\n>\n>  namespace libcamera {\n>\n> @@ -266,7 +266,7 @@ public:\n>  \tstd::vector<Configuration> configs_;\n>  \tstd::map<PixelFormat, std::vector<const Configuration *>> formats_;\n>\n> -\tstd::unique_ptr<SimpleConverter> converter_;\n> +\tstd::unique_ptr<Converter> converter_;\n>  \tstd::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;\n>  \tbool useConverter_;\n>  \tstd::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;\n> @@ -492,7 +492,7 @@ int SimpleCameraData::init()\n>  \t/* Open the converter, if any. */\n>  \tMediaDevice *converter = pipe->converter();\n>  \tif (converter) {\n> -\t\tconverter_ = std::make_unique<SimpleConverter>(converter);\n> +\t\tconverter_ = ConverterFactoryBase::create(converter);\n>  \t\tif (!converter_->isValid()) {\n>  \t\t\tLOG(SimplePipeline, Warning)\n>  \t\t\t\t<< \"Failed to create converter, disabling format conversion\";\n\nAll minors:\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n  j\n\n> --\n> 2.37.3\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 53557BE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  9 Nov 2022 07:54:53 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9A2CB63083;\n\tWed,  9 Nov 2022 08:54:52 +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 DC46A61F39\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  9 Nov 2022 08:54:50 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby mail.gandi.net (Postfix) with ESMTPSA id F2CC210000E;\n\tWed,  9 Nov 2022 07:54:47 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1667980492;\n\tbh=XCSzQs7K7w3LkvwzOCaFQDb37mJoyn/5T7DDfXzb3/8=;\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=JiLTmb1sxqRt9K0PzCUMbi/hnMqftK5jpT1pWlj62+PsS93JnjLtKb6VxElH9ipap\n\tM2UUtp8eW8lrNS7xwBe5UMxxoXiCyqehP2PiGr2zjpt/2x511Q5+FhS3km0j3REK21\n\ttNqH8plCrLGbwF+wCf1/xv52NVxWwr6v2wwixkPgd87GxprIwhPQJYB/T2iUFPug2R\n\tGwdhkTyup8Lc76t9Ca0HnBP9S5Bapee+ApW95Brbjmz8eHex603hEL+W0WSYtKDR1O\n\t/EI5kzlpIVCR7WsbGgVWW/vdvtGYbd/ZQCSz4BiwpIABW911qkMbinydv++hk2ifj+\n\tatFGQ7hw9Rxqg==","Date":"Wed, 9 Nov 2022 08:54:46 +0100","To":"\"Xavier Roumegue (OSS)\" <xavier.roumegue@oss.nxp.com>","Message-ID":"<20221109075446.ydkhn2olumea4ano@uno.localdomain>","References":"<20221010131744.513261-1-xavier.roumegue@oss.nxp.com>\n\t<20221010131744.513261-3-xavier.roumegue@oss.nxp.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20221010131744.513261-3-xavier.roumegue@oss.nxp.com>","Subject":"Re: [libcamera-devel] [PATCH 2/2] libcamera: pipeline: simple:\n\tconverter: Use generic converter 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":25797,"web_url":"https://patchwork.libcamera.org/comment/25797/","msgid":"<79252792-15c7-511c-d1b7-74b0da9d543b@oss.nxp.com>","date":"2022-11-15T10:15:18","subject":"Re: [libcamera-devel] [PATCH 2/2] libcamera: pipeline: simple:\n\tconverter: Use generic converter 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/9/22 08:54, Jacopo Mondi wrote:\n> Hi Xavier\n> \n> On Mon, Oct 10, 2022 at 03:17:44PM +0200, Xavier Roumegue (OSS) wrote:\n>> From: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n>>\n>> Move the simple converter implementation to a generic V4L2 M2M class\n>> derived from the converter interface. This latter could be used by\n>> other pipeline implementations and as base class for customized V4L2 M2M\n>> converters.\n>>\n>> Signed-off-by: Xavier Roumegue <xavier.roumegue@oss.nxp.com>\n>> ---\n>>   .../internal/converter/converter_v4l2_m2m.h   |  18 +--\n>>   .../libcamera/internal/converter/meson.build  |   5 +\n>>   include/libcamera/internal/meson.build        |   2 +\n>>   .../converter_v4l2_m2m.cpp}                   | 149 +++++++++++-------\n>>   src/libcamera/converter/meson.build           |   5 +\n>>   src/libcamera/meson.build                     |   1 +\n>>   src/libcamera/pipeline/simple/meson.build     |   1 -\n>>   src/libcamera/pipeline/simple/simple.cpp      |   6 +-\n>>   8 files changed, 119 insertions(+), 68 deletions(-)\n>>   rename src/libcamera/pipeline/simple/converter.h => include/libcamera/internal/converter/converter_v4l2_m2m.h (83%)\n>>   create mode 100644 include/libcamera/internal/converter/meson.build\n>>   rename src/libcamera/{pipeline/simple/converter.cpp => converter/converter_v4l2_m2m.cpp} (69%)\n>>   create mode 100644 src/libcamera/converter/meson.build\n>>\n>> diff --git a/src/libcamera/pipeline/simple/converter.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n>> similarity index 83%\n>> rename from src/libcamera/pipeline/simple/converter.h\n>> rename to include/libcamera/internal/converter/converter_v4l2_m2m.h\n>> index f0ebe2e0..ef31eeba 100644\n>> --- a/src/libcamera/pipeline/simple/converter.h\n>> +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n>> @@ -1,8 +1,9 @@\n>>   /* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>   /*\n>>    * Copyright (C) 2020, Laurent Pinchart\n>> + * Copyright 2022 NXP\n>>    *\n>> - * converter.h - Format converter for simple pipeline handler\n>> + * converter_v4l2_m2m.h - V4l2 M2M Format converter interface\n>>    */\n>>\n>>   #pragma once\n>> @@ -19,6 +20,8 @@\n>>   #include <libcamera/base/log.h>\n>>   #include <libcamera/base/signal.h>\n>>\n>> +#include \"libcamera/internal/converter.h\"\n>> +\n>>   namespace libcamera {\n>>\n>>   class FrameBuffer;\n>> @@ -28,11 +31,12 @@ class SizeRange;\n>>   struct StreamConfiguration;\n>>   class V4L2M2MDevice;\n>>\n>> -class SimpleConverter\n>> +class V4L2M2MConverter : public Converter\n>>   {\n>>   public:\n>> -\tSimpleConverter(MediaDevice *media);\n>> +\tV4L2M2MConverter(MediaDevice *media);\n>>\n>> +\tint loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }\n> \n> How do you envision this function to be called ?\nShould be called from the pipeline handlers as the configuration file depends on \nthe pipeline handler.\n> If it is only your derived class that calls this, should this be part\n> of the abstract base class ?\n> \n>>   \tbool isValid() const { return m2m_ != nullptr; }\n>>\n>>   \tstd::vector<PixelFormat> formats(PixelFormat input);\n>> @@ -52,14 +56,11 @@ public:\n>>   \tint queueBuffers(FrameBuffer *input,\n>>   \t\t\t const std::map<unsigned int, FrameBuffer *> &outputs);\n>>\n>> -\tSignal<FrameBuffer *> inputBufferReady;\n>> -\tSignal<FrameBuffer *> outputBufferReady;\n>> -\n>>   private:\n>>   \tclass Stream : protected Loggable\n>>   \t{\n>>   \tpublic:\n>> -\t\tStream(SimpleConverter *converter, unsigned int index);\n>> +\t\tStream(V4L2M2MConverter *converter, unsigned int index);\n>>\n>>   \t\tbool isValid() const { return m2m_ != nullptr; }\n>>\n>> @@ -80,7 +81,7 @@ private:\n>>   \t\tvoid captureBufferReady(FrameBuffer *buffer);\n>>   \t\tvoid outputBufferReady(FrameBuffer *buffer);\n>>\n>> -\t\tSimpleConverter *converter_;\n>> +\t\tV4L2M2MConverter *converter_;\n>>   \t\tunsigned int index_;\n>>   \t\tstd::unique_ptr<V4L2M2MDevice> m2m_;\n>>\n>> @@ -88,7 +89,6 @@ private:\n>>   \t\tunsigned int outputBufferCount_;\n>>   \t};\n>>\n>> -\tstd::string deviceNode_;\n>>   \tstd::unique_ptr<V4L2M2MDevice> m2m_;\n>>\n>>   \tstd::vector<Stream> streams_;\n>> diff --git a/include/libcamera/internal/converter/meson.build b/include/libcamera/internal/converter/meson.build\n>> new file mode 100644\n>> index 00000000..891e79e7\n>> --- /dev/null\n>> +++ b/include/libcamera/internal/converter/meson.build\n>> @@ -0,0 +1,5 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +libcamera_internal_headers += files([\n>> +    'converter_v4l2_m2m.h',\n>> +])\n>> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n>> index 8f50d755..b9db5a8c 100644\n>> --- a/include/libcamera/internal/meson.build\n>> +++ b/include/libcamera/internal/meson.build\n>> @@ -45,3 +45,5 @@ libcamera_internal_headers = files([\n>>       'v4l2_videodevice.h',\n>>       'yaml_parser.h',\n>>   ])\n>> +\n>> +subdir('converter')\n>> diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp\n>> similarity index 69%\n>> rename from src/libcamera/pipeline/simple/converter.cpp\n>> rename to src/libcamera/converter/converter_v4l2_m2m.cpp\n>> index acaaa64c..5af054be 100644\n>> --- a/src/libcamera/pipeline/simple/converter.cpp\n>> +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp\n>> @@ -1,12 +1,11 @@\n>>   /* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>   /*\n>>    * Copyright (C) 2020, Laurent Pinchart\n>> + * Copyright 2022 NXP\n>>    *\n>> - * converter.cpp - Format converter for simple pipeline handler\n>> + * converter_v4l2_m2m.cpp - V4L2 M2M Format converter\n>>    */\n>>\n>> -#include \"converter.h\"\n>> -\n>>   #include <algorithm>\n>>   #include <limits.h>\n>>\n>> @@ -20,19 +19,32 @@\n>>\n>>   #include \"libcamera/internal/media_device.h\"\n>>   #include \"libcamera/internal/v4l2_videodevice.h\"\n>> +#include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n> \n> Shouldn't this be included first ?\n> \n>> +\n>> +/**\n>> + * \\file internal/converter/converter_v4l2_m2m.h\n>> + * \\brief V4L2 M2M based converter\n>> + */\n>>\n>>   namespace libcamera {\n>>\n>> -LOG_DECLARE_CATEGORY(SimplePipeline)\n>> +LOG_DECLARE_CATEGORY(Converter)\n>> +\n>> +/**\n>> + * \\class V4L2M2MConverter\n>> + * \\brief V4L2 M2M based converter\n>> + *\n>> + * The V4L2 M2M converter implements the converter interface based on V4L2 M2M device.\n>> + */\n>>\n>>   /* -----------------------------------------------------------------------------\n>> - * SimpleConverter::Stream\n>> + * V4L2M2MConverter::Stream\n>>    */\n>>\n>> -SimpleConverter::Stream::Stream(SimpleConverter *converter, unsigned int index)\n>> +V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, unsigned int index)\n>>   \t: converter_(converter), index_(index)\n>>   {\n>> -\tm2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode_);\n>> +\tm2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode());\n> \n> isValid() simply checks for m2m_ != nullptr.\n> The m2m video dev might fail at open() time.\n> I would use a valid_ flag, to be set to true at the end of the\n> constructor\n> \n>>\n>>   \tm2m_->output()->bufferReady.connect(this, &Stream::outputBufferReady);\n>>   \tm2m_->capture()->bufferReady.connect(this, &Stream::captureBufferReady);\n>> @@ -42,8 +54,8 @@ SimpleConverter::Stream::Stream(SimpleConverter *converter, unsigned int index)\n>>   \t\tm2m_.reset();\n>>   }\n>>\n>> -int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>> -\t\t\t\t       const StreamConfiguration &outputCfg)\n>> +int V4L2M2MConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>> +\t\t\t\t\tconst StreamConfiguration &outputCfg)\n>>   {\n>>   \tV4L2PixelFormat videoFormat =\n>>   \t\tm2m_->output()->toV4L2PixelFormat(inputCfg.pixelFormat);\n>> @@ -56,14 +68,14 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>>\n>>   \tint ret = m2m_->output()->setFormat(&format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set input format: \" << strerror(-ret);\n>>   \t\treturn ret;\n>>   \t}\n>>\n>>   \tif (format.fourcc != videoFormat || format.size != inputCfg.size ||\n>>   \t    format.planes[0].bpl != inputCfg.stride) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Input format not supported (requested \"\n>>   \t\t\t<< inputCfg.size << \"-\" << videoFormat\n>>   \t\t\t<< \", got \" << format << \")\";\n>> @@ -78,13 +90,13 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>>\n>>   \tret = m2m_->capture()->setFormat(&format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set output format: \" << strerror(-ret);\n>>   \t\treturn ret;\n>>   \t}\n>>\n>>   \tif (format.fourcc != videoFormat || format.size != outputCfg.size) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Output format not supported\";\n>>   \t\treturn -EINVAL;\n>>   \t}\n>> @@ -95,13 +107,13 @@ int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg,\n>>   \treturn 0;\n>>   }\n>>\n>> -int SimpleConverter::Stream::exportBuffers(unsigned int count,\n>> -\t\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>> +int V4L2M2MConverter::Stream::exportBuffers(unsigned int count,\n>> +\t\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>>   {\n>>   \treturn m2m_->capture()->exportBuffers(count, buffers);\n>>   }\n>>\n>> -int SimpleConverter::Stream::start()\n>> +int V4L2M2MConverter::Stream::start()\n>>   {\n>>   \tint ret = m2m_->output()->importBuffers(inputBufferCount_);\n>>   \tif (ret < 0)\n>> @@ -128,7 +140,7 @@ int SimpleConverter::Stream::start()\n>>   \treturn 0;\n>>   }\n>>\n>> -void SimpleConverter::Stream::stop()\n>> +void V4L2M2MConverter::Stream::stop()\n>>   {\n>>   \tm2m_->capture()->streamOff();\n>>   \tm2m_->output()->streamOff();\n>> @@ -136,8 +148,7 @@ void SimpleConverter::Stream::stop()\n>>   \tm2m_->output()->releaseBuffers();\n>>   }\n>>\n>> -int SimpleConverter::Stream::queueBuffers(FrameBuffer *input,\n>> -\t\t\t\t\t  FrameBuffer *output)\n>> +int V4L2M2MConverter::Stream::queueBuffers(FrameBuffer *input, FrameBuffer *output)\n>>   {\n>>   \tint ret = m2m_->output()->queueBuffer(input);\n>>   \tif (ret < 0)\n>> @@ -150,12 +161,12 @@ int SimpleConverter::Stream::queueBuffers(FrameBuffer *input,\n>>   \treturn 0;\n>>   }\n>>\n>> -std::string SimpleConverter::Stream::logPrefix() const\n>> +std::string V4L2M2MConverter::Stream::logPrefix() const\n>>   {\n>>   \treturn \"stream\" + std::to_string(index_);\n>>   }\n>>\n>> -void SimpleConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n>> +void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n>>   {\n>>   \tauto it = converter_->queue_.find(buffer);\n>>   \tif (it == converter_->queue_.end())\n>> @@ -167,32 +178,26 @@ void SimpleConverter::Stream::outputBufferReady(FrameBuffer *buffer)\n>>   \t}\n>>   }\n>>\n>> -void SimpleConverter::Stream::captureBufferReady(FrameBuffer *buffer)\n>> +void V4L2M2MConverter::Stream::captureBufferReady(FrameBuffer *buffer)\n>>   {\n>>   \tconverter_->outputBufferReady.emit(buffer);\n>>   }\n>>\n>>   /* -----------------------------------------------------------------------------\n>> - * SimpleConverter\n>> + * V4L2M2MConverter\n>>    */\n>>\n>> -SimpleConverter::SimpleConverter(MediaDevice *media)\n>> +/**\n>> + * \\brief Construct a V4L2M2MConverter instance\n>> + * \\param[in] media The media device implementing the converter\n>> + */\n>> +V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)\n>> +\t: Converter(media)\n>>   {\n>> -\t/*\n>> -\t * Locate the video node. There's no need to validate the pipeline\n>> -\t * further, the caller guarantees that this is a V4L2 mem2mem device.\n>> -\t */\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>> +\tif (deviceNode().empty())\n>>   \t\treturn;\n>>\n>> -\tdeviceNode_ = (*it)->deviceNode();\n>> -\n>> -\tm2m_ = std::make_unique<V4L2M2MDevice>(deviceNode_);\n>> +\tm2m_ = std::make_unique<V4L2M2MDevice>(deviceNode());\n>>   \tint ret = m2m_->open();\n>>   \tif (ret < 0) {\n>>   \t\tm2m_.reset();\n>> @@ -200,7 +205,18 @@ SimpleConverter::SimpleConverter(MediaDevice *media)\n>>   \t}\n>>   }\n>>\n>> -std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n>> +/**\n>> + * \\copydoc libcamera::Converter::loadConfiguration\n>> + */\n>> +\n>> +/**\n>> + * \\copydoc libcamera::Converter::isValid\n>> + */\n>> +\n>> +/**\n>> + * \\copydoc libcamera::Converter::formats\n>> + */\n>> +std::vector<PixelFormat> V4L2M2MConverter::formats(PixelFormat input)\n>>   {\n>>   \tif (!m2m_)\n>>   \t\treturn {};\n>> @@ -215,13 +231,13 @@ std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n>>\n>>   \tint ret = m2m_->output()->setFormat(&v4l2Format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>>   \t\treturn {};\n>>   \t}\n>>\n>>   \tif (v4l2Format.fourcc != m2m_->output()->toV4L2PixelFormat(input)) {\n>> -\t\tLOG(SimplePipeline, Debug)\n>> +\t\tLOG(Converter, Debug)\n>>   \t\t\t<< \"Input format \" << input << \" not supported.\";\n>>   \t\treturn {};\n>>   \t}\n>> @@ -237,7 +253,10 @@ std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n>>   \treturn pixelFormats;\n>>   }\n>>\n>> -SizeRange SimpleConverter::sizes(const Size &input)\n>> +/**\n>> + * \\copydoc libcamera::Converter::sizes\n>> + */\n>> +SizeRange V4L2M2MConverter::sizes(const Size &input)\n>>   {\n>>   \tif (!m2m_)\n>>   \t\treturn {};\n>> @@ -252,7 +271,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>>\n>>   \tint ret = m2m_->output()->setFormat(&format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>>   \t\treturn {};\n>>   \t}\n>> @@ -262,7 +281,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>>   \tformat.size = { 1, 1 };\n>>   \tret = m2m_->capture()->setFormat(&format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>>   \t\treturn {};\n>>   \t}\n>> @@ -272,7 +291,7 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>>   \tformat.size = { UINT_MAX, UINT_MAX };\n>>   \tret = m2m_->capture()->setFormat(&format);\n>>   \tif (ret < 0) {\n>> -\t\tLOG(SimplePipeline, Error)\n>> +\t\tLOG(Converter, Error)\n>>   \t\t\t<< \"Failed to set format: \" << strerror(-ret);\n>>   \t\treturn {};\n>>   \t}\n>> @@ -282,9 +301,12 @@ SizeRange SimpleConverter::sizes(const Size &input)\n>>   \treturn sizes;\n>>   }\n>>\n>> +/**\n>> + * \\copydoc libcamera::Converter::strideAndFrameSize\n>> + */\n>>   std::tuple<unsigned int, unsigned int>\n>> -SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n>> -\t\t\t\t    const Size &size)\n>> +V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n>> +\t\t\t\t     const Size &size)\n>>   {\n>>   \tV4L2DeviceFormat format;\n>>   \tformat.fourcc = m2m_->capture()->toV4L2PixelFormat(pixelFormat);\n>> @@ -297,8 +319,11 @@ SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n>>   \treturn std::make_tuple(format.planes[0].bpl, format.planes[0].size);\n>>   }\n>>\n>> -int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n>> -\t\t\t       const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)\n>> +/**\n>> + * \\copydoc libcamera::Converter::configure\n>> + */\n>> +int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,\n>> +\t\t\t\tconst std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)\n>>   {\n>>   \tint ret = 0;\n>>\n>> @@ -309,7 +334,7 @@ int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n>>   \t\tStream &stream = streams_.emplace_back(this, i);\n>>\n>>   \t\tif (!stream.isValid()) {\n>> -\t\t\tLOG(SimplePipeline, Error)\n>> +\t\t\tLOG(Converter, Error)\n>>   \t\t\t\t<< \"Failed to create stream \" << i;\n>>   \t\t\tret = -EINVAL;\n>>   \t\t\tbreak;\n>> @@ -328,8 +353,11 @@ int SimpleConverter::configure(const StreamConfiguration &inputCfg,\n>>   \treturn 0;\n>>   }\n>>\n>> -int SimpleConverter::exportBuffers(unsigned int output, unsigned int count,\n>> -\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>> +/**\n>> + * \\copydoc libcamera::Converter::exportBuffers\n>> + */\n>> +int V4L2M2MConverter::exportBuffers(unsigned int output, unsigned int count,\n>> +\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>>   {\n>>   \tif (output >= streams_.size())\n>>   \t\treturn -EINVAL;\n>> @@ -337,7 +365,10 @@ int SimpleConverter::exportBuffers(unsigned int output, unsigned int count,\n>>   \treturn streams_[output].exportBuffers(count, buffers);\n>>   }\n>>\n>> -int SimpleConverter::start()\n>> +/**\n>> + * \\copydoc libcamera::Converter::start\n>> + */\n>> +int V4L2M2MConverter::start()\n>>   {\n>>   \tint ret;\n>>\n>> @@ -352,14 +383,20 @@ int SimpleConverter::start()\n>>   \treturn 0;\n>>   }\n>>\n>> -void SimpleConverter::stop()\n>> +/**\n>> + * \\copydoc libcamera::Converter::stop\n>> + */\n>> +void V4L2M2MConverter::stop()\n>>   {\n>>   \tfor (Stream &stream : utils::reverse(streams_))\n>>   \t\tstream.stop();\n>>   }\n>>\n>> -int SimpleConverter::queueBuffers(FrameBuffer *input,\n>> -\t\t\t\t  const std::map<unsigned int, FrameBuffer *> &outputs)\n>> +/**\n>> + * \\copydoc libcamera::Converter::queueBuffers\n>> + */\n>> +int V4L2M2MConverter::queueBuffers(FrameBuffer *input,\n>> +\t\t\t\t   const std::map<unsigned int, FrameBuffer *> &outputs)\n>>   {\n>>   \tunsigned int mask = 0;\n>>   \tint ret;\n>> @@ -402,4 +439,6 @@ int SimpleConverter::queueBuffers(FrameBuffer *input,\n>>   \treturn 0;\n>>   }\n>>\n>> +REGISTER_CONVERTER(\"v4l2_m2m\", V4L2M2MConverter, \"pxp\")\n>> +\n>>   } /* namespace libcamera */\n>> diff --git a/src/libcamera/converter/meson.build b/src/libcamera/converter/meson.build\n>> new file mode 100644\n>> index 00000000..2aa72fe4\n>> --- /dev/null\n>> +++ b/src/libcamera/converter/meson.build\n>> @@ -0,0 +1,5 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +libcamera_sources += files([\n>> +        'converter_v4l2_m2m.cpp'\n>> +])\n>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n>> index 3a9fc31f..d2f75741 100644\n>> --- a/src/libcamera/meson.build\n>> +++ b/src/libcamera/meson.build\n>> @@ -62,6 +62,7 @@ libatomic = cc.find_library('atomic', required : false)\n>>   libthreads = dependency('threads')\n>>\n>>   subdir('base')\n>> +subdir('converter')\n>>   subdir('ipa')\n>>   subdir('pipeline')\n>>   subdir('proxy')\n>> diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build\n>> index 9c99b32f..42b0896d 100644\n>> --- a/src/libcamera/pipeline/simple/meson.build\n>> +++ b/src/libcamera/pipeline/simple/meson.build\n>> @@ -1,6 +1,5 @@\n>>   # SPDX-License-Identifier: CC0-1.0\n>>\n>>   libcamera_sources += files([\n>> -    'converter.cpp',\n>>       'simple.cpp',\n>>   ])\n>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n>> index 37b3e7ac..5cc6bc6a 100644\n>> --- a/src/libcamera/pipeline/simple/simple.cpp\n>> +++ b/src/libcamera/pipeline/simple/simple.cpp\n>> @@ -30,13 +30,13 @@\n>>\n>>   #include \"libcamera/internal/camera.h\"\n>>   #include \"libcamera/internal/camera_sensor.h\"\n>> +#include \"libcamera/internal/converter.h\"\n>>   #include \"libcamera/internal/device_enumerator.h\"\n>>   #include \"libcamera/internal/media_device.h\"\n>>   #include \"libcamera/internal/pipeline_handler.h\"\n>>   #include \"libcamera/internal/v4l2_subdevice.h\"\n>>   #include \"libcamera/internal/v4l2_videodevice.h\"\n>>\n>> -#include \"converter.h\"\n>>\n>>   namespace libcamera {\n>>\n>> @@ -266,7 +266,7 @@ public:\n>>   \tstd::vector<Configuration> configs_;\n>>   \tstd::map<PixelFormat, std::vector<const Configuration *>> formats_;\n>>\n>> -\tstd::unique_ptr<SimpleConverter> converter_;\n>> +\tstd::unique_ptr<Converter> converter_;\n>>   \tstd::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;\n>>   \tbool useConverter_;\n>>   \tstd::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;\n>> @@ -492,7 +492,7 @@ int SimpleCameraData::init()\n>>   \t/* Open the converter, if any. */\n>>   \tMediaDevice *converter = pipe->converter();\n>>   \tif (converter) {\n>> -\t\tconverter_ = std::make_unique<SimpleConverter>(converter);\n>> +\t\tconverter_ = ConverterFactoryBase::create(converter);\n>>   \t\tif (!converter_->isValid()) {\n>>   \t\t\tLOG(SimplePipeline, Warning)\n>>   \t\t\t\t<< \"Failed to create converter, disabling format conversion\";\n> \n> All minors:\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> \n> Thanks\n>    j\n> \n>> --\n>> 2.37.3\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 9627CBE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Nov 2022 10:15:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D81D363088;\n\tTue, 15 Nov 2022 11:15:24 +0100 (CET)","from EUR04-HE1-obe.outbound.protection.outlook.com\n\t(mail-he1eur04on0605.outbound.protection.outlook.com\n\t[IPv6:2a01:111:f400:fe0d::605])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9325E63079\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Nov 2022 11:15:22 +0100 (CET)","from PAXPR04MB8703.eurprd04.prod.outlook.com\n\t(2603:10a6:102:21e::22)\n\tby AS8PR04MB8691.eurprd04.prod.outlook.com (2603:10a6:20b:42a::15)\n\twith Microsoft SMTP Server (version=TLS1_2,\n\tcipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5813.13;\n\tTue, 15 Nov 2022 10:15:20 +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.018;\n\tTue, 15 Nov 2022 10:15:20 +0000"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1668507324;\n\tbh=HXRkFXIm8nm5KVunEJc0iQDu8OcHiq3lDkf2tLAlpJc=;\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=jpWd8JoWt61NYPsgkIgJFzgUr9LMUse6+5GcFy2ax3jBjK9ERDqF32NkdEi6ovlyz\n\tDqUUr4OhbVPJQNaM5jDVl7ArIvwiAkQxRdkySZq49rgB78vMO8M3uaMkeW44gt57Ym\n\tXLQPui88CKWrPMws0zrtXGKNyFBDgRxA+BOsjV/U0EE8t3spK+qQrJTLNlbXAskijn\n\tO77gaCuOje6je6ewgOft7BeDSCCk+PvvadSGAiDbeAXR5E6dj7HJeuZ1CKDN/MGGQm\n\tBZcvlxciC28bKB6gXTzoV6w46Afp2DFUlJeYxSX1KNB8xkYGWN9bMOCNwIdgwcQKSm\n\tJnjCR5gAKLioA==","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=lP59qfXvCaFMVPe+rfSfolZ119OyjUNQkJo+ZQ4loxk=;\n\tb=eFilnkY9ihHInvr2eBqQaz6wC5E7QlAAlTyepb6wZtHEAlEFiL7nFmWXq9aO9CQsXL+bM6HbJZxvSZN4/dmjHGlF/lDt/PN16GYQ9EBYnz9JJBID01P8mu7fQteLJr15VzPfc6pEJC+za+AvWqMS2QQBWN6MiBaYpuWHLN/jfhI="],"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=\"eFilnkY9\"; 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=nb5f11Xj7GLZ0g7Sdk2Q/l2s5bX0b0y868CaouDKd189ulC4wVW6RWJERYMFjFVm3skkdE6JJh86gPfLT34oGHBew51Bfw/2e+wGgqLfzWWDl/SyF2/YfKF27XRgsjrH3vzSStdpS2UgL/1EWT/FnI8Ul4yuZ/ZDJD1XT0df/nYpOcsZabaGRV4sGK43kmaAOTnf8yO++bXwsVU+JoACnrTDu0LwchkAibr9n2UQT6oENNvxTAmp71iwJ44CAeusPo4VXB7vKrHAmXYPLHrdScov9IbRhx7QAYYsyuMKiEgzN+LiPyOwvWPV4Mtyogr1Qd2TFepEldRepkEKlHabvg==","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=lP59qfXvCaFMVPe+rfSfolZ119OyjUNQkJo+ZQ4loxk=;\n\tb=WTILR1HAPerRG1d6TKnQ3Z7yz+xBCjCF5QtpYBEi63vkiooMsDor2Q2UDVtHAozSOAe5lPDz/dUGRIBhYB2dB9iR/j96VjZQDdI2DYbiKktOpjYDh64hRgN1zPxwaIHmRAtABkxikZMNUjHFpVOOvMjueONRJMM46HXxn46CWSGBrSPsYz1uNUZp0Fzs03Z89utRzBnrFCPW1Rm54ZcmWIag3jHDbHDVtxOGzzUslAT1AayYyd6UDJRaOXSadli5krENuk5+Z4321nw2349TOb4r0DnSA4ElcsPHiA17vA5dewVVLBugzZgcR0u23hX8+vbwFLqDac/wjxmOd9e7kw==","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":"<79252792-15c7-511c-d1b7-74b0da9d543b@oss.nxp.com>","Date":"Tue, 15 Nov 2022 11:15:18 +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":"<20221010131744.513261-1-xavier.roumegue@oss.nxp.com>\n\t<20221010131744.513261-3-xavier.roumegue@oss.nxp.com>\n\t<20221109075446.ydkhn2olumea4ano@uno.localdomain>","In-Reply-To":"<20221109075446.ydkhn2olumea4ano@uno.localdomain>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-ClientProxiedBy":"AS4P192CA0022.EURP192.PROD.OUTLOOK.COM\n\t(2603:10a6:20b:5e1::20) 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_|AS8PR04MB8691:EE_","X-MS-Office365-Filtering-Correlation-Id":"1234e2f3-6ec3-48b7-e0de-08dac6f24cc6","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":"saHzGadgXY0FpCKMDkwXzbqXvQ9Ve8Ja9DBnczNOp3vgW2pZ5y4xf1S+nUvXqMr/26ZSt6fIQ9CUTLEcgxxnQlPazZc1paGv6IU4FtGiW35PFalI6+5uvz12hsAwrsexBvPCtx3KGSmCih6Rz3TZKM6Q6NlDbOb8fyLHwLXUX97e5cqTS4+3sBAWadRs9PHbpvgsMRdhwnczOJr4J9hI9E8cV4eTO5X/sC1AdH0sGmqoMhjDT9KHHfZ3Tu014H9aD5qRlgQuNNC1MWLTc4CfVMxPYAT78AS3KTdHdEmUsfjJTjU+cxFgAdZ/nA3h7GbR/HfzLZBBZfEpDE3dsiJJ0k4D3RxDAaYxrh5rDaVSJQd6ra//XTW3D6+3uT53fkT6YAQapUe4pd9M9Ye7quH2A4ZboCzrW4riIZHQk7PaD3rECZZVGa6Rb4NGeYSx5g3SeiYaDY+KC/nIs4bzaT8KAZ+6Ec9WtntkMA23MEgyHEQOVF9+QFFMhpR+HXj00bPhsspGAvvkhMTCKTTdDdaaCv+Q2LS+oV5RmQqq3pRafOg4sMkVWtEXANNnWyxuFMCJPqbDZS77Ii19DfaOcLu1zxcaKJnaaXP7f2+tYz3huAhRrD6bNmT21droVLKtBVuqwUME1RT5jFKdAeGMQK0VQRIUo/Xw1g0UcJZoT1mjurhEzWbe37XMal4mRjOwXw3OVBzE6N+Eel4i+GckUSEKf02MDPR44myWS8rwBInkJ1P8P1iaa22xSNV/djaeP2E4AFkQ94RQugVtO2/Jzrngtf6J8vMYAqK5+itfpohTro+QFFShEyyUPGQd7JUHrCNfZrJwDt3gWHHXaGkL6sh+8krKP+lZMeaWB84aWuSjUZg=","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)(346002)(136003)(376002)(396003)(366004)(39860400002)(451199015)(6916009)(53546011)(316002)(52116002)(6506007)(6512007)(55236004)(31686004)(26005)(6486002)(478600001)(66946007)(66556008)(66476007)(83380400001)(8676002)(5660300002)(30864003)(8936002)(2616005)(186003)(41300700001)(2906002)(4326008)(38350700002)(31696002)(86362001)(38100700002)(41533002)(45980500001)(43740500002);\n\tDIR:OUT; SFP:1101; ","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"=?utf-8?q?rnBxlmfSB+soPuueTQF1LsnuE?=\n\t=?utf-8?q?EUng0Q8S7kJza0ONB+DWiOY6dtfW+WKR0yWkm0d+EZOgVA0X4VVCgYKM?=\n\t=?utf-8?q?PdEkzYvlMrTp1fQ0G58chnJWYehN5I01s/CY/OhJN/84ti9jJ3/43xfL?=\n\t=?utf-8?q?zMI2JYZrJih35WH5jFkpik62+tKDSLdCr0XHk0R5L1H657T5DVV/DrJJ?=\n\t=?utf-8?q?q5c4HtACSSUkAJ8KkH8N5QNarMZyzVzpv05Nv2b/QEmwmGvZOjR+lu4Q?=\n\t=?utf-8?q?mWTlRKrmIrB8HCQxWxtISGPVhxy9uVVD1xKjoOkAChVGrhcaHKI6iHbQ?=\n\t=?utf-8?q?xrM7TbQXvWXlrFfkJi7IjUC9Odp3uuOqazRFkSVByHZwwlgM2Mb9Moc1?=\n\t=?utf-8?q?zr3YrlKdF28PNACeca372GvDjUy9V+nasf01VlPukgYlq0d8WXNbAf2y?=\n\t=?utf-8?q?uoG+Uitti2owjVqJgftPPTr4jZtmdPDPPmK6NlHLaBv+GvJODe8MOCFJ?=\n\t=?utf-8?q?YT9pw3DAKyUhoQR6GYLd/Jkg7yplsWqQTrOKzhP59JLJ1lMEELpdsNKG?=\n\t=?utf-8?q?3G1UBnCREUmS19/jZrg679IY8UCohQHgjpfb45sUhQRWSu/hrQe/A0Se?=\n\t=?utf-8?q?xp6AuraYASdJGzOOhadMixeLaZpT5YNt8lEQv2KoGSRh7ssv08wdZl2P?=\n\t=?utf-8?q?/XKTZwMifEZDAxvrL4Y+hJY9cxi/l6dE2hXThzIVJMzwvmU0LlE9B1kC?=\n\t=?utf-8?q?DNjxxpXQBzI9s+9yX/byT+bo6JEaa4Cnuh6nvHBMehn4SOEOzR5/hWW+?=\n\t=?utf-8?q?MhWVO/ET/4NeJrG4bna64Sa4l5FDp/pk2qpubeRlKKbFRgDUd1e9ymiq?=\n\t=?utf-8?q?6QUwOu/bzairj7n7qjxu2vALHs+5zDN1A2mLFn3OHUXC/oxMS+O4iWSg?=\n\t=?utf-8?q?Pp8pUDDSzLIeOwEi2bwn+BxT8Q97AqgsGGwfUtYztxSpcLMdy6vJQZ3m?=\n\t=?utf-8?q?znnj/8FKhxnLV0yqj7ZWpomM0hGHXaJ7Kf+Rxck5WLdEKVDiKS//VCBj?=\n\t=?utf-8?q?yRFAEuCOKEbzEya8Ons7u+WxBhjxYN2DstwSSh2lfWqwEBO7eLVV6m3E?=\n\t=?utf-8?q?WapFFejqxOkfasCaDw7I2Zy1I2siQoYLZJMYMw2J34T/Dt/jhQXzc4LL?=\n\t=?utf-8?q?/pKOocuFyJ3zCvyCHLXQ44cqHZOfPcacKxfGKXMQk5UuNH8IbOMzLiyg?=\n\t=?utf-8?q?Vqa9eGK+XAxnEQbjtl8klIxKnDNzE+xOsiMjW719RFM3F1AotspIMcuq?=\n\t=?utf-8?q?rNZhR7v+S6y3bGU5nNVYILk0OnLduh+RaYkS7+ZjFosXpEA/6HwdIVtk?=\n\t=?utf-8?q?gP4pRkC2I66ANsodkSOsD89jWxHAGCep02o8q9X0xnCIpyBh3K0I+aq1?=\n\t=?utf-8?q?0yxvUoRnywDZrhQaKjpdhpfN+wcQNuN0zy8OSqHgDENp05cQg4uk3UUG?=\n\t=?utf-8?q?rDzx5atOFBbJUyJ6L0ssJY/X2F8yE+h7kdSVqvWDVEb4xAVcrPkmZVKD?=\n\t=?utf-8?q?nNVHDDkif03DTVWrUnPtuty4vKeMwdyVb+qp0/X18Bjbccc7dhorMrPU?=\n\t=?utf-8?q?LMmSJELIoXYq/LKkWrInKdELwc4Ttk4NKTPMtN91QmVQB7FwJCUN09EH?=\n\t=?utf-8?q?+z2jJXQMYpCXy1WUBIVIhxtz0lwWLfg3geUfHAzlMuZu8OG69zn3/eZY?=\n\t=?utf-8?q?0EfW0nWkggJg77f0Adp4lTc5f8S4A=3D=3D?=","X-OriginatorOrg":"oss.nxp.com","X-MS-Exchange-CrossTenant-Network-Message-Id":"1234e2f3-6ec3-48b7-e0de-08dac6f24cc6","X-MS-Exchange-CrossTenant-AuthSource":"PAXPR04MB8703.eurprd04.prod.outlook.com","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"15 Nov 2022 10:15:20.1802\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":"rr/itqkhW01BkPznRez05/KWZrprUlH0uA7DHfM3BCkoYRYCWcg4n6W0pCGB2pQk5tQEESYgXMwInt/ZEJjb+Q==","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"AS8PR04MB8691","Subject":"Re: [libcamera-devel] [PATCH 2/2] libcamera: pipeline: simple:\n\tconverter: Use generic converter 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>"}}]