[libcamera-devel,v4,10/11] libcamera: pipeline: simple: Add simple format converter

Message ID 20200404004438.17992-11-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • Simple pipeline handler
Related show

Commit Message

Laurent Pinchart April 4, 2020, 12:44 a.m. UTC
The simple format converter supports V4L2 M2M devices that convert pixel
formats.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
Changes since v3:

- Add a comment to explain format enumeration

Changes since v2:

- Rebase on top of V4L2PixelFormat
---
 src/libcamera/pipeline/simple/converter.cpp | 213 ++++++++++++++++++++
 src/libcamera/pipeline/simple/converter.h   |  60 ++++++
 src/libcamera/pipeline/simple/meson.build   |   1 +
 3 files changed, 274 insertions(+)
 create mode 100644 src/libcamera/pipeline/simple/converter.cpp
 create mode 100644 src/libcamera/pipeline/simple/converter.h

Comments

Niklas Söderlund April 21, 2020, 3:57 p.m. UTC | #1
Hi Laurent,

Thanks for your work.

On 2020-04-04 03:44:37 +0300, Laurent Pinchart wrote:
> The simple format converter supports V4L2 M2M devices that convert pixel
> formats.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
> Changes since v3:
> 
> - Add a comment to explain format enumeration
> 
> Changes since v2:
> 
> - Rebase on top of V4L2PixelFormat
> ---
>  src/libcamera/pipeline/simple/converter.cpp | 213 ++++++++++++++++++++
>  src/libcamera/pipeline/simple/converter.h   |  60 ++++++
>  src/libcamera/pipeline/simple/meson.build   |   1 +
>  3 files changed, 274 insertions(+)
>  create mode 100644 src/libcamera/pipeline/simple/converter.cpp
>  create mode 100644 src/libcamera/pipeline/simple/converter.h
> 
> diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp
> new file mode 100644
> index 000000000000..50e554147c5f
> --- /dev/null
> +++ b/src/libcamera/pipeline/simple/converter.cpp
> @@ -0,0 +1,213 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Laurent Pinchart
> + *
> + * converter.cpp - Format converter for simple pipeline handler
> + */
> +
> +#include "converter.h"
> +
> +#include <algorithm>
> +
> +#include <libcamera/buffer.h>
> +#include <libcamera/geometry.h>
> +#include <libcamera/signal.h>
> +
> +#include "log.h"
> +#include "media_device.h"
> +#include "v4l2_videodevice.h"
> +
> +namespace libcamera {
> +
> +LOG_DECLARE_CATEGORY(SimplePipeline);
> +
> +SimpleConverter::SimpleConverter(MediaDevice *media)
> +	: m2m_(nullptr)
> +{
> +	/*
> +	 * Locate the video node. There's no need to validate the pipeline
> +	 * further, the caller guarantees that this is a V4L2 mem2mem device.
> +	 */
> +	const std::vector<MediaEntity *> &entities = media->entities();
> +	auto it = std::find_if(entities.begin(), entities.end(),
> +			       [](MediaEntity *entity) {
> +				       return entity->function() == MEDIA_ENT_F_IO_V4L;
> +			       });
> +	if (it == entities.end())
> +		return;
> +
> +	m2m_ = new V4L2M2MDevice((*it)->deviceNode());
> +
> +	m2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);
> +	m2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);
> +}
> +
> +SimpleConverter::~SimpleConverter()
> +{
> +	delete m2m_;
> +}
> +
> +int SimpleConverter::open()
> +{
> +	if (!m2m_)
> +		return -ENODEV;
> +
> +	return m2m_->open();
> +}
> +
> +void SimpleConverter::close()
> +{
> +	if (m2m_)
> +		m2m_->close();
> +}
> +
> +std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)
> +{
> +	if (!m2m_)
> +		return {};
> +
> +	/*
> +	 * Set the format on the input side (V4L2 output) of the converter to
> +	 * enumerate the conversion capabilities on its output (V4L2 capture).
> +	 */
> +	V4L2DeviceFormat format;
> +	format.fourcc = m2m_->output()->toV4L2PixelFormat(input);
> +	format.size = { 1, 1 };
> +
> +	int ret = m2m_->output()->setFormat(&format);
> +	if (ret < 0) {
> +		LOG(SimplePipeline, Error)
> +			<< "Failed to set format: " << strerror(-ret);
> +		return {};
> +	}
> +
> +	std::vector<PixelFormat> pixelFormats;
> +
> +	for (const auto &format : m2m_->capture()->formats()) {
> +		PixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);
> +		if (pixelFormat)
> +			pixelFormats.push_back(pixelFormat);
> +	}
> +
> +	return pixelFormats;
> +}
> +
> +int SimpleConverter::configure(PixelFormat inputFormat,
> +			       PixelFormat outputFormat, const Size &size)
> +{
> +	V4L2DeviceFormat format;
> +	int ret;
> +
> +	V4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);
> +	format.fourcc = videoFormat;
> +	format.size = size;
> +
> +	ret = m2m_->output()->setFormat(&format);
> +	if (ret < 0) {
> +		LOG(SimplePipeline, Error)
> +			<< "Failed to set input format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	if (format.fourcc != videoFormat || format.size != size) {
> +		LOG(SimplePipeline, Error)
> +			<< "Input format not supported";
> +		return -EINVAL;
> +	}
> +
> +	videoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);
> +	format.fourcc = videoFormat;

I wound set format.size here as well even if it's not strictly needed as 
it adds to readability. With or without this fixed tho,

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> +
> +	ret = m2m_->capture()->setFormat(&format);
> +	if (ret < 0) {
> +		LOG(SimplePipeline, Error)
> +			<< "Failed to set output format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	if (format.fourcc != videoFormat || format.size != size) {
> +		LOG(SimplePipeline, Error)
> +			<< "Output format not supported";
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int SimpleConverter::exportBuffers(unsigned int count,
> +				   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> +{
> +	return m2m_->capture()->exportBuffers(count, buffers);
> +}
> +
> +int SimpleConverter::start(unsigned int count)
> +{
> +	int ret = m2m_->output()->importBuffers(count);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = m2m_->capture()->importBuffers(count);
> +	if (ret < 0) {
> +		stop();
> +		return ret;
> +	}
> +
> +	ret = m2m_->output()->streamOn();
> +	if (ret < 0) {
> +		stop();
> +		return ret;
> +	}
> +
> +	ret = m2m_->capture()->streamOn();
> +	if (ret < 0) {
> +		stop();
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void SimpleConverter::stop()
> +{
> +	m2m_->capture()->streamOff();
> +	m2m_->output()->streamOff();
> +	m2m_->capture()->releaseBuffers();
> +	m2m_->output()->releaseBuffers();
> +}
> +
> +int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)
> +{
> +	int ret = m2m_->output()->queueBuffer(input);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = m2m_->capture()->queueBuffer(output);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +void SimpleConverter::captureBufferReady(FrameBuffer *buffer)
> +{
> +	if (!outputDoneQueue_.empty()) {
> +		FrameBuffer *other = outputDoneQueue_.front();
> +		outputDoneQueue_.pop();
> +		bufferReady.emit(other, buffer);
> +	} else {
> +		captureDoneQueue_.push(buffer);
> +	}
> +}
> +
> +void SimpleConverter::outputBufferReady(FrameBuffer *buffer)
> +{
> +	if (!captureDoneQueue_.empty()) {
> +		FrameBuffer *other = captureDoneQueue_.front();
> +		captureDoneQueue_.pop();
> +		bufferReady.emit(buffer, other);
> +	} else {
> +		outputDoneQueue_.push(buffer);
> +	}
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h
> new file mode 100644
> index 000000000000..a33071fa8578
> --- /dev/null
> +++ b/src/libcamera/pipeline/simple/converter.h
> @@ -0,0 +1,60 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Laurent Pinchart
> + *
> + * converter.h - Format converter for simple pipeline handler
> + */
> +
> +#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> +#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> +
> +#include <memory>
> +#include <queue>
> +#include <vector>
> +
> +#include <libcamera/pixelformats.h>
> +#include <libcamera/signal.h>
> +
> +namespace libcamera {
> +
> +class FrameBuffer;
> +class MediaDevice;
> +struct Size;
> +class V4L2M2MDevice;
> +
> +class SimpleConverter
> +{
> +public:
> +	SimpleConverter(MediaDevice *media);
> +	~SimpleConverter();
> +
> +	int open();
> +	void close();
> +
> +	std::vector<PixelFormat> formats(PixelFormat input);
> +
> +	int configure(PixelFormat inputFormat, PixelFormat outputFormat,
> +		      const Size &size);
> +	int exportBuffers(unsigned int count,
> +			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
> +
> +	int start(unsigned int count);
> +	void stop();
> +
> +	int queueBuffers(FrameBuffer *input, FrameBuffer *output);
> +
> +	Signal<FrameBuffer *, FrameBuffer *> bufferReady;
> +
> +private:
> +	void captureBufferReady(FrameBuffer *buffer);
> +	void outputBufferReady(FrameBuffer *buffer);
> +
> +	V4L2M2MDevice *m2m_;
> +
> +	std::queue<FrameBuffer *> captureDoneQueue_;
> +	std::queue<FrameBuffer *> outputDoneQueue_;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */
> diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build
> index 4945a3e173cf..8372f24e3788 100644
> --- a/src/libcamera/pipeline/simple/meson.build
> +++ b/src/libcamera/pipeline/simple/meson.build
> @@ -1,3 +1,4 @@
>  libcamera_sources += files([
> +    'converter.cpp',
>      'simple.cpp',
>  ])
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart April 21, 2020, 7:27 p.m. UTC | #2
Hi Niklas,

On Tue, Apr 21, 2020 at 05:57:05PM +0200, Niklas Söderlund wrote:
> On 2020-04-04 03:44:37 +0300, Laurent Pinchart wrote:
> > The simple format converter supports V4L2 M2M devices that convert pixel
> > formats.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > ---
> > Changes since v3:
> > 
> > - Add a comment to explain format enumeration
> > 
> > Changes since v2:
> > 
> > - Rebase on top of V4L2PixelFormat
> > ---
> >  src/libcamera/pipeline/simple/converter.cpp | 213 ++++++++++++++++++++
> >  src/libcamera/pipeline/simple/converter.h   |  60 ++++++
> >  src/libcamera/pipeline/simple/meson.build   |   1 +
> >  3 files changed, 274 insertions(+)
> >  create mode 100644 src/libcamera/pipeline/simple/converter.cpp
> >  create mode 100644 src/libcamera/pipeline/simple/converter.h
> > 
> > diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp
> > new file mode 100644
> > index 000000000000..50e554147c5f
> > --- /dev/null
> > +++ b/src/libcamera/pipeline/simple/converter.cpp
> > @@ -0,0 +1,213 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2020, Laurent Pinchart
> > + *
> > + * converter.cpp - Format converter for simple pipeline handler
> > + */
> > +
> > +#include "converter.h"
> > +
> > +#include <algorithm>
> > +
> > +#include <libcamera/buffer.h>
> > +#include <libcamera/geometry.h>
> > +#include <libcamera/signal.h>
> > +
> > +#include "log.h"
> > +#include "media_device.h"
> > +#include "v4l2_videodevice.h"
> > +
> > +namespace libcamera {
> > +
> > +LOG_DECLARE_CATEGORY(SimplePipeline);
> > +
> > +SimpleConverter::SimpleConverter(MediaDevice *media)
> > +	: m2m_(nullptr)
> > +{
> > +	/*
> > +	 * Locate the video node. There's no need to validate the pipeline
> > +	 * further, the caller guarantees that this is a V4L2 mem2mem device.
> > +	 */
> > +	const std::vector<MediaEntity *> &entities = media->entities();
> > +	auto it = std::find_if(entities.begin(), entities.end(),
> > +			       [](MediaEntity *entity) {
> > +				       return entity->function() == MEDIA_ENT_F_IO_V4L;
> > +			       });
> > +	if (it == entities.end())
> > +		return;
> > +
> > +	m2m_ = new V4L2M2MDevice((*it)->deviceNode());
> > +
> > +	m2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);
> > +	m2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);
> > +}
> > +
> > +SimpleConverter::~SimpleConverter()
> > +{
> > +	delete m2m_;
> > +}
> > +
> > +int SimpleConverter::open()
> > +{
> > +	if (!m2m_)
> > +		return -ENODEV;
> > +
> > +	return m2m_->open();
> > +}
> > +
> > +void SimpleConverter::close()
> > +{
> > +	if (m2m_)
> > +		m2m_->close();
> > +}
> > +
> > +std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)
> > +{
> > +	if (!m2m_)
> > +		return {};
> > +
> > +	/*
> > +	 * Set the format on the input side (V4L2 output) of the converter to
> > +	 * enumerate the conversion capabilities on its output (V4L2 capture).
> > +	 */
> > +	V4L2DeviceFormat format;
> > +	format.fourcc = m2m_->output()->toV4L2PixelFormat(input);
> > +	format.size = { 1, 1 };
> > +
> > +	int ret = m2m_->output()->setFormat(&format);
> > +	if (ret < 0) {
> > +		LOG(SimplePipeline, Error)
> > +			<< "Failed to set format: " << strerror(-ret);
> > +		return {};
> > +	}
> > +
> > +	std::vector<PixelFormat> pixelFormats;
> > +
> > +	for (const auto &format : m2m_->capture()->formats()) {
> > +		PixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);
> > +		if (pixelFormat)
> > +			pixelFormats.push_back(pixelFormat);
> > +	}
> > +
> > +	return pixelFormats;
> > +}
> > +
> > +int SimpleConverter::configure(PixelFormat inputFormat,
> > +			       PixelFormat outputFormat, const Size &size)
> > +{
> > +	V4L2DeviceFormat format;
> > +	int ret;
> > +
> > +	V4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);
> > +	format.fourcc = videoFormat;
> > +	format.size = size;
> > +
> > +	ret = m2m_->output()->setFormat(&format);
> > +	if (ret < 0) {
> > +		LOG(SimplePipeline, Error)
> > +			<< "Failed to set input format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	if (format.fourcc != videoFormat || format.size != size) {
> > +		LOG(SimplePipeline, Error)
> > +			<< "Input format not supported";
> > +		return -EINVAL;
> > +	}
> > +
> > +	videoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);
> > +	format.fourcc = videoFormat;
> 
> I wound set format.size here as well even if it's not strictly needed as 
> it adds to readability. With or without this fixed tho,

Both gcc 9 and clang 10 fail to optimize that away though :-S It's not
big deal in this specific case, but do we generally want to do so ?
Maybe a comment would be better ?

> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> 
> > +
> > +	ret = m2m_->capture()->setFormat(&format);
> > +	if (ret < 0) {
> > +		LOG(SimplePipeline, Error)
> > +			<< "Failed to set output format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	if (format.fourcc != videoFormat || format.size != size) {
> > +		LOG(SimplePipeline, Error)
> > +			<< "Output format not supported";
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int SimpleConverter::exportBuffers(unsigned int count,
> > +				   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> > +{
> > +	return m2m_->capture()->exportBuffers(count, buffers);
> > +}
> > +
> > +int SimpleConverter::start(unsigned int count)
> > +{
> > +	int ret = m2m_->output()->importBuffers(count);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = m2m_->capture()->importBuffers(count);
> > +	if (ret < 0) {
> > +		stop();
> > +		return ret;
> > +	}
> > +
> > +	ret = m2m_->output()->streamOn();
> > +	if (ret < 0) {
> > +		stop();
> > +		return ret;
> > +	}
> > +
> > +	ret = m2m_->capture()->streamOn();
> > +	if (ret < 0) {
> > +		stop();
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +void SimpleConverter::stop()
> > +{
> > +	m2m_->capture()->streamOff();
> > +	m2m_->output()->streamOff();
> > +	m2m_->capture()->releaseBuffers();
> > +	m2m_->output()->releaseBuffers();
> > +}
> > +
> > +int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)
> > +{
> > +	int ret = m2m_->output()->queueBuffer(input);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = m2m_->capture()->queueBuffer(output);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +void SimpleConverter::captureBufferReady(FrameBuffer *buffer)
> > +{
> > +	if (!outputDoneQueue_.empty()) {
> > +		FrameBuffer *other = outputDoneQueue_.front();
> > +		outputDoneQueue_.pop();
> > +		bufferReady.emit(other, buffer);
> > +	} else {
> > +		captureDoneQueue_.push(buffer);
> > +	}
> > +}
> > +
> > +void SimpleConverter::outputBufferReady(FrameBuffer *buffer)
> > +{
> > +	if (!captureDoneQueue_.empty()) {
> > +		FrameBuffer *other = captureDoneQueue_.front();
> > +		captureDoneQueue_.pop();
> > +		bufferReady.emit(buffer, other);
> > +	} else {
> > +		outputDoneQueue_.push(buffer);
> > +	}
> > +}
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h
> > new file mode 100644
> > index 000000000000..a33071fa8578
> > --- /dev/null
> > +++ b/src/libcamera/pipeline/simple/converter.h
> > @@ -0,0 +1,60 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2020, Laurent Pinchart
> > + *
> > + * converter.h - Format converter for simple pipeline handler
> > + */
> > +
> > +#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> > +#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> > +
> > +#include <memory>
> > +#include <queue>
> > +#include <vector>
> > +
> > +#include <libcamera/pixelformats.h>
> > +#include <libcamera/signal.h>
> > +
> > +namespace libcamera {
> > +
> > +class FrameBuffer;
> > +class MediaDevice;
> > +struct Size;
> > +class V4L2M2MDevice;
> > +
> > +class SimpleConverter
> > +{
> > +public:
> > +	SimpleConverter(MediaDevice *media);
> > +	~SimpleConverter();
> > +
> > +	int open();
> > +	void close();
> > +
> > +	std::vector<PixelFormat> formats(PixelFormat input);
> > +
> > +	int configure(PixelFormat inputFormat, PixelFormat outputFormat,
> > +		      const Size &size);
> > +	int exportBuffers(unsigned int count,
> > +			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
> > +
> > +	int start(unsigned int count);
> > +	void stop();
> > +
> > +	int queueBuffers(FrameBuffer *input, FrameBuffer *output);
> > +
> > +	Signal<FrameBuffer *, FrameBuffer *> bufferReady;
> > +
> > +private:
> > +	void captureBufferReady(FrameBuffer *buffer);
> > +	void outputBufferReady(FrameBuffer *buffer);
> > +
> > +	V4L2M2MDevice *m2m_;
> > +
> > +	std::queue<FrameBuffer *> captureDoneQueue_;
> > +	std::queue<FrameBuffer *> outputDoneQueue_;
> > +};
> > +
> > +} /* namespace libcamera */
> > +
> > +#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */
> > diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build
> > index 4945a3e173cf..8372f24e3788 100644
> > --- a/src/libcamera/pipeline/simple/meson.build
> > +++ b/src/libcamera/pipeline/simple/meson.build
> > @@ -1,3 +1,4 @@
> >  libcamera_sources += files([
> > +    'converter.cpp',
> >      'simple.cpp',
> >  ])
Niklas Söderlund April 21, 2020, 8:17 p.m. UTC | #3
Hi Laurent,

On 2020-04-21 22:27:49 +0300, Laurent Pinchart wrote:
> Hi Niklas,
> 
> On Tue, Apr 21, 2020 at 05:57:05PM +0200, Niklas Söderlund wrote:
> > On 2020-04-04 03:44:37 +0300, Laurent Pinchart wrote:
> > > The simple format converter supports V4L2 M2M devices that convert pixel
> > > formats.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > ---
> > > Changes since v3:
> > > 
> > > - Add a comment to explain format enumeration
> > > 
> > > Changes since v2:
> > > 
> > > - Rebase on top of V4L2PixelFormat
> > > ---
> > >  src/libcamera/pipeline/simple/converter.cpp | 213 ++++++++++++++++++++
> > >  src/libcamera/pipeline/simple/converter.h   |  60 ++++++
> > >  src/libcamera/pipeline/simple/meson.build   |   1 +
> > >  3 files changed, 274 insertions(+)
> > >  create mode 100644 src/libcamera/pipeline/simple/converter.cpp
> > >  create mode 100644 src/libcamera/pipeline/simple/converter.h
> > > 
> > > diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp
> > > new file mode 100644
> > > index 000000000000..50e554147c5f
> > > --- /dev/null
> > > +++ b/src/libcamera/pipeline/simple/converter.cpp
> > > @@ -0,0 +1,213 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2020, Laurent Pinchart
> > > + *
> > > + * converter.cpp - Format converter for simple pipeline handler
> > > + */
> > > +
> > > +#include "converter.h"
> > > +
> > > +#include <algorithm>
> > > +
> > > +#include <libcamera/buffer.h>
> > > +#include <libcamera/geometry.h>
> > > +#include <libcamera/signal.h>
> > > +
> > > +#include "log.h"
> > > +#include "media_device.h"
> > > +#include "v4l2_videodevice.h"
> > > +
> > > +namespace libcamera {
> > > +
> > > +LOG_DECLARE_CATEGORY(SimplePipeline);
> > > +
> > > +SimpleConverter::SimpleConverter(MediaDevice *media)
> > > +	: m2m_(nullptr)
> > > +{
> > > +	/*
> > > +	 * Locate the video node. There's no need to validate the pipeline
> > > +	 * further, the caller guarantees that this is a V4L2 mem2mem device.
> > > +	 */
> > > +	const std::vector<MediaEntity *> &entities = media->entities();
> > > +	auto it = std::find_if(entities.begin(), entities.end(),
> > > +			       [](MediaEntity *entity) {
> > > +				       return entity->function() == MEDIA_ENT_F_IO_V4L;
> > > +			       });
> > > +	if (it == entities.end())
> > > +		return;
> > > +
> > > +	m2m_ = new V4L2M2MDevice((*it)->deviceNode());
> > > +
> > > +	m2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);
> > > +	m2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);
> > > +}
> > > +
> > > +SimpleConverter::~SimpleConverter()
> > > +{
> > > +	delete m2m_;
> > > +}
> > > +
> > > +int SimpleConverter::open()
> > > +{
> > > +	if (!m2m_)
> > > +		return -ENODEV;
> > > +
> > > +	return m2m_->open();
> > > +}
> > > +
> > > +void SimpleConverter::close()
> > > +{
> > > +	if (m2m_)
> > > +		m2m_->close();
> > > +}
> > > +
> > > +std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)
> > > +{
> > > +	if (!m2m_)
> > > +		return {};
> > > +
> > > +	/*
> > > +	 * Set the format on the input side (V4L2 output) of the converter to
> > > +	 * enumerate the conversion capabilities on its output (V4L2 capture).
> > > +	 */
> > > +	V4L2DeviceFormat format;
> > > +	format.fourcc = m2m_->output()->toV4L2PixelFormat(input);
> > > +	format.size = { 1, 1 };
> > > +
> > > +	int ret = m2m_->output()->setFormat(&format);
> > > +	if (ret < 0) {
> > > +		LOG(SimplePipeline, Error)
> > > +			<< "Failed to set format: " << strerror(-ret);
> > > +		return {};
> > > +	}
> > > +
> > > +	std::vector<PixelFormat> pixelFormats;
> > > +
> > > +	for (const auto &format : m2m_->capture()->formats()) {
> > > +		PixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);
> > > +		if (pixelFormat)
> > > +			pixelFormats.push_back(pixelFormat);
> > > +	}
> > > +
> > > +	return pixelFormats;
> > > +}
> > > +
> > > +int SimpleConverter::configure(PixelFormat inputFormat,
> > > +			       PixelFormat outputFormat, const Size &size)
> > > +{
> > > +	V4L2DeviceFormat format;
> > > +	int ret;
> > > +
> > > +	V4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);
> > > +	format.fourcc = videoFormat;
> > > +	format.size = size;
> > > +
> > > +	ret = m2m_->output()->setFormat(&format);
> > > +	if (ret < 0) {
> > > +		LOG(SimplePipeline, Error)
> > > +			<< "Failed to set input format: " << strerror(-ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (format.fourcc != videoFormat || format.size != size) {
> > > +		LOG(SimplePipeline, Error)
> > > +			<< "Input format not supported";
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	videoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);
> > > +	format.fourcc = videoFormat;
> > 
> > I wound set format.size here as well even if it's not strictly needed as 
> > it adds to readability. With or without this fixed tho,
> 
> Both gcc 9 and clang 10 fail to optimize that away though :-S It's not
> big deal in this specific case, but do we generally want to do so ?
> Maybe a comment would be better ?

I can live with a comment.

> 
> > Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > 
> > > +
> > > +	ret = m2m_->capture()->setFormat(&format);
> > > +	if (ret < 0) {
> > > +		LOG(SimplePipeline, Error)
> > > +			<< "Failed to set output format: " << strerror(-ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (format.fourcc != videoFormat || format.size != size) {
> > > +		LOG(SimplePipeline, Error)
> > > +			<< "Output format not supported";
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int SimpleConverter::exportBuffers(unsigned int count,
> > > +				   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> > > +{
> > > +	return m2m_->capture()->exportBuffers(count, buffers);
> > > +}
> > > +
> > > +int SimpleConverter::start(unsigned int count)
> > > +{
> > > +	int ret = m2m_->output()->importBuffers(count);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = m2m_->capture()->importBuffers(count);
> > > +	if (ret < 0) {
> > > +		stop();
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = m2m_->output()->streamOn();
> > > +	if (ret < 0) {
> > > +		stop();
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = m2m_->capture()->streamOn();
> > > +	if (ret < 0) {
> > > +		stop();
> > > +		return ret;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void SimpleConverter::stop()
> > > +{
> > > +	m2m_->capture()->streamOff();
> > > +	m2m_->output()->streamOff();
> > > +	m2m_->capture()->releaseBuffers();
> > > +	m2m_->output()->releaseBuffers();
> > > +}
> > > +
> > > +int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)
> > > +{
> > > +	int ret = m2m_->output()->queueBuffer(input);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = m2m_->capture()->queueBuffer(output);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +void SimpleConverter::captureBufferReady(FrameBuffer *buffer)
> > > +{
> > > +	if (!outputDoneQueue_.empty()) {
> > > +		FrameBuffer *other = outputDoneQueue_.front();
> > > +		outputDoneQueue_.pop();
> > > +		bufferReady.emit(other, buffer);
> > > +	} else {
> > > +		captureDoneQueue_.push(buffer);
> > > +	}
> > > +}
> > > +
> > > +void SimpleConverter::outputBufferReady(FrameBuffer *buffer)
> > > +{
> > > +	if (!captureDoneQueue_.empty()) {
> > > +		FrameBuffer *other = captureDoneQueue_.front();
> > > +		captureDoneQueue_.pop();
> > > +		bufferReady.emit(buffer, other);
> > > +	} else {
> > > +		outputDoneQueue_.push(buffer);
> > > +	}
> > > +}
> > > +
> > > +} /* namespace libcamera */
> > > diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h
> > > new file mode 100644
> > > index 000000000000..a33071fa8578
> > > --- /dev/null
> > > +++ b/src/libcamera/pipeline/simple/converter.h
> > > @@ -0,0 +1,60 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2020, Laurent Pinchart
> > > + *
> > > + * converter.h - Format converter for simple pipeline handler
> > > + */
> > > +
> > > +#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> > > +#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
> > > +
> > > +#include <memory>
> > > +#include <queue>
> > > +#include <vector>
> > > +
> > > +#include <libcamera/pixelformats.h>
> > > +#include <libcamera/signal.h>
> > > +
> > > +namespace libcamera {
> > > +
> > > +class FrameBuffer;
> > > +class MediaDevice;
> > > +struct Size;
> > > +class V4L2M2MDevice;
> > > +
> > > +class SimpleConverter
> > > +{
> > > +public:
> > > +	SimpleConverter(MediaDevice *media);
> > > +	~SimpleConverter();
> > > +
> > > +	int open();
> > > +	void close();
> > > +
> > > +	std::vector<PixelFormat> formats(PixelFormat input);
> > > +
> > > +	int configure(PixelFormat inputFormat, PixelFormat outputFormat,
> > > +		      const Size &size);
> > > +	int exportBuffers(unsigned int count,
> > > +			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
> > > +
> > > +	int start(unsigned int count);
> > > +	void stop();
> > > +
> > > +	int queueBuffers(FrameBuffer *input, FrameBuffer *output);
> > > +
> > > +	Signal<FrameBuffer *, FrameBuffer *> bufferReady;
> > > +
> > > +private:
> > > +	void captureBufferReady(FrameBuffer *buffer);
> > > +	void outputBufferReady(FrameBuffer *buffer);
> > > +
> > > +	V4L2M2MDevice *m2m_;
> > > +
> > > +	std::queue<FrameBuffer *> captureDoneQueue_;
> > > +	std::queue<FrameBuffer *> outputDoneQueue_;
> > > +};
> > > +
> > > +} /* namespace libcamera */
> > > +
> > > +#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */
> > > diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build
> > > index 4945a3e173cf..8372f24e3788 100644
> > > --- a/src/libcamera/pipeline/simple/meson.build
> > > +++ b/src/libcamera/pipeline/simple/meson.build
> > > @@ -1,3 +1,4 @@
> > >  libcamera_sources += files([
> > > +    'converter.cpp',
> > >      'simple.cpp',
> > >  ])
> 
> -- 
> Regards,
> 
> Laurent Pinchart

Patch

diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp
new file mode 100644
index 000000000000..50e554147c5f
--- /dev/null
+++ b/src/libcamera/pipeline/simple/converter.cpp
@@ -0,0 +1,213 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Laurent Pinchart
+ *
+ * converter.cpp - Format converter for simple pipeline handler
+ */
+
+#include "converter.h"
+
+#include <algorithm>
+
+#include <libcamera/buffer.h>
+#include <libcamera/geometry.h>
+#include <libcamera/signal.h>
+
+#include "log.h"
+#include "media_device.h"
+#include "v4l2_videodevice.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(SimplePipeline);
+
+SimpleConverter::SimpleConverter(MediaDevice *media)
+	: m2m_(nullptr)
+{
+	/*
+	 * Locate the video node. There's no need to validate the pipeline
+	 * further, the caller guarantees that this is a V4L2 mem2mem device.
+	 */
+	const std::vector<MediaEntity *> &entities = media->entities();
+	auto it = std::find_if(entities.begin(), entities.end(),
+			       [](MediaEntity *entity) {
+				       return entity->function() == MEDIA_ENT_F_IO_V4L;
+			       });
+	if (it == entities.end())
+		return;
+
+	m2m_ = new V4L2M2MDevice((*it)->deviceNode());
+
+	m2m_->output()->bufferReady.connect(this, &SimpleConverter::outputBufferReady);
+	m2m_->capture()->bufferReady.connect(this, &SimpleConverter::captureBufferReady);
+}
+
+SimpleConverter::~SimpleConverter()
+{
+	delete m2m_;
+}
+
+int SimpleConverter::open()
+{
+	if (!m2m_)
+		return -ENODEV;
+
+	return m2m_->open();
+}
+
+void SimpleConverter::close()
+{
+	if (m2m_)
+		m2m_->close();
+}
+
+std::vector<PixelFormat> SimpleConverter::formats(PixelFormat input)
+{
+	if (!m2m_)
+		return {};
+
+	/*
+	 * Set the format on the input side (V4L2 output) of the converter to
+	 * enumerate the conversion capabilities on its output (V4L2 capture).
+	 */
+	V4L2DeviceFormat format;
+	format.fourcc = m2m_->output()->toV4L2PixelFormat(input);
+	format.size = { 1, 1 };
+
+	int ret = m2m_->output()->setFormat(&format);
+	if (ret < 0) {
+		LOG(SimplePipeline, Error)
+			<< "Failed to set format: " << strerror(-ret);
+		return {};
+	}
+
+	std::vector<PixelFormat> pixelFormats;
+
+	for (const auto &format : m2m_->capture()->formats()) {
+		PixelFormat pixelFormat = m2m_->capture()->toPixelFormat(format.first);
+		if (pixelFormat)
+			pixelFormats.push_back(pixelFormat);
+	}
+
+	return pixelFormats;
+}
+
+int SimpleConverter::configure(PixelFormat inputFormat,
+			       PixelFormat outputFormat, const Size &size)
+{
+	V4L2DeviceFormat format;
+	int ret;
+
+	V4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);
+	format.fourcc = videoFormat;
+	format.size = size;
+
+	ret = m2m_->output()->setFormat(&format);
+	if (ret < 0) {
+		LOG(SimplePipeline, Error)
+			<< "Failed to set input format: " << strerror(-ret);
+		return ret;
+	}
+
+	if (format.fourcc != videoFormat || format.size != size) {
+		LOG(SimplePipeline, Error)
+			<< "Input format not supported";
+		return -EINVAL;
+	}
+
+	videoFormat = m2m_->capture()->toV4L2PixelFormat(outputFormat);
+	format.fourcc = videoFormat;
+
+	ret = m2m_->capture()->setFormat(&format);
+	if (ret < 0) {
+		LOG(SimplePipeline, Error)
+			<< "Failed to set output format: " << strerror(-ret);
+		return ret;
+	}
+
+	if (format.fourcc != videoFormat || format.size != size) {
+		LOG(SimplePipeline, Error)
+			<< "Output format not supported";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int SimpleConverter::exportBuffers(unsigned int count,
+				   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+	return m2m_->capture()->exportBuffers(count, buffers);
+}
+
+int SimpleConverter::start(unsigned int count)
+{
+	int ret = m2m_->output()->importBuffers(count);
+	if (ret < 0)
+		return ret;
+
+	ret = m2m_->capture()->importBuffers(count);
+	if (ret < 0) {
+		stop();
+		return ret;
+	}
+
+	ret = m2m_->output()->streamOn();
+	if (ret < 0) {
+		stop();
+		return ret;
+	}
+
+	ret = m2m_->capture()->streamOn();
+	if (ret < 0) {
+		stop();
+		return ret;
+	}
+
+	return 0;
+}
+
+void SimpleConverter::stop()
+{
+	m2m_->capture()->streamOff();
+	m2m_->output()->streamOff();
+	m2m_->capture()->releaseBuffers();
+	m2m_->output()->releaseBuffers();
+}
+
+int SimpleConverter::queueBuffers(FrameBuffer *input, FrameBuffer *output)
+{
+	int ret = m2m_->output()->queueBuffer(input);
+	if (ret < 0)
+		return ret;
+
+	ret = m2m_->capture()->queueBuffer(output);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+void SimpleConverter::captureBufferReady(FrameBuffer *buffer)
+{
+	if (!outputDoneQueue_.empty()) {
+		FrameBuffer *other = outputDoneQueue_.front();
+		outputDoneQueue_.pop();
+		bufferReady.emit(other, buffer);
+	} else {
+		captureDoneQueue_.push(buffer);
+	}
+}
+
+void SimpleConverter::outputBufferReady(FrameBuffer *buffer)
+{
+	if (!captureDoneQueue_.empty()) {
+		FrameBuffer *other = captureDoneQueue_.front();
+		captureDoneQueue_.pop();
+		bufferReady.emit(buffer, other);
+	} else {
+		outputDoneQueue_.push(buffer);
+	}
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h
new file mode 100644
index 000000000000..a33071fa8578
--- /dev/null
+++ b/src/libcamera/pipeline/simple/converter.h
@@ -0,0 +1,60 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Laurent Pinchart
+ *
+ * converter.h - Format converter for simple pipeline handler
+ */
+
+#ifndef __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
+#define __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__
+
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include <libcamera/pixelformats.h>
+#include <libcamera/signal.h>
+
+namespace libcamera {
+
+class FrameBuffer;
+class MediaDevice;
+struct Size;
+class V4L2M2MDevice;
+
+class SimpleConverter
+{
+public:
+	SimpleConverter(MediaDevice *media);
+	~SimpleConverter();
+
+	int open();
+	void close();
+
+	std::vector<PixelFormat> formats(PixelFormat input);
+
+	int configure(PixelFormat inputFormat, PixelFormat outputFormat,
+		      const Size &size);
+	int exportBuffers(unsigned int count,
+			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+
+	int start(unsigned int count);
+	void stop();
+
+	int queueBuffers(FrameBuffer *input, FrameBuffer *output);
+
+	Signal<FrameBuffer *, FrameBuffer *> bufferReady;
+
+private:
+	void captureBufferReady(FrameBuffer *buffer);
+	void outputBufferReady(FrameBuffer *buffer);
+
+	V4L2M2MDevice *m2m_;
+
+	std::queue<FrameBuffer *> captureDoneQueue_;
+	std::queue<FrameBuffer *> outputDoneQueue_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_PIPELINE_SIMPLE_CONVERTER_H__ */
diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build
index 4945a3e173cf..8372f24e3788 100644
--- a/src/libcamera/pipeline/simple/meson.build
+++ b/src/libcamera/pipeline/simple/meson.build
@@ -1,3 +1,4 @@ 
 libcamera_sources += files([
+    'converter.cpp',
     'simple.cpp',
 ])