diff options
-rw-r--r-- | src/libcamera/pipeline/simple/converter.cpp | 217 | ||||
-rw-r--r-- | src/libcamera/pipeline/simple/converter.h | 60 | ||||
-rw-r--r-- | src/libcamera/pipeline/simple/meson.build | 1 |
3 files changed, 278 insertions, 0 deletions
diff --git a/src/libcamera/pipeline/simple/converter.cpp b/src/libcamera/pipeline/simple/converter.cpp new file mode 100644 index 00000000..6d88776d --- /dev/null +++ b/src/libcamera/pipeline/simple/converter.cpp @@ -0,0 +1,217 @@ +/* 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 = format.first.toPixelFormat(); + 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; + } + + /* + * Set the pixel format on the output, the size is identical to the + * input as we don't support scaling. + */ + 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 00000000..a33071fa --- /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 4945a3e1..8372f24e 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', ]) |