diff options
-rw-r--r-- | include/libcamera/internal/converter.h | 5 | ||||
-rw-r--r-- | include/libcamera/internal/converter/converter_v4l2_m2m.h | 11 | ||||
-rw-r--r-- | src/libcamera/converter.cpp | 37 | ||||
-rw-r--r-- | src/libcamera/converter/converter_v4l2_m2m.cpp | 110 |
4 files changed, 163 insertions, 0 deletions
diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h index 6623de4d..ffbb6f34 100644 --- a/include/libcamera/internal/converter.h +++ b/include/libcamera/internal/converter.h @@ -14,6 +14,7 @@ #include <memory> #include <string> #include <tuple> +#include <utility> #include <vector> #include <libcamera/base/class.h> @@ -35,6 +36,7 @@ class Converter public: enum class Feature { None = 0, + InputCrop = (1 << 0), }; using Features = Flags<Feature>; @@ -63,6 +65,9 @@ public: virtual int queueBuffers(FrameBuffer *input, const std::map<const Stream *, FrameBuffer *> &outputs) = 0; + virtual int setInputCrop(const Stream *stream, Rectangle *rect) = 0; + virtual std::pair<Rectangle, Rectangle> inputCropBounds(const Stream *stream) = 0; + Signal<FrameBuffer *> inputBufferReady; Signal<FrameBuffer *> outputBufferReady; diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h index b9e59899..0bc0d053 100644 --- a/include/libcamera/internal/converter/converter_v4l2_m2m.h +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h @@ -30,6 +30,7 @@ class Size; class SizeRange; class Stream; struct StreamConfiguration; +class Rectangle; class V4L2M2MDevice; class V4L2M2MConverter : public Converter @@ -57,6 +58,9 @@ public: int queueBuffers(FrameBuffer *input, const std::map<const Stream *, FrameBuffer *> &outputs); + int setInputCrop(const Stream *stream, Rectangle *rect); + std::pair<Rectangle, Rectangle> inputCropBounds(const Stream *stream); + private: class V4L2M2MStream : protected Loggable { @@ -75,6 +79,11 @@ private: int queueBuffers(FrameBuffer *input, FrameBuffer *output); + int setInputSelection(unsigned int target, Rectangle *rect); + int getInputSelection(unsigned int target, Rectangle *rect); + + std::pair<Rectangle, Rectangle> inputCropBounds(); + protected: std::string logPrefix() const override; @@ -88,6 +97,8 @@ private: unsigned int inputBufferCount_; unsigned int outputBufferCount_; + + std::pair<Rectangle, Rectangle> inputCropBounds_; }; std::unique_ptr<V4L2M2MDevice> m2m_; diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp index d7bb7273..945f2527 100644 --- a/src/libcamera/converter.cpp +++ b/src/libcamera/converter.cpp @@ -11,6 +11,8 @@ #include <libcamera/base/log.h> +#include <libcamera/stream.h> + #include "libcamera/internal/media_device.h" /** @@ -39,6 +41,8 @@ LOG_DEFINE_CATEGORY(Converter) * \brief Specify the features supported by the converter * \var Converter::Feature::None * \brief No extra features supported by the converter + * \var Converter::Feature::InputCrop + * \brief Cropping capability at input is supported by the converter */ /** @@ -162,6 +166,39 @@ Converter::~Converter() */ /** + * \fn Converter::setInputCrop() + * \brief Set the crop rectangle \a rect for \a stream + * \param[in] stream The output stream + * \param[inout] rect The crop rectangle to apply and return the rectangle + * that is actually applied + * + * Set the crop rectangle \a rect for \a stream provided the converter supports + * cropping. The converter has the Feature::InputCrop flag in this case. + * + * The underlying hardware can adjust the rectangle supplied by the user + * due to hardware constraints. The caller can inspect \a rect to determine the + * actual rectangle that has been applied by the converter, after this function + * returns. + * + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn Converter::inputCropBounds() + * \brief Retrieve the crop bounds for \a stream + * \param[in] stream The output stream + * + * Retrieve the minimum and maximum crop bounds for \a stream. The converter + * should support cropping (Feature::InputCrop). + * + * The crop bounds depend on the configuration of the output stream and hence + * this function should be called after the \a stream has been configured using + * configure(). + * + * \return A pair containing the minimum and maximum crop bound in that order + */ + +/** * \var Converter::inputBufferReady * \brief A signal emitted when the input frame buffer completes */ diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp index 4f3e8ce4..d63ef2f8 100644 --- a/src/libcamera/converter/converter_v4l2_m2m.cpp +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp @@ -97,6 +97,44 @@ int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC inputBufferCount_ = inputCfg.bufferCount; outputBufferCount_ = outputCfg.bufferCount; + if (converter_->features() & Feature::InputCrop) { + Rectangle minCrop; + Rectangle maxCrop; + + /* Find crop bounds */ + minCrop.width = 1; + minCrop.height = 1; + maxCrop.width = UINT_MAX; + maxCrop.height = UINT_MAX; + + ret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop); + if (ret) { + LOG(Converter, Error) + << "Could not query minimum selection crop: " + << strerror(-ret); + return ret; + } + + ret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop); + if (ret) { + LOG(Converter, Error) + << "Could not query maximum selection crop: " + << strerror(-ret); + return ret; + } + + /* Reset the crop to its maximum */ + ret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop); + if (ret) { + LOG(Converter, Error) + << "Could not reset selection crop: " + << strerror(-ret); + return ret; + } + + inputCropBounds_ = { minCrop, maxCrop }; + } + return 0; } @@ -154,6 +192,21 @@ int V4L2M2MConverter::V4L2M2MStream::queueBuffers(FrameBuffer *input, FrameBuffe 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(); @@ -204,6 +257,33 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media) m2m_.reset(); return; } + + /* Discover Feature::InputCrop */ + Rectangle maxCrop; + maxCrop.width = UINT_MAX; + maxCrop.height = UINT_MAX; + + ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop); + if (ret) + return; + + /* + * Rectangles for cropping targets are defined even if the device + * does not support cropping. Their sizes and positions will be + * fixed in such cases. + * + * Set and inspect a crop equivalent to half of the maximum crop + * returned earlier. Use this to determine whether the crop on + * input is really supported. + */ + Rectangle halfCrop(maxCrop.size() / 2); + ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop); + if (!ret && halfCrop != maxCrop) { + features_ |= Feature::InputCrop; + + LOG(Converter, Info) + << "Converter supports cropping on its input"; + } } /** @@ -374,6 +454,36 @@ int V4L2M2MConverter::exportBuffers(const Stream *stream, unsigned int count, } /** + * \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); +} + +/** + * \copydoc libcamera::Converter::inputCropBounds + */ +std::pair<Rectangle, Rectangle> +V4L2M2MConverter::inputCropBounds(const Stream *stream) +{ + auto iter = streams_.find(stream); + if (iter == streams_.end()) + return {}; + + return iter->second->inputCropBounds(); +} + +/** * \copydoc libcamera::Converter::start */ int V4L2M2MConverter::start() |