summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libcamera/internal/converter.h15
-rw-r--r--include/libcamera/internal/converter/converter_v4l2_m2m.h11
-rw-r--r--src/libcamera/converter.cpp40
-rw-r--r--src/libcamera/converter/converter_v4l2_m2m.cpp169
4 files changed, 235 insertions, 0 deletions
diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h
index afea4624..644ec429 100644
--- a/include/libcamera/internal/converter.h
+++ b/include/libcamera/internal/converter.h
@@ -41,6 +41,11 @@ public:
using Features = Flags<Feature>;
+ enum class Alignment {
+ Down = 0,
+ Up,
+ };
+
Converter(MediaDevice *media, Features features = Feature::None);
virtual ~Converter();
@@ -51,9 +56,19 @@ public:
virtual std::vector<PixelFormat> formats(PixelFormat input) = 0;
virtual SizeRange sizes(const Size &input) = 0;
+ virtual Size adjustInputSize(const PixelFormat &pixFmt,
+ const Size &size,
+ Alignment align = Alignment::Down) = 0;
+ virtual Size adjustOutputSize(const PixelFormat &pixFmt,
+ const Size &size,
+ Alignment align = Alignment::Down) = 0;
+
virtual std::tuple<unsigned int, unsigned int>
strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;
+ virtual int validateOutput(StreamConfiguration *cfg, bool *adjusted,
+ Alignment align = Alignment::Down) = 0;
+
virtual int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;
virtual bool isConfigured(const Stream *stream) const = 0;
diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
index 1ccbfc7c..0ad7bf7f 100644
--- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
+++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
@@ -47,6 +47,11 @@ public:
std::tuple<unsigned int, unsigned int>
strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) override;
+ Size adjustInputSize(const PixelFormat &pixFmt,
+ const Size &size, Alignment align = Alignment::Down) override;
+ Size adjustOutputSize(const PixelFormat &pixFmt,
+ const Size &size, Alignment align = Alignment::Down) override;
+
int configure(const StreamConfiguration &inputCfg,
const std::vector<std::reference_wrapper<StreamConfiguration>>
&outputCfg) override;
@@ -57,6 +62,9 @@ public:
int start() override;
void stop() override;
+ int validateOutput(StreamConfiguration *cfg, bool *adjusted,
+ Alignment align = Alignment::Down) override;
+
int queueBuffers(FrameBuffer *input,
const std::map<const Stream *, FrameBuffer *> &outputs) override;
@@ -104,6 +112,9 @@ private:
std::pair<Rectangle, Rectangle> inputCropBounds_;
};
+ Size adjustSizes(const Size &size, const std::vector<SizeRange> &ranges,
+ Alignment align);
+
std::unique_ptr<V4L2M2MDevice> m2m_;
std::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;
diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
index 09ebf4f5..d551b908 100644
--- a/src/libcamera/converter.cpp
+++ b/src/libcamera/converter.cpp
@@ -51,6 +51,16 @@ LOG_DEFINE_CATEGORY(Converter)
*/
/**
+ * \enum Converter::Alignment
+ * \brief The alignment mode specified when adjusting the converter input or
+ * output sizes
+ * \var Converter::Alignment::Down
+ * \brief Adjust the Converter sizes to a smaller valid size
+ * \var Converter::Alignment::Up
+ * \brief Adjust the Converter sizes to a larger valid size
+ */
+
+/**
* \brief Construct a Converter instance
* \param[in] media The media device implementing the converter
* \param[in] features Features flags representing supported features
@@ -111,6 +121,26 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::adjustInputSize()
+ * \brief Adjust the converter input \a size to a valid value
+ * \param[in] pixFmt The pixel format of the converter input stream
+ * \param[in] size The converter input size to adjust to a valid value
+ * \param[in] align The desired alignment
+ * \return The adjusted converter input size or a null Size if \a size cannot
+ * be adjusted
+ */
+
+/**
+ * \fn Converter::adjustOutputSize()
+ * \brief Adjust the converter output \a size to a valid value
+ * \param[in] pixFmt The pixel format of the converter output stream
+ * \param[in] size The converter output size to adjust to a valid value
+ * \param[in] align The desired alignment
+ * \return The adjusted converter output size or a null Size if \a size cannot
+ * be adjusted
+ */
+
+/**
* \fn Converter::strideAndFrameSize()
* \brief Retrieve the output stride and frame size for an input configutation
* \param[in] pixelFormat Input stream pixel format
@@ -119,6 +149,16 @@ Converter::~Converter()
*/
/**
+ * \fn Converter::validateOutput()
+ * \brief Validate and possibily adjust \a cfg to a valid converter output
+ * \param[inout] cfg The StreamConfiguration to validate and adjust
+ * \param[out] adjusted Set to true if \a cfg has been adjusted
+ * \param[in] align The desired alignment
+ * \return 0 if \a cfg is valid or has been adjusted, a negative error code
+ * otherwise if \a cfg cannot be adjusted
+ */
+
+/**
* \fn Converter::configure()
* \brief Configure a set of output stream conversion from an input stream
* \param[in] inputCfg Input stream configuration
diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
index 83daca15..566f18ce 100644
--- a/src/libcamera/converter/converter_v4l2_m2m.cpp
+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
@@ -8,6 +8,7 @@
#include "libcamera/internal/converter/converter_v4l2_m2m.h"
+#include <algorithm>
#include <limits.h>
#include <libcamera/base/log.h>
@@ -401,6 +402,127 @@ V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat,
}
/**
+ * \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,
@@ -523,6 +645,53 @@ void V4L2M2MConverter::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)
+ *adjusted = true;
+
+ LOG(Converter, Info)
+ << "Converter output pixel format adjusted to "
+ << cfg->pixelFormat;
+ }
+
+ const Size cfgSize = cfg->size;
+ cfg->size = adjustSizes(cfgSize, it->second, align);
+
+ if (cfg->size.isNull())
+ return -EINVAL;
+
+ if (cfg->size.width != cfgSize.width ||
+ cfg->size.height != cfgSize.height) {
+ LOG(Converter, Info)
+ << "Converter size adjusted to "
+ << cfg->size;
+ if (adjusted)
+ *adjusted = true;
+ }
+
+ return 0;
+}
+
+/**
* \copydoc libcamera::Converter::queueBuffers
*/
int V4L2M2MConverter::queueBuffers(FrameBuffer *input,