summaryrefslogtreecommitdiff
AgeCommit message (Expand)Author
2020-06-02test: Fixed the compilation issueMadhavan Krishnan
2020-06-02libcamera: Declare functions before variables in class definitionsLaurent Pinchart
2020-05-28qcam: viewfinder: Use correct DRM/QImage mappingsKieran Bingham
2020-05-28v4l2: Relicense V4L2 compatibility layer under LGPLLaurent Pinchart
2020-05-22libcamera: pipeline: simple: Add scaling supportLaurent Pinchart
2020-05-22libcamera: pipeline: simple: converter: Add scaling supportLaurent Pinchart
2020-05-22libcamera: pipeline: simple: Add stride supportLaurent Pinchart
2020-05-22libcamera: geometry: Set steps to 0 in default SizeRange constructorLaurent Pinchart
2020-05-21libcamera: raspberry: Fix segfault in ~RPiCameraData()Jacopo Mondi
2020-05-21libcamera: camera_sensor: Update properties parsingJacopo Mondi
2020-05-21include: linux: Update v4l2 ctrls for propertiesJacopo Mondi
2020-05-19libcamera: v4l2_videodevice: Use FileDescriptor "fd move" constructorLaurent Pinchart
2020-05-19test: file-descriptor: Add "fd move" constructor testLaurent Pinchart
2020-05-19libcamera: file_descriptor: Implement move semantics for constructorLaurent Pinchart
2020-05-18(q)cam: Fix header guardsLaurent Pinchart
2020-05-18libcamera: v4l2_videodevice: Fix dangling file descriptorNaushir Patuck
2020-05-18meson: Rename variables storing headers listsLaurent Pinchart
2020-05-16libcamera: Move IPA headers from include/ipa/ to include/libcamera/ipa/Laurent Pinchart
2020-05-16libcamera: Move internal headers to include/libcamera/internal/Laurent Pinchart
2020-05-15libcamera: pipeline: raspberrypi: Move StaggeredCtrl to libcamera namespaceLaurent Pinchart
2020-05-15libcamera: pipeline: raspberrypi: Don't inline all of StaggeredCtrlLaurent Pinchart
2020-05-13ipa: Only sign IPA modules that are being installedLaurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E302Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E305Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E741Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle W504Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E722Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E721Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E713Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E116 and E117Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E123 and E126Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E711 and E712Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E222Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E261 and E262Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E303Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E701Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E228Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E225Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E128Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E251Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E211Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E241Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E203Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E201 and E202Laurent Pinchart
2020-05-13utils: raspberrypi: ctt: Fix pycodestyle E231Laurent Pinchart
2020-05-13licenses: License all meson files under CC0-1.0Laurent Pinchart
2020-05-12libcamera: proxy: Relicense proxy worker under LGPLLaurent Pinchart
2020-05-12ipa: Drop v4l2_controls.h from ipa_interface.hLaurent Pinchart
2020-05-11libcamera: raspberrypi: Add components to meson buildNaushir Patuck
2020-05-11libcamera: utils: Raspberry Pi Camera Tuning ToolNaushir Patuck
1'>471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2020, Laurent Pinchart
 * Copyright 2022 NXP
 *
 * V4L2 M2M Format converter
 */

#include "libcamera/internal/converter/converter_v4l2_m2m.h"

#include <algorithm>
#include <limits.h>

#include <libcamera/base/log.h>
#include <libcamera/base/signal.h>
#include <libcamera/base/utils.h>

#include <libcamera/framebuffer.h>
#include <libcamera/geometry.h>
#include <libcamera/stream.h>

#include "libcamera/internal/media_device.h"
#include "libcamera/internal/v4l2_videodevice.h"

/**
 * \file converter/converter_v4l2_m2m.h
 * \brief V4L2 M2M based converter
 */

namespace libcamera {

LOG_DECLARE_CATEGORY(Converter)

namespace {

int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
		  Rectangle &maxCrop)
{
	Rectangle minC;
	Rectangle maxC;

	/* Find crop bounds */
	minC.width = 1;
	minC.height = 1;
	maxC.width = UINT_MAX;
	maxC.height = UINT_MAX;

	int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
	if (ret) {
		LOG(Converter, Error)
			<< "Could not query minimum selection crop: "
			<< strerror(-ret);
		return ret;
	}

	ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
	if (ret) {
		LOG(Converter, Error)
			<< "Could not query maximum selection crop: "
			<< strerror(-ret);
		return ret;
	}

	/* Reset the crop to its maximum */
	ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
	if (ret) {
		LOG(Converter, Error)
			<< "Could not reset selection crop: "
			<< strerror(-ret);
		return ret;
	}

	minCrop = minC;
	maxCrop = maxC;
	return 0;
}

} /* namespace */

/* -----------------------------------------------------------------------------
 * V4L2M2MConverter::V4L2M2MStream
 */

V4L2M2MConverter::V4L2M2MStream::V4L2M2MStream(V4L2M2MConverter *converter, const Stream *stream)
	: converter_(converter), stream_(stream)
{
	m2m_ = std::make_unique<V4L2M2MDevice>(converter->deviceNode());

	m2m_->output()->bufferReady.connect(this, &V4L2M2MStream::outputBufferReady);
	m2m_->capture()->bufferReady.connect(this, &V4L2M2MStream::captureBufferReady);

	int ret = m2m_->open();
	if (ret < 0)
		m2m_.reset();
}

int V4L2M2MConverter::V4L2M2MStream::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;

	if (converter_->features() & Feature::InputCrop) {
		ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
				    inputCropBounds_.second);
		if (ret)
			return ret;
	}

	return 0;
}

int V4L2M2MConverter::V4L2M2MStream::exportBuffers(unsigned int count,
						   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
	return m2m_->capture()->exportBuffers(count, buffers);
}

int V4L2M2MConverter::V4L2M2MStream::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::V4L2M2MStream::stop()
{
	m2m_->capture()->streamOff();
	m2m_->output()->streamOff();
	m2m_->capture()->releaseBuffers();
	m2m_->output()->releaseBuffers();
}

int V4L2M2MConverter::V4L2M2MStream::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;
}

int V4L2M2MConverter::V4L2M2MStream::getInputSelection(unsigned int target, Rectangle *rect)
{
	return m2m_->output()->getSelection(target, rect);
}

int V4L2M2MConverter::V4L2M2MStream::setInputSelection(unsigned int target, Rectangle *rect)
{
	return m2m_->output()->setSelection(target, rect);
}

std::pair<Rectangle, Rectangle> V4L2M2MConverter::V4L2M2MStream::inputCropBounds()
{
	return inputCropBounds_;
}

std::string V4L2M2MConverter::V4L2M2MStream::logPrefix() const
{
	return stream_->configuration().toString();
}

void V4L2M2MConverter::V4L2M2MStream::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::V4L2M2MStream::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<V4L2M2MDevice>(deviceNode());
	int ret = m2m_->open();
	if (ret < 0) {
		m2m_.reset();
		return;
	}

	ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
			    inputCropBounds_.second);
	if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
		features_ |= Feature::InputCrop;

		LOG(Converter, Info)
			<< "Converter supports cropping on its input";
	}
}

/**
 * \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<PixelFormat> 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<PixelFormat> 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<unsigned int, unsigned int>
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::adjustInputSize
 */
Size V4L2M2MConverter::adjustInputSize(const PixelFormat &pixFmt,
				       const Size &size, Alignment align)
{
	auto formats = m2m_->output()->formats();
	V4L2PixelFormat v4l2PixFmt = m2m_->output()->toV4L2PixelFormat(pixFmt);

	auto it = formats.find(v4l2PixFmt);
	if (it == formats.end()) {
		LOG(Converter, Info)
			<< "Unsupported pixel format " << pixFmt;
		return {};
	}

	return adjustSizes(size, it->second, align);
}

/**
 * \copydoc libcamera::Converter::adjustOutputSize
 */
Size V4L2M2MConverter::adjustOutputSize(const PixelFormat &pixFmt,
					const Size &size, Alignment align)
{
	auto formats = m2m_->capture()->formats();
	V4L2PixelFormat v4l2PixFmt = m2m_->capture()->toV4L2PixelFormat(pixFmt);

	auto it = formats.find(v4l2PixFmt);
	if (it == formats.end()) {
		LOG(Converter, Info)
			<< "Unsupported pixel format " << pixFmt;
		return {};
	}

	return adjustSizes(size, it->second, align);
}

Size V4L2M2MConverter::adjustSizes(const Size &cfgSize,
				   const std::vector<SizeRange> &ranges,
				   Alignment align)
{
	Size size = cfgSize;

	if (ranges.size() == 1) {
		/*
		 * The device supports either V4L2_FRMSIZE_TYPE_CONTINUOUS or
		 * V4L2_FRMSIZE_TYPE_STEPWISE.
		 */
		const SizeRange &range = *ranges.begin();

		size.width = std::clamp(size.width, range.min.width,
					range.max.width);
		size.height = std::clamp(size.height, range.min.height,
					 range.max.height);

		/*
		 * Check if any alignment is needed. If the sizes are already
		 * aligned, or the device supports V4L2_FRMSIZE_TYPE_CONTINUOUS
		 * with hStep and vStep equal to 1, we're done here.
		 */
		int widthR = size.width % range.hStep;
		int heightR = size.height % range.vStep;

		/* Align up or down according to the caller request. */

		if (widthR != 0)
			size.width = size.width - widthR
				   + ((align == Alignment::Up) ? range.hStep : 0);

		if (heightR != 0)
			size.height = size.height - heightR
				    + ((align == Alignment::Up) ? range.vStep : 0);
	} else {
		/*
		 * The device supports V4L2_FRMSIZE_TYPE_DISCRETE, find the
		 * size closer to the requested output configuration.
		 *
		 * The size ranges vector is not ordered, so we sort it first.
		 * If we align up, start from the larger element.
		 */
		std::vector<Size> sizes(ranges.size());
		std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),
			       [](const SizeRange &range) { return range.max; });
		std::sort(sizes.begin(), sizes.end());

		if (align == Alignment::Up)
			std::reverse(sizes.begin(), sizes.end());

		/*
		 * Return true if s2 is valid according to the desired
		 * alignment: smaller than s1 if we align down, larger than s1
		 * if we align up.
		 */
		auto nextSizeValid = [](const Size &s1, const Size &s2, Alignment a) {
			return a == Alignment::Down
				? (s1.width > s2.width && s1.height > s2.height)
				: (s1.width < s2.width && s1.height < s2.height);
		};

		Size newSize;
		for (const Size &sz : sizes) {
			if (!nextSizeValid(size, sz, align))
				break;

			newSize = sz;
		}

		if (newSize.isNull()) {
			LOG(Converter, Error)
				<< "Cannot adjust " << cfgSize
				<< " to a supported converter size";
			return {};
		}

		size = newSize;
	}

	return size;
}

/**
 * \copydoc libcamera::Converter::configure
 */
int V4L2M2MConverter::configure(const StreamConfiguration &inputCfg,
				const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
{
	int ret = 0;

	streams_.clear();

	for (unsigned int i = 0; i < outputCfgs.size(); ++i) {
		const StreamConfiguration &cfg = outputCfgs[i];
		std::unique_ptr<V4L2M2MStream> stream =
			std::make_unique<V4L2M2MStream>(this, cfg.stream());

		if (!stream->isValid()) {
			LOG(Converter, Error)
				<< "Failed to create stream " << i;
			ret = -EINVAL;
			break;
		}

		ret = stream->configure(inputCfg, cfg);
		if (ret < 0)
			break;

		streams_.emplace(cfg.stream(), std::move(stream));
	}

	if (ret < 0) {
		streams_.clear();
		return ret;
	}

	return 0;
}

/**
 * \copydoc libcamera::Converter::isConfigured
 */
bool V4L2M2MConverter::isConfigured(const Stream *stream) const
{
	return streams_.find(stream) != streams_.end();
}

/**
 * \copydoc libcamera::Converter::exportBuffers
 */
int V4L2M2MConverter::exportBuffers(const Stream *stream, unsigned int count,
				    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
{
	auto iter = streams_.find(stream);
	if (iter == streams_.end())
		return -EINVAL;

	return iter->second->exportBuffers(count, buffers);
}

/**
 * \copydoc libcamera::Converter::setInputCrop
 */
int V4L2M2MConverter::setInputCrop(const Stream *stream, Rectangle *rect)
{
	if (!(features_ & Feature::InputCrop))
		return -ENOTSUP;

	auto iter = streams_.find(stream);
	if (iter == streams_.end()) {
		LOG(Converter, Error) << "Invalid output stream";
		return -EINVAL;
	}

	return iter->second->setInputSelection(V4L2_SEL_TGT_CROP, rect);
}

/**
 * \fn libcamera::V4L2M2MConverter::inputCropBounds()
 * \copydoc libcamera::Converter::inputCropBounds()
 */

/**
 * \copydoc libcamera::Converter::inputCropBounds(const Stream *stream)
 */
std::pair<Rectangle, Rectangle>
V4L2M2MConverter::inputCropBounds(const Stream *stream)
{
	auto iter = streams_.find(stream);
	if (iter == streams_.end()) {
		LOG(Converter, Error) << "Invalid output stream";
		return {};
	}

	return iter->second->inputCropBounds();
}

/**
 * \copydoc libcamera::Converter::start
 */
int V4L2M2MConverter::start()
{
	int ret;

	for (auto &iter : streams_) {
		ret = iter.second->start();
		if (ret < 0) {
			stop();
			return ret;
		}
	}

	return 0;
}

/**
 * \copydoc libcamera::Converter::stop
 */
void V4L2M2MConverter::stop()
{
	for (auto &iter : streams_)
		iter.second->stop();
}

/**
 * \copydoc libcamera::Converter::validateOutput
 */
int V4L2M2MConverter::validateOutput(StreamConfiguration *cfg, bool *adjusted,
				     Alignment align)
{
	V4L2VideoDevice *capture = m2m_->capture();
	V4L2VideoDevice::Formats fmts = capture->formats();

	if (adjusted)
		*adjusted = false;

	PixelFormat fmt = cfg->pixelFormat;
	V4L2PixelFormat v4l2PixFmt = capture->toV4L2PixelFormat(fmt);

	auto it = fmts.find(v4l2PixFmt);
	if (it == fmts.end()) {
		it = fmts.begin();
		v4l2PixFmt = it->first;
		cfg->pixelFormat = v4l2PixFmt.toPixelFormat();

		if (adjusted)