summaryrefslogtreecommitdiff
path: root/utils
AgeCommit message (Expand)Author
2020-01-18checkstyle: Add a pre-commit hook scriptNicolas Dufresne
2020-01-18checkstyle: Add support for checking style on amendmentsNicolas Dufresne
2020-01-18checkstyle: Add support for checking style on staged changesNicolas Dufresne
2020-01-18checkstyle: Introduce a Commit classNicolas Dufresne
2020-01-18checkstyle: Exit with 1 status if issues are foundNicolas Dufresne
2020-01-18checkstyle: Move from pep8 to pycodestyleNicolas Dufresne
2020-01-07utils: checkstyle.py: Support single line hunksKieran Bingham
2020-01-07utils: checkstyle.py: Fix regex string literalKieran Bingham
2019-10-23utils: checkstyle.py: Add include checkerLaurent Pinchart
2019-07-11libcamera: skip auto version generation when building for Chromium OSPaul Elder
2019-07-09libcamera: Rework automatic version generation to avoid rebuildsLaurent Pinchart
2019-07-04libcamera: Auto generate version informationKieran Bingham
2019-07-04utils: checkstyle.py: Add pep8 checkerKieran Bingham
2019-07-04utils: checkstyle.py: Add Doxygen formatterLaurent Pinchart
2019-07-01utils: checkstyle.py: Add meson.build checkerLaurent Pinchart
2019-07-01utils: checkstyle.py: Refactor formatters and checkers supportLaurent Pinchart
2019-03-29utils: rkisp1: Add test capture scriptLaurent Pinchart
2019-02-13utils: checkstyle: Catch LOG() usage without an explicit categoryLaurent Pinchart
2019-01-30utils: hooks: Provide post-commit hook example to checkstyle.pyKieran Bingham
2019-01-22utils: checkstyle: Add support for clang-formatLaurent Pinchart
2018-12-21utils: checkstyle: add keep-one-line-blocksKieran Bingham
2018-12-19utils: checkstyle.py: Strip trailing white spacesLaurent Pinchart
2018-12-19utils: checkstyle.py: Highlight trailing white space at end of lineLaurent Pinchart
2018-12-19utils: checkstyle.py: Support execution from non-root directoriesLaurent Pinchart
2018-12-14utils: Add Python-based commit style checker scriptLaurent Pinchart
2018-12-11utils: ipu3: process: Configure formats on ImgU subdev padsLaurent Pinchart
2018-12-11utils: ipu3: process: Fix typo in output files pathLaurent Pinchart
2018-12-11utils: ipu3: Abort when sensor or media device isn't foundLaurent Pinchart
2018-12-02utils: ipu3: Add test process scriptLaurent Pinchart
2018-11-29utils: ipu3: Add test capture scriptLaurent Pinchart
2018-11-20utils: ipu3: Add IPU3 raw capture unpack utilityLaurent Pinchart
ef='#n244'>244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
/* 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 <limits.h>

#include <libcamera/buffer.h>
#include <libcamera/geometry.h>
#include <libcamera/signal.h>
#include <libcamera/stream.h>

#include "libcamera/internal/log.h"
#include "libcamera/internal/media_device.h"
#include "libcamera/internal/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;
}

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;
}

int SimpleConverter::configure(PixelFormat inputFormat, const Size &inputSize,
			       StreamConfiguration *cfg)
{
	V4L2DeviceFormat format;
	int ret;

	V4L2PixelFormat videoFormat = m2m_->output()->toV4L2PixelFormat(inputFormat);
	format.fourcc = videoFormat;
	format.size = inputSize;

	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 != inputSize) {
		LOG(SimplePipeline, Error)
			<< "Input format not supported";
		return -EINVAL;
	}

	/* Set the pixel format and size on the output. */
	videoFormat = m2m_->capture()->toV4L2PixelFormat(cfg->pixelFormat);
	format.fourcc = videoFormat;
	format.size = cfg->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 != cfg->size) {
		LOG(SimplePipeline, Error)
			<< "Output format not supported";
		return -EINVAL;
	}

	cfg->stride = format.planes[0].bpl;

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