libcamera: pipeline: simple: Add simple format converter

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>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
This commit is contained in:
Laurent Pinchart
2020-03-14 02:34:21 +02:00
parent a8964c28c8
commit bfd9185736
3 changed files with 278 additions and 0 deletions

View File

@@ -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 */

View File

@@ -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__ */

View File

@@ -1,3 +1,4 @@
libcamera_sources += files([
'converter.cpp',
'simple.cpp',
])