Show a patch.

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

{
    "id": 22359,
    "url": "https://patchwork.libcamera.org/api/patches/22359/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/22359/",
    "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": "<20241216154124.203650-14-stefan.klug@ideasonboard.com>",
    "date": "2024-12-16T15:40:53",
    "name": "[v4,13/20] libcamera: converter: Add functions to adjust config",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "3fd33cc79d2c7af72e7c0824dcf3151feda51505",
    "submitter": {
        "id": 184,
        "url": "https://patchwork.libcamera.org/api/people/184/?format=api",
        "name": "Stefan Klug",
        "email": "stefan.klug@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/22359/mbox/",
    "series": [
        {
            "id": 4896,
            "url": "https://patchwork.libcamera.org/api/series/4896/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4896",
            "date": "2024-12-16T15:40:40",
            "name": "rkisp1: Fix aspect ratio and ScalerCrop",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/4896/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/22359/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/22359/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 657CBC32F6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Dec 2024 15:42:25 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DD5F367F93;\n\tMon, 16 Dec 2024 16:42:24 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 371FC67F8B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Dec 2024 16:42:22 +0100 (CET)",
            "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:bfdf:3a3c:e45:66e3])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 994D782A;\n\tMon, 16 Dec 2024 16:41:45 +0100 (CET)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"pJDS9ko1\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1734363705;\n\tbh=FhxnPk/5CqUsH6IMDYtk9umBD0796wjt/SU4zUBTu/g=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=pJDS9ko1Y82yTBAUFBsYLCRDzkwHgwj7Nswh0aS4e0RzPD9A1nbPQ505cnuG6X3D2\n\t3K+nxEUPCtaBIGspivJftJCnX4GFe+bKLMWAPZrfdMvjA+VWxA13G6nbEYVV35d5B8\n\tYCu4KqHbHQwNMY89f/ML9dwGRqjWe5IrwKdcc1eY=",
        "From": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>",
        "Subject": "[PATCH v4 13/20] libcamera: converter: Add functions to adjust\n\tconfig",
        "Date": "Mon, 16 Dec 2024 16:40:53 +0100",
        "Message-ID": "<20241216154124.203650-14-stefan.klug@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "In-Reply-To": "<20241216154124.203650-1-stefan.klug@ideasonboard.com>",
        "References": "<20241216154124.203650-1-stefan.klug@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": "From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nAdd to the Converter interface two functions used by pipeline handlers\nto validate and adjust the converter input and output configurations\nby specifying the desired alignment for the adjustment.\n\nAdd the adjustInputSize() and adjustOutputSize() functions that allows\nto adjust the converter input/output sizes with the desired alignment.\n\nAdd a validateOutput() function meant to be used by the pipeline\nhandler implementations of validate(). The function adjusts a\nStreamConfiguration to a valid configuration produced by the Converter.\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\nReviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\n\nChanges in v4:\n- Added docs for returned null sizes\n- Replaced alignment flags with alignment enum\n- Collected tags\n\nChanges in v3:\n- Added this patch\n\nSigned-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n include/libcamera/internal/converter.h        |  15 ++\n .../internal/converter/converter_v4l2_m2m.h   |  11 ++\n src/libcamera/converter.cpp                   |  40 +++++\n .../converter/converter_v4l2_m2m.cpp          | 169 ++++++++++++++++++\n 4 files changed, 235 insertions(+)",
    "diff": "diff --git a/include/libcamera/internal/converter.h b/include/libcamera/internal/converter.h\nindex afea4624041b..644ec429bb25 100644\n--- a/include/libcamera/internal/converter.h\n+++ b/include/libcamera/internal/converter.h\n@@ -41,6 +41,11 @@ public:\n \n \tusing Features = Flags<Feature>;\n \n+\tenum class Alignment {\n+\t\tDown = 0,\n+\t\tUp,\n+\t};\n+\n \tConverter(MediaDevice *media, Features features = Feature::None);\n \tvirtual ~Converter();\n \n@@ -51,9 +56,19 @@ public:\n \tvirtual std::vector<PixelFormat> formats(PixelFormat input) = 0;\n \tvirtual SizeRange sizes(const Size &input) = 0;\n \n+\tvirtual Size adjustInputSize(const PixelFormat &pixFmt,\n+\t\t\t\t     const Size &size,\n+\t\t\t\t     Alignment align = Alignment::Down) = 0;\n+\tvirtual Size adjustOutputSize(const PixelFormat &pixFmt,\n+\t\t\t\t      const Size &size,\n+\t\t\t\t      Alignment align = Alignment::Down) = 0;\n+\n \tvirtual std::tuple<unsigned int, unsigned int>\n \tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) = 0;\n \n+\tvirtual int validateOutput(StreamConfiguration *cfg, bool *adjusted,\n+\t\t\t\t   Alignment align = Alignment::Down) = 0;\n+\n \tvirtual int configure(const StreamConfiguration &inputCfg,\n \t\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;\n \tvirtual bool isConfigured(const Stream *stream) const = 0;\ndiff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h\nindex 1ccbfc7c2d4e..0ad7bf7fdbe2 100644\n--- a/include/libcamera/internal/converter/converter_v4l2_m2m.h\n+++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h\n@@ -47,6 +47,11 @@ public:\n \tstd::tuple<unsigned int, unsigned int>\n \tstrideAndFrameSize(const PixelFormat &pixelFormat, const Size &size) override;\n \n+\tSize adjustInputSize(const PixelFormat &pixFmt,\n+\t\t\t     const Size &size, Alignment align = Alignment::Down) override;\n+\tSize adjustOutputSize(const PixelFormat &pixFmt,\n+\t\t\t      const Size &size, Alignment align = Alignment::Down) override;\n+\n \tint configure(const StreamConfiguration &inputCfg,\n \t\t      const std::vector<std::reference_wrapper<StreamConfiguration>>\n \t\t      &outputCfg) override;\n@@ -57,6 +62,9 @@ public:\n \tint start() override;\n \tvoid stop() override;\n \n+\tint validateOutput(StreamConfiguration *cfg, bool *adjusted,\n+\t\t\t   Alignment align = Alignment::Down) override;\n+\n \tint queueBuffers(FrameBuffer *input,\n \t\t\t const std::map<const Stream *, FrameBuffer *> &outputs) override;\n \n@@ -104,6 +112,9 @@ private:\n \t\tstd::pair<Rectangle, Rectangle> inputCropBounds_;\n \t};\n \n+\tSize adjustSizes(const Size &size, const std::vector<SizeRange> &ranges,\n+\t\t\t Alignment align);\n+\n \tstd::unique_ptr<V4L2M2MDevice> m2m_;\n \n \tstd::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;\ndiff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp\nindex 09ebf4f57d12..d551b908d523 100644\n--- a/src/libcamera/converter.cpp\n+++ b/src/libcamera/converter.cpp\n@@ -50,6 +50,16 @@ LOG_DEFINE_CATEGORY(Converter)\n  * \\brief A bitwise combination of features supported by the converter\n  */\n \n+/**\n+ * \\enum Converter::Alignment\n+ * \\brief The alignment mode specified when adjusting the converter input or\n+ * output sizes\n+ * \\var Converter::Alignment::Down\n+ * \\brief Adjust the Converter sizes to a smaller valid size\n+ * \\var Converter::Alignment::Up\n+ * \\brief Adjust the Converter sizes to a larger valid size\n+ */\n+\n /**\n  * \\brief Construct a Converter instance\n  * \\param[in] media The media device implementing the converter\n@@ -110,6 +120,26 @@ Converter::~Converter()\n  * \\return A range of output image sizes\n  */\n \n+/**\n+ * \\fn Converter::adjustInputSize()\n+ * \\brief Adjust the converter input \\a size to a valid value\n+ * \\param[in] pixFmt The pixel format of the converter input stream\n+ * \\param[in] size The converter input size to adjust to a valid value\n+ * \\param[in] align The desired alignment\n+ * \\return The adjusted converter input size or a null Size if \\a size cannot\n+ * be adjusted\n+ */\n+\n+/**\n+ * \\fn Converter::adjustOutputSize()\n+ * \\brief Adjust the converter output \\a size to a valid value\n+ * \\param[in] pixFmt The pixel format of the converter output stream\n+ * \\param[in] size The converter output size to adjust to a valid value\n+ * \\param[in] align The desired alignment\n+ * \\return The adjusted converter output size or a null Size if \\a size cannot\n+ * be adjusted\n+ */\n+\n /**\n  * \\fn Converter::strideAndFrameSize()\n  * \\brief Retrieve the output stride and frame size for an input configutation\n@@ -118,6 +148,16 @@ Converter::~Converter()\n  * \\return A tuple indicating the stride and frame size or an empty tuple on error\n  */\n \n+/**\n+ * \\fn Converter::validateOutput()\n+ * \\brief Validate and possibily adjust \\a cfg to a valid converter output\n+ * \\param[inout] cfg The StreamConfiguration to validate and adjust\n+ * \\param[out] adjusted Set to true if \\a cfg has been adjusted\n+ * \\param[in] align The desired alignment\n+ * \\return 0 if \\a cfg is valid or has been adjusted, a negative error code\n+ * otherwise if \\a cfg cannot be adjusted\n+ */\n+\n /**\n  * \\fn Converter::configure()\n  * \\brief Configure a set of output stream conversion from an input stream\ndiff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp\nindex 83daca15b37e..566f18ced51c 100644\n--- a/src/libcamera/converter/converter_v4l2_m2m.cpp\n+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp\n@@ -8,6 +8,7 @@\n \n #include \"libcamera/internal/converter/converter_v4l2_m2m.h\"\n \n+#include <algorithm>\n #include <limits.h>\n \n #include <libcamera/base/log.h>\n@@ -400,6 +401,127 @@ V4L2M2MConverter::strideAndFrameSize(const PixelFormat &pixelFormat,\n \treturn std::make_tuple(format.planes[0].bpl, format.planes[0].size);\n }\n \n+/**\n+ * \\copydoc libcamera::Converter::adjustInputSize\n+ */\n+Size V4L2M2MConverter::adjustInputSize(const PixelFormat &pixFmt,\n+\t\t\t\t       const Size &size, Alignment align)\n+{\n+\tauto formats = m2m_->output()->formats();\n+\tV4L2PixelFormat v4l2PixFmt = m2m_->output()->toV4L2PixelFormat(pixFmt);\n+\n+\tauto it = formats.find(v4l2PixFmt);\n+\tif (it == formats.end()) {\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Unsupported pixel format \" << pixFmt;\n+\t\treturn {};\n+\t}\n+\n+\treturn adjustSizes(size, it->second, align);\n+}\n+\n+/**\n+ * \\copydoc libcamera::Converter::adjustOutputSize\n+ */\n+Size V4L2M2MConverter::adjustOutputSize(const PixelFormat &pixFmt,\n+\t\t\t\t\tconst Size &size, Alignment align)\n+{\n+\tauto formats = m2m_->capture()->formats();\n+\tV4L2PixelFormat v4l2PixFmt = m2m_->capture()->toV4L2PixelFormat(pixFmt);\n+\n+\tauto it = formats.find(v4l2PixFmt);\n+\tif (it == formats.end()) {\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Unsupported pixel format \" << pixFmt;\n+\t\treturn {};\n+\t}\n+\n+\treturn adjustSizes(size, it->second, align);\n+}\n+\n+Size V4L2M2MConverter::adjustSizes(const Size &cfgSize,\n+\t\t\t\t   const std::vector<SizeRange> &ranges,\n+\t\t\t\t   Alignment align)\n+{\n+\tSize size = cfgSize;\n+\n+\tif (ranges.size() == 1) {\n+\t\t/*\n+\t\t * The device supports either V4L2_FRMSIZE_TYPE_CONTINUOUS or\n+\t\t * V4L2_FRMSIZE_TYPE_STEPWISE.\n+\t\t */\n+\t\tconst SizeRange &range = *ranges.begin();\n+\n+\t\tsize.width = std::clamp(size.width, range.min.width,\n+\t\t\t\t\trange.max.width);\n+\t\tsize.height = std::clamp(size.height, range.min.height,\n+\t\t\t\t\t range.max.height);\n+\n+\t\t/*\n+\t\t * Check if any alignment is needed. If the sizes are already\n+\t\t * aligned, or the device supports V4L2_FRMSIZE_TYPE_CONTINUOUS\n+\t\t * with hStep and vStep equal to 1, we're done here.\n+\t\t */\n+\t\tint widthR = size.width % range.hStep;\n+\t\tint heightR = size.height % range.vStep;\n+\n+\t\t/* Align up or down according to the caller request. */\n+\n+\t\tif (widthR != 0)\n+\t\t\tsize.width = size.width - widthR\n+\t\t\t\t   + ((align == Alignment::Up) ? range.hStep : 0);\n+\n+\t\tif (heightR != 0)\n+\t\t\tsize.height = size.height - heightR\n+\t\t\t\t    + ((align == Alignment::Up) ? range.vStep : 0);\n+\t} else {\n+\t\t/*\n+\t\t * The device supports V4L2_FRMSIZE_TYPE_DISCRETE, find the\n+\t\t * size closer to the requested output configuration.\n+\t\t *\n+\t\t * The size ranges vector is not ordered, so we sort it first.\n+\t\t * If we align up, start from the larger element.\n+\t\t */\n+\t\tstd::vector<Size> sizes(ranges.size());\n+\t\tstd::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes),\n+\t\t\t       [](const SizeRange &range) { return range.max; });\n+\t\tstd::sort(sizes.begin(), sizes.end());\n+\n+\t\tif (align == Alignment::Up)\n+\t\t\tstd::reverse(sizes.begin(), sizes.end());\n+\n+\t\t/*\n+\t\t * Return true if s2 is valid according to the desired\n+\t\t * alignment: smaller than s1 if we align down, larger than s1\n+\t\t * if we align up.\n+\t\t */\n+\t\tauto nextSizeValid = [](const Size &s1, const Size &s2, Alignment a) {\n+\t\t\treturn a == Alignment::Down\n+\t\t\t\t? (s1.width > s2.width && s1.height > s2.height)\n+\t\t\t\t: (s1.width < s2.width && s1.height < s2.height);\n+\t\t};\n+\n+\t\tSize newSize;\n+\t\tfor (const Size &sz : sizes) {\n+\t\t\tif (!nextSizeValid(size, sz, align))\n+\t\t\t\tbreak;\n+\n+\t\t\tnewSize = sz;\n+\t\t}\n+\n+\t\tif (newSize.isNull()) {\n+\t\t\tLOG(Converter, Error)\n+\t\t\t\t<< \"Cannot adjust \" << cfgSize\n+\t\t\t\t<< \" to a supported converter size\";\n+\t\t\treturn {};\n+\t\t}\n+\n+\t\tsize = newSize;\n+\t}\n+\n+\treturn size;\n+}\n+\n /**\n  * \\copydoc libcamera::Converter::configure\n  */\n@@ -522,6 +644,53 @@ void V4L2M2MConverter::stop()\n \t\titer.second->stop();\n }\n \n+/**\n+ * \\copydoc libcamera::Converter::validateOutput\n+ */\n+int V4L2M2MConverter::validateOutput(StreamConfiguration *cfg, bool *adjusted,\n+\t\t\t\t     Alignment align)\n+{\n+\tV4L2VideoDevice *capture = m2m_->capture();\n+\tV4L2VideoDevice::Formats fmts = capture->formats();\n+\n+\tif (adjusted)\n+\t\t*adjusted = false;\n+\n+\tPixelFormat fmt = cfg->pixelFormat;\n+\tV4L2PixelFormat v4l2PixFmt = capture->toV4L2PixelFormat(fmt);\n+\n+\tauto it = fmts.find(v4l2PixFmt);\n+\tif (it == fmts.end()) {\n+\t\tit = fmts.begin();\n+\t\tv4l2PixFmt = it->first;\n+\t\tcfg->pixelFormat = v4l2PixFmt.toPixelFormat();\n+\n+\t\tif (adjusted)\n+\t\t\t*adjusted = true;\n+\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Converter output pixel format adjusted to \"\n+\t\t\t<< cfg->pixelFormat;\n+\t}\n+\n+\tconst Size cfgSize = cfg->size;\n+\tcfg->size = adjustSizes(cfgSize, it->second, align);\n+\n+\tif (cfg->size.isNull())\n+\t\treturn -EINVAL;\n+\n+\tif (cfg->size.width != cfgSize.width ||\n+\t    cfg->size.height != cfgSize.height) {\n+\t\tLOG(Converter, Info)\n+\t\t\t<< \"Converter size adjusted to \"\n+\t\t\t<< cfg->size;\n+\t\tif (adjusted)\n+\t\t\t*adjusted = true;\n+\t}\n+\n+\treturn 0;\n+}\n+\n /**\n  * \\copydoc libcamera::Converter::queueBuffers\n  */\n",
    "prefixes": [
        "v4",
        "13/20"
    ]
}