diff options
-rw-r--r-- | include/libcamera/internal/converter.h | 15 | ||||
-rw-r--r-- | include/libcamera/internal/converter/converter_v4l2_m2m.h | 11 | ||||
-rw-r--r-- | src/libcamera/converter.cpp | 40 | ||||
-rw-r--r-- | src/libcamera/converter/converter_v4l2_m2m.cpp | 169 |
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, |