Message ID | 20200404004438.17992-11-laurent.pinchart@ideasonboard.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
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
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', > > ])
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
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', ])