Show a patch.

GET /api/patches/21378/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 21378,
    "url": "https://patchwork.libcamera.org/api/patches/21378/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/21378/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20240926093623.94136-3-umang.jain@ideasonboard.com>",
    "date": "2024-09-26T09:36:21",
    "name": "[v8,2/4] libcamera: converter: Add interface to support cropping capability",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "d8757042c9cc2a79b6a7e5e6d054108b5007333d",
    "submitter": {
        "id": 86,
        "url": "https://patchwork.libcamera.org/api/people/86/?format=api",
        "name": "Umang Jain",
        "email": "umang.jain@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/21378/mbox/",
    "series": [
        {
            "id": 4629,
            "url": "https://patchwork.libcamera.org/api/series/4629/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4629",
            "date": "2024-09-26T09:36:19",
            "name": "rkisp1: Plumb the DW100 dewarper as converter",
            "version": 8,
            "mbox": "https://patchwork.libcamera.org/series/4629/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/21378/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/21378/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id EEA8DC3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Sep 2024 09:36:40 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6D1AA63515;\n\tThu, 26 Sep 2024 11:36:39 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AF0776350B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Sep 2024 11:36:36 +0200 (CEST)",
            "from localhost.localdomain (unknown\n\t[IPv6:2405:201:2015:f873:55f8:639e:8e9f:12ec])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id ADD768D4;\n\tThu, 26 Sep 2024 11:35:07 +0200 (CEST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"bM0ue/Hh\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1727343308;\n\tbh=MmlFVK9zBIfESiISRS9XJwiukPcBDcJZyEqk1+DIrB4=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=bM0ue/HhtAnxKaxYGAEy82yOMUGlRDUHZbFzpaePC2U5lN/Tof/46qxaruk3P9v1W\n\t/QrcKR8BHhRzAiVnLYsErKwJLZH/A6v31SkMD0Udv6xuB0V1vPWqax+uw9MPr27oh1\n\tIMCgRvxvhu8g5vZrK2qCAHIwj50xcF/6ydGLDG9c=",
        "From": "Umang Jain <umang.jain@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>,\n\tUmang Jain <umang.jain@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>",
        "Subject": "[PATCH v8 2/4] libcamera: converter: Add interface to support\n\tcropping capability",
        "Date": "Thu, 26 Sep 2024 15:06:21 +0530",
        "Message-ID": "<20240926093623.94136-3-umang.jain@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.45.0",
        "In-Reply-To": "<20240926093623.94136-1-umang.jain@ideasonboard.com>",
        "References": "<20240926093623.94136-1-umang.jain@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "If the converter has cropping capability on its input, the interface\nshould support it by providing appropriate virtual functions. Provide\nFeature::InputCrop in Feature enumeration for the same.\n\nProvide virtual setInputCrop() and inputCropBounds() interfaces so that\nthe converter can implement its own cropping functionality.\n\nThe V4L2M2MConverter implements these interfaces of the Converter\ninterface. Not all V4L2M2M converters will have cropping ability\non its input, hence it needs to be discovered at construction time.\nIf the capability to crop is identified successfully, the cropping\nbounds are determined during configure() time.\n\nSigned-off-by: Umang Jain <umang.jain@ideasonboard.com>\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\n---\n include/libcamera/internal/converter.h        |   5 +\n .../internal/converter/converter_v4l2_m2m.h   |  11 ++\n src/libcamera/converter.cpp                   |  37 ++++++\n .../converter/converter_v4l2_m2m.cpp          | 110 ++++++++++++++++++\n 4 files changed, 163 insertions(+)",
    "diff": "diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h\nindex 6623de4d..ffbb6f34 100644\n--- a/include/libcamera/internal/converter.h\n+++ b/include/libcamera/internal/converter.h\n@@ -14,6 +14,7 @@\n #include <memory>\n #include <string>\n #include <tuple>\n+#include <utility>\n #include <vector>\n \n #include <libcamera/base/class.h>\n@@ -35,6 +36,7 @@ class Converter\n public:\n \tenum class Feature {\n \t\tNone = 0,\n+\t\tInputCrop = (1 << 0),\n \t};\n \n \tusing Features = Flags<Feature>;\n@@ -63,6 +65,9 @@ public:\n \tvirtual int queueBuffers(FrameBuffer *input,\n \t\t\t\t const std::map<const Stream *, FrameBuffer *> &outputs) = 0;\n \n+\tvirtual int setInputCrop(const Stream *stream, Rectangle *rect) = 0;\n+\tvirtual std::pair<Rectangle, Rectangle> inputCropBounds(const Stream *stream) = 0;\n+\n \tSignal<FrameBuffer *> inputBufferReady;\n \tSignal<FrameBuffer *> outputBufferReady;\n \ndiff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h\nindex b9e59899..0bc0d053 100644\n--- a/include/libcamera/internal/converter/converter_v4l2_m2m.h\n+++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n@@ -30,6 +30,7 @@ class Size;\n class SizeRange;\n class Stream;\n struct StreamConfiguration;\n+class Rectangle;\n class V4L2M2MDevice;\n \n class V4L2M2MConverter : public Converter\n@@ -57,6 +58,9 @@ public:\n \tint queueBuffers(FrameBuffer *input,\n \t\t\t const std::map<const Stream *, FrameBuffer *> &outputs);\n \n+\tint setInputCrop(const Stream *stream, Rectangle *rect);\n+\tstd::pair<Rectangle, Rectangle> inputCropBounds(const Stream *stream);\n+\n private:\n \tclass V4L2M2MStream : protected Loggable\n \t{\n@@ -75,6 +79,11 @@ private:\n \n \t\tint queueBuffers(FrameBuffer *input, FrameBuffer *output);\n \n+\t\tint setInputSelection(unsigned int target, Rectangle *rect);\n+\t\tint getInputSelection(unsigned int target, Rectangle *rect);\n+\n+\t\tstd::pair<Rectangle, Rectangle> inputCropBounds();\n+\n \tprotected:\n \t\tstd::string logPrefix() const override;\n \n@@ -88,6 +97,8 @@ private:\n \n \t\tunsigned int inputBufferCount_;\n \t\tunsigned int outputBufferCount_;\n+\n+\t\tstd::pair<Rectangle, Rectangle> inputCropBounds_;\n \t};\n \n \tstd::unique_ptr<V4L2M2MDevice> m2m_;\ndiff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp\nindex d7bb7273..945f2527 100644\n--- a/src/libcamera/converter.cpp\n+++ b/src/libcamera/converter.cpp\n@@ -11,6 +11,8 @@\n \n #include <libcamera/base/log.h>\n \n+#include <libcamera/stream.h>\n+\n #include \"libcamera/internal/media_device.h\"\n \n /**\n@@ -39,6 +41,8 @@ LOG_DEFINE_CATEGORY(Converter)\n  * \\brief Specify the features supported by the converter\n  * \\var Converter::Feature::None\n  * \\brief No extra features supported by the converter\n+ * \\var Converter::Feature::InputCrop\n+ * \\brief Cropping capability at input is supported by the converter\n  */\n \n /**\n@@ -161,6 +165,39 @@ Converter::~Converter()\n  * \\return 0 on success or a negative error code otherwise\n  */\n \n+/**\n+ * \\fn Converter::setInputCrop()\n+ * \\brief Set the crop rectangle \\a rect for \\a stream\n+ * \\param[in] stream The output stream\n+ * \\param[inout] rect The crop rectangle to apply and return the rectangle\n+ * that is actually applied\n+ *\n+ * Set the crop rectangle \\a rect for \\a stream provided the converter supports\n+ * cropping. The converter has the Feature::InputCrop flag in this case.\n+ *\n+ * The underlying hardware can adjust the rectangle supplied by the user\n+ * due to hardware constraints. The caller can inspect \\a rect to determine the\n+ * actual rectangle that has been applied by the converter, after this function\n+ * returns.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ */\n+\n+/**\n+ * \\fn Converter::inputCropBounds()\n+ * \\brief Retrieve the crop bounds for \\a stream\n+ * \\param[in] stream The output stream\n+ *\n+ * Retrieve the minimum and maximum crop bounds for \\a stream. The converter\n+ * should support cropping (Feature::InputCrop).\n+ *\n+ * The crop bounds depend on the configuration of the output stream and hence\n+ * this function should be called after the \\a stream has been configured using\n+ * configure().\n+ *\n+ * \\return A pair containing the minimum and maximum crop bound in that order\n+ */\n+\n /**\n  * \\var Converter::inputBufferReady\n  * \\brief A signal emitted when the input frame buffer completes\ndiff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp\nindex 4f3e8ce4..d63ef2f8 100644\n--- a/src/libcamera/converter/converter_v4l2_m2m.cpp\n+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp\n@@ -97,6 +97,44 @@ int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC\n \tinputBufferCount_ = inputCfg.bufferCount;\n \toutputBufferCount_ = outputCfg.bufferCount;\n \n+\tif (converter_->features() & Feature::InputCrop) {\n+\t\tRectangle minCrop;\n+\t\tRectangle maxCrop;\n+\n+\t\t/* Find crop bounds */\n+\t\tminCrop.width = 1;\n+\t\tminCrop.height = 1;\n+\t\tmaxCrop.width = UINT_MAX;\n+\t\tmaxCrop.height = UINT_MAX;\n+\n+\t\tret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop);\n+\t\tif (ret) {\n+\t\t\tLOG(Converter, Error)\n+\t\t\t\t<< \"Could not query minimum selection crop: \"\n+\t\t\t\t<< strerror(-ret);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop);\n+\t\tif (ret) {\n+\t\t\tLOG(Converter, Error)\n+\t\t\t\t<< \"Could not query maximum selection crop: \"\n+\t\t\t\t<< strerror(-ret);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\t/* Reset the crop to its maximum */\n+\t\tret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop);\n+\t\tif (ret) {\n+\t\t\tLOG(Converter, Error)\n+\t\t\t\t<< \"Could not reset selection crop: \"\n+\t\t\t\t<< strerror(-ret);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tinputCropBounds_ = { minCrop, maxCrop };\n+\t}\n+\n \treturn 0;\n }\n \n@@ -154,6 +192,21 @@ int V4L2M2MConverter::V4L2M2MStream::queueBuffers(FrameBuffer *input, FrameBuffe\n \treturn 0;\n }\n \n+int V4L2M2MConverter::V4L2M2MStream::getInputSelection(unsigned int target, Rectangle *rect)\n+{\n+\treturn m2m_->output()->getSelection(target, rect);\n+}\n+\n+int V4L2M2MConverter::V4L2M2MStream::setInputSelection(unsigned int target, Rectangle *rect)\n+{\n+\treturn m2m_->output()->setSelection(target, rect);\n+}\n+\n+std::pair<Rectangle, Rectangle> V4L2M2MConverter::V4L2M2MStream::inputCropBounds()\n+{\n+\treturn inputCropBounds_;\n+}\n+\n std::string V4L2M2MConverter::V4L2M2MStream::logPrefix() const\n {\n \treturn stream_->configuration().toString();\n@@ -204,6 +257,33 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)\n \t\tm2m_.reset();\n \t\treturn;\n \t}\n+\n+\t/* Discover Feature::InputCrop */\n+\tRectangle maxCrop;\n+\tmaxCrop.width = UINT_MAX;\n+\tmaxCrop.height = UINT_MAX;\n+\n+\tret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop);\n+\tif (ret)\n+\t\treturn;\n+\n+\t/*\n+\t * Rectangles for cropping targets are defined even if the device\n+\t * does not support cropping. Their sizes and positions will be\n+\t * fixed in such cases.\n+\t *\n+\t * Set and inspect a crop equivalent to half of the maximum crop\n+\t * returned earlier. Use this to determine whether the crop on\n+\t * input is really supported.\n+\t */\n+\tRectangle halfCrop(maxCrop.size() / 2);\n+\tret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop);\n+\tif (!ret && halfCrop != maxCrop) {\n+\t\tfeatures_ |= Feature::InputCrop;\n+\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Converter supports cropping on its input\";\n+\t}\n }\n \n /**\n@@ -373,6 +453,36 @@ int V4L2M2MConverter::exportBuffers(const Stream *stream, unsigned int count,\n \treturn iter->second->exportBuffers(count, buffers);\n }\n \n+/**\n+ * \\copydoc libcamera::Converter::setInputCrop\n+ */\n+int V4L2M2MConverter::setInputCrop(const Stream *stream, Rectangle *rect)\n+{\n+\tif (!(features_ & Feature::InputCrop))\n+\t\treturn -ENOTSUP;\n+\n+\tauto iter = streams_.find(stream);\n+\tif (iter == streams_.end()) {\n+\t\tLOG(Converter, Error) << \"Invalid output stream\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn iter->second->setInputSelection(V4L2_SEL_TGT_CROP, rect);\n+}\n+\n+/**\n+ * \\copydoc libcamera::Converter::inputCropBounds\n+ */\n+std::pair<Rectangle, Rectangle>\n+V4L2M2MConverter::inputCropBounds(const Stream *stream)\n+{\n+\tauto iter = streams_.find(stream);\n+\tif (iter == streams_.end())\n+\t\treturn {};\n+\n+\treturn iter->second->inputCropBounds();\n+}\n+\n /**\n  * \\copydoc libcamera::Converter::start\n  */\n",
    "prefixes": [
        "v8",
        "2/4"
    ]
}