[{"id":4355,"web_url":"https://patchwork.libcamera.org/comment/4355/","msgid":"<f45d4b66-5b31-9221-b122-a09b42fb56e3@ideasonboard.com>","date":"2020-03-31T12:39:37","subject":"Re: [libcamera-devel] [PATCH v3 10/11] libcamera: pipeline: simple:\n\tAdd simple format converter","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nOn 20/03/2020 01:48, Laurent Pinchart wrote:\n> The simple format converter supports V4L2 M2M devices that convert pixel\n> formats.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> Changes since v2:\n> \n> - Rebase on top of V4L2PixelFormat\n\nNot much I can spot in this one, and I'm pleased to see the V4L2 M2M\ndevice abstraction being useful/used more ;-)\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> ---\n>  src/libcamera/pipeline/simple/converter.cpp | 209 ++++++++++++++++++++\n>  src/libcamera/pipeline/simple/converter.h   |  60 ++++++\n>  src/libcamera/pipeline/simple/meson.build   |   1 +\n>  3 files changed, 270 insertions(+)\n>  create mode 100644 src/libcamera/pipeline/simple/converter.cpp\n>  create mode 100644 src/libcamera/pipeline/simple/converter.h\n> \n> diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp\n> new file mode 100644\n> index 000000000000..3025c3dea809\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/simple/converter.cpp\n> @@ -0,0 +1,209 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Laurent Pinchart\n> + *\n> + * converter.cpp - Format converter for simple pipeline handler\n> + */\n> +\n> +#include \"converter.h\"\n> +\n> +#include <algorithm>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/signal.h>\n> +\n> +#include \"log.h\"\n> +#include \"media_device.h\"\n> +#include \"v4l2_videodevice.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(SimplePipeline);\n> +\n> +SimpleConverter::SimpleConverter(MediaDevice *media)\n> +\t: m2m_(nullptr)\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> +\t\treturn;\n> +\n> +\tm2m_ = new V4L2M2MDevice((*it)->deviceNode());\n> +\n> +\tm2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);\n> +\tm2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);\n> +}\n> +\n> +SimpleConverter::~SimpleConverter()\n> +{\n> +\tdelete m2m_;\n> +}\n> +\n> +int SimpleConverter::open()\n> +{\n> +\tif (!m2m_)\n> +\t\treturn -ENODEV;\n> +\n> +\treturn m2m_->open();\n> +}\n> +\n> +void SimpleConverter::close()\n> +{\n> +\tif (m2m_)\n> +\t\tm2m_->close();\n> +}\n> +\n> +std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n> +{\n> +\tif (!m2m_)\n> +\t\treturn {};\n> +\n\n/* Set the given PixelFormat on the output queue, to determine the\nconversion capabilities at the capture queue. */\n\n(where /queue/{node,device,something else}/)\n\n> +\tV4L2DeviceFormat format;\n> +\tformat.fourcc = m2m_->output()->toV4L2PixelFormat(input);\n> +\tformat.size = { 1, 1 };\n> +\n> +\tint ret = m2m_->output()->setFormat(&format);\n> +\tif (ret < 0) {\n> +\t\tLOG(SimplePipeline, Error)\n> +\t\t\t<< \"Failed to set format: \" << strerror(-ret);\n> +\t\treturn {};\n> +\t}\n> +\n> +\tstd::vector<PixelFormat> pixelFormats;\n> +\n> +\tfor (const auto &format : m2m_->capture()->formats()) {\n> +\t\tPixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);\n> +\t\tif (pixelFormat)\n> +\t\t\tpixelFormats.push_back(pixelFormat);\n> +\t}\n> +\n> +\treturn pixelFormats;\n> +}\n> +\n> +int SimpleConverter::configure(PixelFormat inputFormat,\n> +\t\t\t       PixelFormat outputFormat, const Size &size)\n> +{\n> +\tV4L2DeviceFormat format;\n> +\tint ret;\n> +\n> +\tV4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);\n> +\tformat.fourcc = videoFormat;\n> +\tformat.size = size;\n> +\n> +\tret = m2m_->output()->setFormat(&format);\n> +\tif (ret < 0) {\n> +\t\tLOG(SimplePipeline, 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 != size) {\n> +\t\tLOG(SimplePipeline, Error)\n> +\t\t\t<< \"Input format not supported\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\tvideoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);\n> +\tformat.fourcc = videoFormat;\n> +\n> +\tret = m2m_->capture()->setFormat(&format);\n> +\tif (ret < 0) {\n> +\t\tLOG(SimplePipeline, 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 != size) {\n> +\t\tLOG(SimplePipeline, Error)\n> +\t\t\t<< \"Output format not supported\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +int SimpleConverter::exportBuffers(unsigned int count,\n> +\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n> +{\n> +\treturn m2m_->capture()->exportBuffers(count, buffers);\n> +}\n> +\n> +int SimpleConverter::start(unsigned int count)\n> +{\n> +\tint ret = m2m_->output()->importBuffers(count);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = m2m_->capture()->importBuffers(count);\n> +\tif (ret < 0) {\n> +\t\tstop();\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tret = m2m_->output()->streamOn();\n> +\tif (ret < 0) {\n> +\t\tstop();\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tret = m2m_->capture()->streamOn();\n> +\tif (ret < 0) {\n> +\t\tstop();\n> +\t\treturn ret;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +void SimpleConverter::stop()\n> +{\n> +\tm2m_->capture()->streamOff();\n> +\tm2m_->output()->streamOff();\n> +\tm2m_->capture()->releaseBuffers();\n> +\tm2m_->output()->releaseBuffers();\n> +}\n> +\n> +int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)\n> +{\n> +\tint ret = m2m_->output()->queueBuffer(input);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = m2m_->capture()->queueBuffer(output);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\treturn 0;\n> +}\n> +\n> +void SimpleConverter::captureBufferReady(FrameBuffer *buffer)\n> +{\n\nDo we need any kind of locking between these two BufferReady slots which\nessentially interact with each other? or is the queue 'safe' enough\n (or perhaps are we guaranteed to process both in the same thread?)\n\n> +\tif (!outputDoneQueue_.empty()) {\n> +\t\tFrameBuffer *other = outputDoneQueue_.front();\n> +\t\toutputDoneQueue_.pop();\n> +\t\tbufferReady.emit(other, buffer);\n> +\t} else {\n> +\t\tcaptureDoneQueue_.push(buffer);\n> +\t}\n> +}\n> +\n> +void SimpleConverter::outputBufferReady(FrameBuffer *buffer)\n> +{\n> +\tif (!captureDoneQueue_.empty()) {\n> +\t\tFrameBuffer *other = captureDoneQueue_.front();\n> +\t\tcaptureDoneQueue_.pop();\n> +\t\tbufferReady.emit(buffer, other);\n> +\t} else {\n> +\t\toutputDoneQueue_.push(buffer);\n> +\t}\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h\n> new file mode 100644\n> index 000000000000..a33071fa8578\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/simple/converter.h\n> @@ -0,0 +1,60 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Laurent Pinchart\n> + *\n> + * converter.h - Format converter for simple pipeline handler\n> + */\n> +\n> +#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__\n> +#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__\n> +\n> +#include <memory>\n> +#include <queue>\n> +#include <vector>\n> +\n> +#include <libcamera/pixelformats.h>\n> +#include <libcamera/signal.h>\n> +\n> +namespace libcamera {\n> +\n> +class FrameBuffer;\n> +class MediaDevice;\n> +struct Size;\n> +class V4L2M2MDevice;\n> +\n> +class SimpleConverter\n> +{\n> +public:\n> +\tSimpleConverter(MediaDevice *media);\n> +\t~SimpleConverter();\n> +\n> +\tint open();\n> +\tvoid close();\n> +\n> +\tstd::vector<PixelFormat> formats(PixelFormat input);\n> +\n> +\tint configure(PixelFormat inputFormat, PixelFormat outputFormat,\n> +\t\t      const Size &size);\n> +\tint exportBuffers(unsigned int count,\n> +\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers);\n> +\n> +\tint start(unsigned int count);\n> +\tvoid stop();\n> +\n> +\tint queueBuffers(FrameBuffer *input, FrameBuffer *output);\n> +\n> +\tSignal<FrameBuffer *, FrameBuffer *> bufferReady;\n> +\n> +private:\n> +\tvoid captureBufferReady(FrameBuffer *buffer);\n> +\tvoid outputBufferReady(FrameBuffer *buffer);\n> +\n> +\tV4L2M2MDevice *m2m_;\n> +\n> +\tstd::queue<FrameBuffer *> captureDoneQueue_;\n> +\tstd::queue<FrameBuffer *> outputDoneQueue_;\n> +};\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */\n> diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build\n> index 4945a3e173cf..8372f24e3788 100644\n> --- a/src/libcamera/pipeline/simple/meson.build\n> +++ b/src/libcamera/pipeline/simple/meson.build\n> @@ -1,3 +1,4 @@\n>  libcamera_sources += files([\n> +    'converter.cpp',\n>      'simple.cpp',\n>  ])\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6BD486040B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 Mar 2020 14:39:41 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CA071B18;\n\tTue, 31 Mar 2020 14:39:40 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"Z2680ych\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1585658381;\n\tbh=TXyn05i73Dhfs6mx3smajKuQBsGCLrXgujI2/jzrwvQ=;\n\th=Reply-To:Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=Z2680ychkQT3fKj6wF0o3aCNzYu0LenrL/Djt1q9w6NsRGiqRiJ1s/wjyRkqRD5CK\n\tENUeov2VL8UTeUefBWS1z6qCSQbh8FPxIKYW9byIxNVx+EKV6v8/Ux1uinIIYNyrA+\n\tZD/fvrGaORYueGShJqISEsrMZQ9R1FNfbgo78kjk=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Cc":"Martijn Braam <martijn@brixit.nl>,\n\tBenjamin GAIGNARD <benjamin.gaignard@st.com>","References":"<20200320014839.14683-1-laurent.pinchart@ideasonboard.com>\n\t<20200320014839.14683-11-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<f45d4b66-5b31-9221-b122-a09b42fb56e3@ideasonboard.com>","Date":"Tue, 31 Mar 2020 13:39:37 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.9.1","MIME-Version":"1.0","In-Reply-To":"<20200320014839.14683-11-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v3 10/11] libcamera: pipeline: simple:\n\tAdd simple format converter","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>","X-List-Received-Date":"Tue, 31 Mar 2020 12:39:41 -0000"}},{"id":4381,"web_url":"https://patchwork.libcamera.org/comment/4381/","msgid":"<20200404002449.GH9690@pendragon.ideasonboard.com>","date":"2020-04-04T00:24:49","subject":"Re: [libcamera-devel] [PATCH v3 10/11] libcamera: pipeline: simple:\n\tAdd simple format converter","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Tue, Mar 31, 2020 at 01:39:37PM +0100, Kieran Bingham wrote:\n> On 20/03/2020 01:48, Laurent Pinchart wrote:\n> > The simple format converter supports V4L2 M2M devices that convert pixel\n> > formats.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > Changes since v2:\n> > \n> > - Rebase on top of V4L2PixelFormat\n> \n> Not much I can spot in this one, and I'm pleased to see the V4L2 M2M\n> device abstraction being useful/used more ;-)\n> \n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> > ---\n> >  src/libcamera/pipeline/simple/converter.cpp | 209 ++++++++++++++++++++\n> >  src/libcamera/pipeline/simple/converter.h   |  60 ++++++\n> >  src/libcamera/pipeline/simple/meson.build   |   1 +\n> >  3 files changed, 270 insertions(+)\n> >  create mode 100644 src/libcamera/pipeline/simple/converter.cpp\n> >  create mode 100644 src/libcamera/pipeline/simple/converter.h\n> > \n> > diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp\n> > new file mode 100644\n> > index 000000000000..3025c3dea809\n> > --- /dev/null\n> > +++ b/src/libcamera/pipeline/simple/converter.cpp\n> > @@ -0,0 +1,209 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Laurent Pinchart\n> > + *\n> > + * converter.cpp - Format converter for simple pipeline handler\n> > + */\n> > +\n> > +#include \"converter.h\"\n> > +\n> > +#include <algorithm>\n> > +\n> > +#include <libcamera/buffer.h>\n> > +#include <libcamera/geometry.h>\n> > +#include <libcamera/signal.h>\n> > +\n> > +#include \"log.h\"\n> > +#include \"media_device.h\"\n> > +#include \"v4l2_videodevice.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(SimplePipeline);\n> > +\n> > +SimpleConverter::SimpleConverter(MediaDevice *media)\n> > +\t: m2m_(nullptr)\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> > +\t\treturn;\n> > +\n> > +\tm2m_ = new V4L2M2MDevice((*it)->deviceNode());\n> > +\n> > +\tm2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);\n> > +\tm2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);\n> > +}\n> > +\n> > +SimpleConverter::~SimpleConverter()\n> > +{\n> > +\tdelete m2m_;\n> > +}\n> > +\n> > +int SimpleConverter::open()\n> > +{\n> > +\tif (!m2m_)\n> > +\t\treturn -ENODEV;\n> > +\n> > +\treturn m2m_->open();\n> > +}\n> > +\n> > +void SimpleConverter::close()\n> > +{\n> > +\tif (m2m_)\n> > +\t\tm2m_->close();\n> > +}\n> > +\n> > +std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)\n> > +{\n> > +\tif (!m2m_)\n> > +\t\treturn {};\n> > +\n> \n> /* Set the given PixelFormat on the output queue, to determine the\n> conversion capabilities at the capture queue. */\n> \n> (where /queue/{node,device,something else}/)\n\n        /*\n         * Set the format on the input side (V4L2 output) of the converter to\n         * enumerate the conversion capabilities on its output (V4L2 capture).\n         */\n\n> > +\tV4L2DeviceFormat format;\n> > +\tformat.fourcc = m2m_->output()->toV4L2PixelFormat(input);\n> > +\tformat.size = { 1, 1 };\n> > +\n> > +\tint ret = m2m_->output()->setFormat(&format);\n> > +\tif (ret < 0) {\n> > +\t\tLOG(SimplePipeline, Error)\n> > +\t\t\t<< \"Failed to set format: \" << strerror(-ret);\n> > +\t\treturn {};\n> > +\t}\n> > +\n> > +\tstd::vector<PixelFormat> pixelFormats;\n> > +\n> > +\tfor (const auto &format : m2m_->capture()->formats()) {\n> > +\t\tPixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);\n> > +\t\tif (pixelFormat)\n> > +\t\t\tpixelFormats.push_back(pixelFormat);\n> > +\t}\n> > +\n> > +\treturn pixelFormats;\n> > +}\n> > +\n> > +int SimpleConverter::configure(PixelFormat inputFormat,\n> > +\t\t\t       PixelFormat outputFormat, const Size &size)\n> > +{\n> > +\tV4L2DeviceFormat format;\n> > +\tint ret;\n> > +\n> > +\tV4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);\n> > +\tformat.fourcc = videoFormat;\n> > +\tformat.size = size;\n> > +\n> > +\tret = m2m_->output()->setFormat(&format);\n> > +\tif (ret < 0) {\n> > +\t\tLOG(SimplePipeline, 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 != size) {\n> > +\t\tLOG(SimplePipeline, Error)\n> > +\t\t\t<< \"Input format not supported\";\n> > +\t\treturn -EINVAL;\n> > +\t}\n> > +\n> > +\tvideoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);\n> > +\tformat.fourcc = videoFormat;\n> > +\n> > +\tret = m2m_->capture()->setFormat(&format);\n> > +\tif (ret < 0) {\n> > +\t\tLOG(SimplePipeline, 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 != size) {\n> > +\t\tLOG(SimplePipeline, Error)\n> > +\t\t\t<< \"Output format not supported\";\n> > +\t\treturn -EINVAL;\n> > +\t}\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +int SimpleConverter::exportBuffers(unsigned int count,\n> > +\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n> > +{\n> > +\treturn m2m_->capture()->exportBuffers(count, buffers);\n> > +}\n> > +\n> > +int SimpleConverter::start(unsigned int count)\n> > +{\n> > +\tint ret = m2m_->output()->importBuffers(count);\n> > +\tif (ret < 0)\n> > +\t\treturn ret;\n> > +\n> > +\tret = m2m_->capture()->importBuffers(count);\n> > +\tif (ret < 0) {\n> > +\t\tstop();\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tret = m2m_->output()->streamOn();\n> > +\tif (ret < 0) {\n> > +\t\tstop();\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tret = m2m_->capture()->streamOn();\n> > +\tif (ret < 0) {\n> > +\t\tstop();\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +void SimpleConverter::stop()\n> > +{\n> > +\tm2m_->capture()->streamOff();\n> > +\tm2m_->output()->streamOff();\n> > +\tm2m_->capture()->releaseBuffers();\n> > +\tm2m_->output()->releaseBuffers();\n> > +}\n> > +\n> > +int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)\n> > +{\n> > +\tint ret = m2m_->output()->queueBuffer(input);\n> > +\tif (ret < 0)\n> > +\t\treturn ret;\n> > +\n> > +\tret = m2m_->capture()->queueBuffer(output);\n> > +\tif (ret < 0)\n> > +\t\treturn ret;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +void SimpleConverter::captureBufferReady(FrameBuffer *buffer)\n> > +{\n> \n> Do we need any kind of locking between these two BufferReady slots which\n> essentially interact with each other? or is the queue 'safe' enough\n>  (or perhaps are we guaranteed to process both in the same thread?)\n\nIt's all single-threaded code, so no locking is needed.\n\n> > +\tif (!outputDoneQueue_.empty()) {\n> > +\t\tFrameBuffer *other = outputDoneQueue_.front();\n> > +\t\toutputDoneQueue_.pop();\n> > +\t\tbufferReady.emit(other, buffer);\n> > +\t} else {\n> > +\t\tcaptureDoneQueue_.push(buffer);\n> > +\t}\n> > +}\n> > +\n> > +void SimpleConverter::outputBufferReady(FrameBuffer *buffer)\n> > +{\n> > +\tif (!captureDoneQueue_.empty()) {\n> > +\t\tFrameBuffer *other = captureDoneQueue_.front();\n> > +\t\tcaptureDoneQueue_.pop();\n> > +\t\tbufferReady.emit(buffer, other);\n> > +\t} else {\n> > +\t\toutputDoneQueue_.push(buffer);\n> > +\t}\n> > +}\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h\n> > new file mode 100644\n> > index 000000000000..a33071fa8578\n> > --- /dev/null\n> > +++ b/src/libcamera/pipeline/simple/converter.h\n> > @@ -0,0 +1,60 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Laurent Pinchart\n> > + *\n> > + * converter.h - Format converter for simple pipeline handler\n> > + */\n> > +\n> > +#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__\n> > +#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__\n> > +\n> > +#include <memory>\n> > +#include <queue>\n> > +#include <vector>\n> > +\n> > +#include <libcamera/pixelformats.h>\n> > +#include <libcamera/signal.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +class FrameBuffer;\n> > +class MediaDevice;\n> > +struct Size;\n> > +class V4L2M2MDevice;\n> > +\n> > +class SimpleConverter\n> > +{\n> > +public:\n> > +\tSimpleConverter(MediaDevice *media);\n> > +\t~SimpleConverter();\n> > +\n> > +\tint open();\n> > +\tvoid close();\n> > +\n> > +\tstd::vector<PixelFormat> formats(PixelFormat input);\n> > +\n> > +\tint configure(PixelFormat inputFormat, PixelFormat outputFormat,\n> > +\t\t      const Size &size);\n> > +\tint exportBuffers(unsigned int count,\n> > +\t\t\t  std::vector<std::unique_ptr<FrameBuffer>> *buffers);\n> > +\n> > +\tint start(unsigned int count);\n> > +\tvoid stop();\n> > +\n> > +\tint queueBuffers(FrameBuffer *input, FrameBuffer *output);\n> > +\n> > +\tSignal<FrameBuffer *, FrameBuffer *> bufferReady;\n> > +\n> > +private:\n> > +\tvoid captureBufferReady(FrameBuffer *buffer);\n> > +\tvoid outputBufferReady(FrameBuffer *buffer);\n> > +\n> > +\tV4L2M2MDevice *m2m_;\n> > +\n> > +\tstd::queue<FrameBuffer *> captureDoneQueue_;\n> > +\tstd::queue<FrameBuffer *> outputDoneQueue_;\n> > +};\n> > +\n> > +} /* namespace libcamera */\n> > +\n> > +#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */\n> > diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build\n> > index 4945a3e173cf..8372f24e3788 100644\n> > --- a/src/libcamera/pipeline/simple/meson.build\n> > +++ b/src/libcamera/pipeline/simple/meson.build\n> > @@ -1,3 +1,4 @@\n> >  libcamera_sources += files([\n> > +    'converter.cpp',\n> >      'simple.cpp',\n> >  ])\n> >","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 75CFE605D1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat,  4 Apr 2020 02:24:58 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id D5487321;\n\tSat,  4 Apr 2020 02:24:57 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"gncL9n9G\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1585959898;\n\tbh=oBeTdL6mEQ4l96wyUIyJVOtnOzvZ5EQs/c63eWatmoQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=gncL9n9G9RlfKDOYxZVtXJ0vlSqoDbEVoRoUzrqhZGd5mM5QIzZ6ZqwmktTeJ2YjR\n\tFZLbhOei+I9hrQQMMM3T5FjU8vpFSc4CQV2rVHXIFrhPqcnMVchLnSttackNEseRZd\n\taFZMPIJQGOrQK4o+mMm1uVUOpU/QDYtsj1v8RnNQ=","Date":"Sat, 4 Apr 2020 03:24:49 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, Martijn Braam <martijn@brixit.nl>, \n\tBenjamin GAIGNARD <benjamin.gaignard@st.com>","Message-ID":"<20200404002449.GH9690@pendragon.ideasonboard.com>","References":"<20200320014839.14683-1-laurent.pinchart@ideasonboard.com>\n\t<20200320014839.14683-11-laurent.pinchart@ideasonboard.com>\n\t<f45d4b66-5b31-9221-b122-a09b42fb56e3@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<f45d4b66-5b31-9221-b122-a09b42fb56e3@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v3 10/11] libcamera: pipeline: simple:\n\tAdd simple format converter","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>","X-List-Received-Date":"Sat, 04 Apr 2020 00:24:58 -0000"}}]