From 5a8271ad70fb30460aa0aefb85fbaca484fceb01 Mon Sep 17 00:00:00 2001 From: Xavier Roumegue Date: Wed, 14 Dec 2022 16:52:18 +0100 Subject: libcamera: pipeline: simple: converter: Use generic converter interface Move the simple converter implementation to a generic V4L2 M2M class derived from the converter interface. This latter could be used by other pipeline implementations and as base class for customized V4L2 M2M converters. Signed-off-by: Xavier Roumegue Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Signed-off-by: Kieran Bingham --- .../internal/converter/converter_v4l2_m2m.h | 98 +++++ include/libcamera/internal/converter/meson.build | 5 + include/libcamera/internal/meson.build | 2 + src/libcamera/converter/converter_v4l2_m2m.cpp | 453 +++++++++++++++++++++ src/libcamera/converter/meson.build | 5 + src/libcamera/meson.build | 1 + src/libcamera/pipeline/simple/converter.cpp | 405 ------------------ src/libcamera/pipeline/simple/converter.h | 98 ----- src/libcamera/pipeline/simple/meson.build | 1 - src/libcamera/pipeline/simple/simple.cpp | 6 +- 10 files changed, 567 insertions(+), 507 deletions(-) create mode 100644 include/libcamera/internal/converter/converter_v4l2_m2m.h create mode 100644 include/libcamera/internal/converter/meson.build create mode 100644 src/libcamera/converter/converter_v4l2_m2m.cpp create mode 100644 src/libcamera/converter/meson.build delete mode 100644 src/libcamera/pipeline/simple/converter.cpp delete mode 100644 src/libcamera/pipeline/simple/converter.h diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h new file mode 100644 index 00000000..815916d0 --- /dev/null +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * Copyright 2022 NXP + * + * converter_v4l2_m2m.h - V4l2 M2M Format converter interface + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "libcamera/internal/converter.h" + +namespace libcamera { + +class FrameBuffer; +class MediaDevice; +class Size; +class SizeRange; +struct StreamConfiguration; +class V4L2M2MDevice; + +class V4L2M2MConverter : public Converter +{ +public: + V4L2M2MConverter(MediaDevice *media); + + int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; } + bool isValid() const { return m2m_ != nullptr; } + + std::vector formats(PixelFormat input); + SizeRange sizes(const Size &input); + + std::tuple + strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size); + + int configure(const StreamConfiguration &inputCfg, + const std::vector> &outputCfg); + int exportBuffers(unsigned int ouput, unsigned int count, + std::vector> *buffers); + + int start(); + void stop(); + + int queueBuffers(FrameBuffer *input, + const std::map &outputs); + +private: + class Stream : protected Loggable + { + public: + Stream(V4L2M2MConverter *converter, unsigned int index); + + bool isValid() const { return m2m_ != nullptr; } + + int configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg); + int exportBuffers(unsigned int count, + std::vector> *buffers); + + int start(); + void stop(); + + int queueBuffers(FrameBuffer *input, FrameBuffer *output); + + protected: + std::string logPrefix() const override; + + private: + void captureBufferReady(FrameBuffer *buffer); + void outputBufferReady(FrameBuffer *buffer); + + V4L2M2MConverter *converter_; + unsigned int index_; + std::unique_ptr m2m_; + + unsigned int inputBufferCount_; + unsigned int outputBufferCount_; + }; + + std::unique_ptr m2m_; + + std::vector streams_; + std::map queue_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/converter/meson.build b/include/libcamera/internal/converter/meson.build new file mode 100644 index 00000000..891e79e7 --- /dev/null +++ b/include/libcamera/internal/converter/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_internal_headers += files([ + 'converter_v4l2_m2m.h', +]) diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 341af8a2..d7508805 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -45,3 +45,5 @@ libcamera_internal_headers = files([ 'v4l2_videodevice.h', 'yaml_parser.h', ]) + +subdir('converter') diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp new file mode 100644 index 00000000..2a4d1d99 --- /dev/null +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Laurent Pinchart + * Copyright 2022 NXP + * + * converter_v4l2_m2m.cpp - V4L2 M2M Format converter + */ + +#include "libcamera/internal/converter/converter_v4l2_m2m.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/v4l2_videodevice.h" + +/** + * \file internal/converter/converter_v4l2_m2m.h + * \brief V4L2 M2M based converter + */ + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Converter) + +/* ----------------------------------------------------------------------------- + * V4L2M2MConverter::Stream + */ + +V4L2M2MConverter::Stream::Stream(V4L2M2MConverter *converter, unsigned int index) + : converter_(converter), index_(index) +{ + m2m_ = std::make_unique(converter->deviceNode()); + + m2m_->output()->bufferReady.connect(this, &Stream::outputBufferReady); + m2m_->capture()->bufferReady.connect(this, &Stream::captureBufferReady); + + int ret = m2m_->open(); + if (ret < 0) + m2m_.reset(); +} + +int V4L2M2MConverter::Stream::configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) +{ + V4L2PixelFormat videoFormat = + m2m_->output()->toV4L2PixelFormat(inputCfg.pixelFormat); + + V4L2DeviceFormat format; + format.fourcc = videoFormat; + format.size = inputCfg.size; + format.planesCount = 1; + format.planes[0].bpl = inputCfg.stride; + + int ret = m2m_->output()->setFormat(&format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set input format: " << strerror(-ret); + return ret; + } + + if (format.fourcc != videoFormat || format.size != inputCfg.size || + format.planes[0].bpl != inputCfg.stride) { + LOG(Converter, Error) + << "Input format not supported (requested " + << inputCfg.size << "-" << videoFormat + << ", got " << format << ")"; + return -EINVAL; + } + + /* Set the pixel format and size on the output. */ + videoFormat = m2m_->capture()->toV4L2PixelFormat(outputCfg.pixelFormat); + format = {}; + format.fourcc = videoFormat; + format.size = outputCfg.size; + + ret = m2m_->capture()->setFormat(&format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set output format: " << strerror(-ret); + return ret; + } + + if (format.fourcc != videoFormat || format.size != outputCfg.size) { + LOG(Converter, Error) + << "Output format not supported"; + return -EINVAL; + } + + inputBufferCount_ = inputCfg.bufferCount; + outputBufferCount_ = outputCfg.bufferCount; + + return 0; +} + +int V4L2M2MConverter::Stream::exportBuffers(unsigned int count, + std::vector> *buffers) +{ + return m2m_->capture()->exportBuffers(count, buffers); +} + +int V4L2M2MConverter::Stream::start() +{ + int ret = m2m_->output()->importBuffers(inputBufferCount_); + if (ret < 0) + return ret; + + ret = m2m_->capture()->importBuffers(outputBufferCount_); + 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 V4L2M2MConverter::Stream::stop() +{ + m2m_->capture()->streamOff(); + m2m_->output()->streamOff(); + m2m_->capture()->releaseBuffers(); + m2m_->output()->releaseBuffers(); +} + +int V4L2M2MConverter::Stream::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; +} + +std::string V4L2M2MConverter::Stream::logPrefix() const +{ + return "stream" + std::to_string(index_); +} + +void V4L2M2MConverter::Stream::outputBufferReady(FrameBuffer *buffer) +{ + auto it = converter_->queue_.find(buffer); + if (it == converter_->queue_.end()) + return; + + if (!--it->second) { + converter_->inputBufferReady.emit(buffer); + converter_->queue_.erase(it); + } +} + +void V4L2M2MConverter::Stream::captureBufferReady(FrameBuffer *buffer) +{ + converter_->outputBufferReady.emit(buffer); +} + +/* ----------------------------------------------------------------------------- + * V4L2M2MConverter + */ + +/** + * \class libcamera::V4L2M2MConverter + * \brief The V4L2 M2M converter implements the converter interface based on + * V4L2 M2M device. +*/ + +/** + * \fn V4L2M2MConverter::V4L2M2MConverter + * \brief Construct a V4L2M2MConverter instance + * \param[in] media The media device implementing the converter + */ + +V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media) + : Converter(media) +{ + if (deviceNode().empty()) + return; + + m2m_ = std::make_unique(deviceNode()); + int ret = m2m_->open(); + if (ret < 0) { + m2m_.reset(); + return; + } +} + +/** + * \fn libcamera::V4L2M2MConverter::loadConfiguration + * \details \copydetails libcamera::Converter::loadConfiguration + */ + +/** + * \fn libcamera::V4L2M2MConverter::isValid + * \details \copydetails libcamera::Converter::isValid + */ + +/** + * \fn libcamera::V4L2M2MConverter::formats + * \details \copydetails libcamera::Converter::formats + */ +std::vector V4L2M2MConverter::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 v4l2Format; + v4l2Format.fourcc = m2m_->output()->toV4L2PixelFormat(input); + v4l2Format.size = { 1, 1 }; + + int ret = m2m_->output()->setFormat(&v4l2Format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set format: " << strerror(-ret); + return {}; + } + + if (v4l2Format.fourcc != m2m_->output()->toV4L2PixelFormat(input)) { + LOG(Converter, Debug) + << "Input format " << input << " not supported."; + return {}; + } + + std::vector pixelFormats; + + for (const auto &format : m2m_->capture()->formats()) { + PixelFormat pixelFormat = format.first.toPixelFormat(); + if (pixelFormat) + pixelFormats.push_back(pixelFormat); + } + + return pixelFormats; +} + +/** + * \copydoc libcamera::Converter::sizes + */ +SizeRange V4L2M2MConverter::sizes(const Size &input) +{ + if (!m2m_) + return {}; + + /* + * Set the size on the input side (V4L2 output) of the converter to + * enumerate the scaling capabilities on its output (V4L2 capture). + */ + V4L2DeviceFormat format; + format.fourcc = V4L2PixelFormat(); + format.size = input; + + int ret = m2m_->output()->setFormat(&format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set format: " << strerror(-ret); + return {}; + } + + SizeRange sizes; + + format.size = { 1, 1 }; + ret = m2m_->capture()->setFormat(&format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set format: " << strerror(-ret); + return {}; + } + + sizes.min = format.size; + + format.size = { UINT_MAX, UINT_MAX }; + ret = m2m_->capture()->setFormat(&format); + if (ret < 0) { + LOG(Converter, Error) + << "Failed to set format: " << strerror(-ret); + return {}; + } + + sizes.max = format.size; + + return sizes; +} + +/** + * \copydoc libcamera::Converter::strideAndFrameSize + */ +std::tuple +V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat, + const Size &size) +{ + V4L2DeviceFormat format; + format.fourcc = m2m_->capture()->toV4L2PixelFormat(pixelFormat); + format.size = size; + + int ret = m2m_->capture()->tryFormat(&format); + if (ret < 0) + return std::make_tuple(0, 0); + + return std::make_tuple(format.planes[0].bpl, format.planes[0].size); +} + +/** + * \copydoc libcamera::Converter::configure + */ +int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg, + const std::vector> &outputCfgs) +{ + int ret = 0; + + streams_.clear(); + streams_.reserve(outputCfgs.size()); + + for (unsigned int i = 0; i < outputCfgs.size(); ++i) { + Stream &stream = streams_.emplace_back(this, i); + + if (!stream.isValid()) { + LOG(Converter, Error) + << "Failed to create stream " << i; + ret = -EINVAL; + break; + } + + ret = stream.configure(inputCfg, outputCfgs[i]); + if (ret < 0) + break; + } + + if (ret < 0) { + streams_.clear(); + return ret; + } + + return 0; +} + +/** + * \copydoc libcamera::Converter::exportBuffers + */ +int V4L2M2MConverter::exportBuffers(unsigned int output, unsigned int count, + std::vector> *buffers) +{ + if (output >= streams_.size()) + return -EINVAL; + + return streams_[output].exportBuffers(count, buffers); +} + +/** + * \copydoc libcamera::Converter::start + */ +int V4L2M2MConverter::start() +{ + int ret; + + for (Stream &stream : streams_) { + ret = stream.start(); + if (ret < 0) { + stop(); + return ret; + } + } + + return 0; +} + +/** + * \copydoc libcamera::Converter::stop + */ +void V4L2M2MConverter::stop() +{ + for (Stream &stream : utils::reverse(streams_)) + stream.stop(); +} + +/** + * \copydoc libcamera::Converter::queueBuffers + */ +int V4L2M2MConverter::queueBuffers(FrameBuffer *input, + const std::map &outputs) +{ + unsigned int mask = 0; + int ret; + + /* + * Validate the outputs as a sanity check: at least one output is + * required, all outputs must reference a valid stream and no two + * outputs can reference the same stream. + */ + if (outputs.empty()) + return -EINVAL; + + for (auto [index, buffer] : outputs) { + if (!buffer) + return -EINVAL; + if (index >= streams_.size()) + return -EINVAL; + if (mask & (1 << index)) + return -EINVAL; + + mask |= 1 << index; + } + + /* Queue the input and output buffers to all the streams. */ + for (auto [index, buffer] : outputs) { + ret = streams_[index].queueBuffers(input, buffer); + if (ret < 0) + return ret; + } + + /* + * Add the input buffer to the queue, with the number of streams as a + * reference count. Completion of the input buffer will be signalled by + * the stream that releases the last reference. + */ + queue_.emplace(std::piecewise_construct, + std::forward_as_tuple(input), + std::forward_as_tuple(outputs.size())); + + return 0; +} + +static std::initializer_list compatibles = { + "pxp", +}; + +REGISTER_CONVERTER("v4l2_m2m", V4L2M2MConverter, compatibles) + +} /* namespace libcamera */ diff --git a/src/libcamera/converter/meson.build b/src/libcamera/converter/meson.build new file mode 100644 index 00000000..2aa72fe4 --- /dev/null +++ b/src/libcamera/converter/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_sources += files([ + 'converter_v4l2_m2m.cpp' +]) diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index e9d0324e..ffc294f3 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -62,6 +62,7 @@ libatomic = cc.find_library('atomic', required : false) libthreads = dependency('threads') subdir('base') +subdir('converter') subdir('ipa') subdir('pipeline') subdir('proxy') diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp deleted file mode 100644 index acaaa64c..00000000 --- a/src/libcamera/pipeline/simple/converter.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* 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 -#include - -#include -#include -#include - -#include -#include -#include - -#include "libcamera/internal/media_device.h" -#include "libcamera/internal/v4l2_videodevice.h" - -namespace libcamera { - -LOG_DECLARE_CATEGORY(SimplePipeline) - -/* ----------------------------------------------------------------------------- - * SimpleConverter::Stream - */ - -SimpleConverter::Stream::Stream(SimpleConverter *converter, unsigned int index) - : converter_(converter), index_(index) -{ - m2m_ = std::make_unique(converter->deviceNode_); - - m2m_->output()->bufferReady.connect(this, &Stream::outputBufferReady); - m2m_->capture()->bufferReady.connect(this, &Stream::captureBufferReady); - - int ret = m2m_->open(); - if (ret < 0) - m2m_.reset(); -} - -int SimpleConverter::Stream::configure(const StreamConfiguration &inputCfg, - const StreamConfiguration &outputCfg) -{ - V4L2PixelFormat videoFormat = - m2m_->output()->toV4L2PixelFormat(inputCfg.pixelFormat); - - V4L2DeviceFormat format; - format.fourcc = videoFormat; - format.size = inputCfg.size; - format.planesCount = 1; - format.planes[0].bpl = inputCfg.stride; - - int 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 != inputCfg.size || - format.planes[0].bpl != inputCfg.stride) { - LOG(SimplePipeline, Error) - << "Input format not supported (requested " - << inputCfg.size << "-" << videoFormat - << ", got " << format << ")"; - return -EINVAL; - } - - /* Set the pixel format and size on the output. */ - videoFormat = m2m_->capture()->toV4L2PixelFormat(outputCfg.pixelFormat); - format = {}; - format.fourcc = videoFormat; - format.size = outputCfg.size; - - 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 != outputCfg.size) { - LOG(SimplePipeline, Error) - << "Output format not supported"; - return -EINVAL; - } - - inputBufferCount_ = inputCfg.bufferCount; - outputBufferCount_ = outputCfg.bufferCount; - - return 0; -} - -int SimpleConverter::Stream::exportBuffers(unsigned int count, - std::vector> *buffers) -{ - return m2m_->capture()->exportBuffers(count, buffers); -} - -int SimpleConverter::Stream::start() -{ - int ret = m2m_->output()->importBuffers(inputBufferCount_); - if (ret < 0) - return ret; - - ret = m2m_->capture()->importBuffers(outputBufferCount_); - 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::Stream::stop() -{ - m2m_->capture()->streamOff(); - m2m_->output()->streamOff(); - m2m_->capture()->releaseBuffers(); - m2m_->output()->releaseBuffers(); -} - -int SimpleConverter::Stream::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; -} - -std::string SimpleConverter::Stream::logPrefix() const -{ - return "stream" + std::to_string(index_); -} - -void SimpleConverter::Stream::outputBufferReady(FrameBuffer *buffer) -{ - auto it = converter_->queue_.find(buffer); - if (it == converter_->queue_.end()) - return; - - if (!--it->second) { - converter_->inputBufferReady.emit(buffer); - converter_->queue_.erase(it); - } -} - -void SimpleConverter::Stream::captureBufferReady(FrameBuffer *buffer) -{ - converter_->outputBufferReady.emit(buffer); -} - -/* ----------------------------------------------------------------------------- - * SimpleConverter - */ - -SimpleConverter::SimpleConverter(MediaDevice *media) -{ - /* - * 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 &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; - - deviceNode_ = (*it)->deviceNode(); - - m2m_ = std::make_unique(deviceNode_); - int ret = m2m_->open(); - if (ret < 0) { - m2m_.reset(); - return; - } -} - -std::vector 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 v4l2Format; - v4l2Format.fourcc = m2m_->output()->toV4L2PixelFormat(input); - v4l2Format.size = { 1, 1 }; - - int ret = m2m_->output()->setFormat(&v4l2Format); - if (ret < 0) { - LOG(SimplePipeline, Error) - << "Failed to set format: " << strerror(-ret); - return {}; - } - - if (v4l2Format.fourcc != m2m_->output()->toV4L2PixelFormat(input)) { - LOG(SimplePipeline, Debug) - << "Input format " << input << " not supported."; - return {}; - } - - std::vector pixelFormats; - - for (const auto &format : m2m_->capture()->formats()) { - PixelFormat pixelFormat = format.first.toPixelFormat(); - if (pixelFormat) - pixelFormats.push_back(pixelFormat); - } - - return pixelFormats; -} - -SizeRange SimpleConverter::sizes(const Size &input) -{ - if (!m2m_) - return {}; - - /* - * Set the size on the input side (V4L2 output) of the converter to - * enumerate the scaling capabilities on its output (V4L2 capture). - */ - V4L2DeviceFormat format; - format.fourcc = V4L2PixelFormat(); - format.size = input; - - int ret = m2m_->output()->setFormat(&format); - if (ret < 0) { - LOG(SimplePipeline, Error) - << "Failed to set format: " << strerror(-ret); - return {}; - } - - SizeRange sizes; - - format.size = { 1, 1 }; - ret = m2m_->capture()->setFormat(&format); - if (ret < 0) { - LOG(SimplePipeline, Error) - << "Failed to set format: " << strerror(-ret); - return {}; - } - - sizes.min = format.size; - - format.size = { UINT_MAX, UINT_MAX }; - ret = m2m_->capture()->setFormat(&format); - if (ret < 0) { - LOG(SimplePipeline, Error) - << "Failed to set format: " << strerror(-ret); - return {}; - } - - sizes.max = format.size; - - return sizes; -} - -std::tuple -SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat, - const Size &size) -{ - V4L2DeviceFormat format; - format.fourcc = m2m_->capture()->toV4L2PixelFormat(pixelFormat); - format.size = size; - - int ret = m2m_->capture()->tryFormat(&format); - if (ret < 0) - return std::make_tuple(0, 0); - - return std::make_tuple(format.planes[0].bpl, format.planes[0].size); -} - -int SimpleConverter::configure(const StreamConfiguration &inputCfg, - const std::vector> &outputCfgs) -{ - int ret = 0; - - streams_.clear(); - streams_.reserve(outputCfgs.size()); - - for (unsigned int i = 0; i < outputCfgs.size(); ++i) { - Stream &stream = streams_.emplace_back(this, i); - - if (!stream.isValid()) { - LOG(SimplePipeline, Error) - << "Failed to create stream " << i; - ret = -EINVAL; - break; - } - - ret = stream.configure(inputCfg, outputCfgs[i]); - if (ret < 0) - break; - } - - if (ret < 0) { - streams_.clear(); - return ret; - } - - return 0; -} - -int SimpleConverter::exportBuffers(unsigned int output, unsigned int count, - std::vector> *buffers) -{ - if (output >= streams_.size()) - return -EINVAL; - - return streams_[output].exportBuffers(count, buffers); -} - -int SimpleConverter::start() -{ - int ret; - - for (Stream &stream : streams_) { - ret = stream.start(); - if (ret < 0) { - stop(); - return ret; - } - } - - return 0; -} - -void SimpleConverter::stop() -{ - for (Stream &stream : utils::reverse(streams_)) - stream.stop(); -} - -int SimpleConverter::queueBuffers(FrameBuffer *input, - const std::map &outputs) -{ - unsigned int mask = 0; - int ret; - - /* - * Validate the outputs as a sanity check: at least one output is - * required, all outputs must reference a valid stream and no two - * outputs can reference the same stream. - */ - if (outputs.empty()) - return -EINVAL; - - for (auto [index, buffer] : outputs) { - if (!buffer) - return -EINVAL; - if (index >= streams_.size()) - return -EINVAL; - if (mask & (1 << index)) - return -EINVAL; - - mask |= 1 << index; - } - - /* Queue the input and output buffers to all the streams. */ - for (auto [index, buffer] : outputs) { - ret = streams_[index].queueBuffers(input, buffer); - if (ret < 0) - return ret; - } - - /* - * Add the input buffer to the queue, with the number of streams as a - * reference count. Completion of the input buffer will be signalled by - * the stream that releases the last reference. - */ - queue_.emplace(std::piecewise_construct, - std::forward_as_tuple(input), - std::forward_as_tuple(outputs.size())); - - return 0; -} - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/simple/converter.h b/src/libcamera/pipeline/simple/converter.h deleted file mode 100644 index f0ebe2e0..00000000 --- a/src/libcamera/pipeline/simple/converter.h +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Laurent Pinchart - * - * converter.h - Format converter for simple pipeline handler - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace libcamera { - -class FrameBuffer; -class MediaDevice; -class Size; -class SizeRange; -struct StreamConfiguration; -class V4L2M2MDevice; - -class SimpleConverter -{ -public: - SimpleConverter(MediaDevice *media); - - bool isValid() const { return m2m_ != nullptr; } - - std::vector formats(PixelFormat input); - SizeRange sizes(const Size &input); - - std::tuple - strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size); - - int configure(const StreamConfiguration &inputCfg, - const std::vector> &outputCfg); - int exportBuffers(unsigned int ouput, unsigned int count, - std::vector> *buffers); - - int start(); - void stop(); - - int queueBuffers(FrameBuffer *input, - const std::map &outputs); - - Signal inputBufferReady; - Signal outputBufferReady; - -private: - class Stream : protected Loggable - { - public: - Stream(SimpleConverter *converter, unsigned int index); - - bool isValid() const { return m2m_ != nullptr; } - - int configure(const StreamConfiguration &inputCfg, - const StreamConfiguration &outputCfg); - int exportBuffers(unsigned int count, - std::vector> *buffers); - - int start(); - void stop(); - - int queueBuffers(FrameBuffer *input, FrameBuffer *output); - - protected: - std::string logPrefix() const override; - - private: - void captureBufferReady(FrameBuffer *buffer); - void outputBufferReady(FrameBuffer *buffer); - - SimpleConverter *converter_; - unsigned int index_; - std::unique_ptr m2m_; - - unsigned int inputBufferCount_; - unsigned int outputBufferCount_; - }; - - std::string deviceNode_; - std::unique_ptr m2m_; - - std::vector streams_; - std::map queue_; -}; - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/simple/meson.build b/src/libcamera/pipeline/simple/meson.build index 9c99b32f..42b0896d 100644 --- a/src/libcamera/pipeline/simple/meson.build +++ b/src/libcamera/pipeline/simple/meson.build @@ -1,6 +1,5 @@ # SPDX-License-Identifier: CC0-1.0 libcamera_sources += files([ - 'converter.cpp', 'simple.cpp', ]) diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index a32de7f3..24ded4db 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -30,13 +30,13 @@ #include "libcamera/internal/camera.h" #include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/converter.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" -#include "converter.h" namespace libcamera { @@ -266,7 +266,7 @@ public: std::vector configs_; std::map> formats_; - std::unique_ptr converter_; + std::unique_ptr converter_; std::vector> converterBuffers_; bool useConverter_; std::queue> converterQueue_; @@ -492,7 +492,7 @@ int SimpleCameraData::init() /* Open the converter, if any. */ MediaDevice *converter = pipe->converter(); if (converter) { - converter_ = std::make_unique(converter); + converter_ = ConverterFactoryBase::create(converter); if (!converter_->isValid()) { LOG(SimplePipeline, Warning) << "Failed to create converter, disabling format conversion"; -- cgit v1.2.1