summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcamera/converter.cpp37
-rw-r--r--src/libcamera/converter/converter_v4l2_m2m.cpp110
2 files changed, 147 insertions, 0 deletions
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()