Show a patch.

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

{
    "id": 21493,
    "url": "https://patchwork.libcamera.org/api/patches/21493/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/21493/",
    "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": "<20241003074720.18882-8-naush@raspberrypi.com>",
    "date": "2024-10-03T07:47:20",
    "name": "[v3,7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "5ff3459edc646240bb4ae74019fbae68558fb286",
    "submitter": {
        "id": 34,
        "url": "https://patchwork.libcamera.org/api/people/34/?format=api",
        "name": "Naushir Patuck",
        "email": "naush@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/21493/mbox/",
    "series": [
        {
            "id": 4651,
            "url": "https://patchwork.libcamera.org/api/series/4651/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4651",
            "date": "2024-10-03T07:47:13",
            "name": "Raspberry Pi: Add controls::rpi::ScalerCrops",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/4651/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/21493/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/21493/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 9509DC32DB\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  3 Oct 2024 07:54:04 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 719A163530;\n\tThu,  3 Oct 2024 09:54:03 +0200 (CEST)",
            "from mail-wm1-x331.google.com (mail-wm1-x331.google.com\n\t[IPv6:2a00:1450:4864:20::331])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D089563530\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  3 Oct 2024 09:53:50 +0200 (CEST)",
            "by mail-wm1-x331.google.com with SMTP id\n\t5b1f17b1804b1-42ca4e0014dso949645e9.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 03 Oct 2024 00:53:50 -0700 (PDT)",
            "from naush-laptop.pitowers.org ([93.93.133.154])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-42f8025b583sm8740825e9.12.2024.10.03.00.53.49\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 03 Oct 2024 00:53:49 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"TS5R9rXD\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1727942030; x=1728546830;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=LJAXZj7wiaO7Oa3XnUk7pfIlTYztCA94P+HfSdQLQC0=;\n\tb=TS5R9rXDgDsMMskVOO3InYtZrvI6b6ipUdWqKvW7giYvlx+mXBiu4PyCScP5/CKNwK\n\t6I7ytxUem6/DrZaVfbIkBGIj7rpEG6g2lV0pWrXYpeLIPTxnkgM1ozZammmjcti2HDpI\n\tCOetDdciwdjfHgzuBspMR0lamc18td8eVkmsKm6VrU8Tsgl0C8N7mX9cf6CxIeqZaX4q\n\t8G4nWV3EgXv8wd7URwpttVUNtvZ8wvVlfyEpPI7KvaGhrsQwT5bFAYXEdAr9AysI/34B\n\tz3QXrMQR1oNqG2bw3XbnF2nfsGN6/aAyrCIt4JcKjSKN+IPIoQmScq7xP22uFX8xIm5Q\n\tCpGA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1727942030; x=1728546830;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=LJAXZj7wiaO7Oa3XnUk7pfIlTYztCA94P+HfSdQLQC0=;\n\tb=pQc1VX2B0wsB0BhxA8oh+PWeYVOQxM+pk1AjFuIUOz7kjD8z69win+LwgUQCdBF2Oc\n\t3vB5m5xNhS3PKYgr87Zk86RsFQdyITDIwGQrLXuv9sHTVs3ftU+xI6VPmGCEtTFAHZjn\n\tNUeHprVytQIZFmpwenFJFeEDDq8lzocqFMq0txDJnP8vMGU4fFw8XlUGQus7z+QmOu9E\n\tzWq7xTWgNHrrOzFddogTXSJp2RdPvWQ/j4C1p647D7asOtLBs/KHldnghY4dJr48hrLD\n\t75PzaX1fqzIz4fIGA+NyycmynKcV1OFnWW2isIwIWyr4MP1GOp8xr3X+ZlSFqCEpRTA2\n\tzeEg==",
        "X-Gm-Message-State": "AOJu0YwyVz3st4JQ/kN8sDB9AdJm4Jx+qY6uOr343tzz5GbMTBKCfCvi\n\ta9GX2WpXzp7X6UM8IxzSXKptnRejOKTOzlb2gJhCliskWvIi2NLc8UNzV94huZmYsQMBQH8Qc2b\n\tZ",
        "X-Google-Smtp-Source": "AGHT+IGqKnJO7cDNKvE+rq+2a48gxIUnBho1u9T434peERQGuKb20WclxJqVo8hhP0E8bFC45jVS4Q==",
        "X-Received": "by 2002:a05:600c:4f50:b0:42c:c59a:ac21 with SMTP id\n\t5b1f17b1804b1-42f777b0b7fmr21661395e9.2.1727942029973; \n\tThu, 03 Oct 2024 00:53:49 -0700 (PDT)",
        "From": "Naushir Patuck <naush@raspberrypi.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Naushir Patuck <naush@raspberrypi.com>,\n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>",
        "Subject": "[PATCH v3 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops",
        "Date": "Thu,  3 Oct 2024 08:47:20 +0100",
        "Message-Id": "<20241003074720.18882-8-naush@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20241003074720.18882-1-naush@raspberrypi.com>",
        "References": "<20241003074720.18882-1-naush@raspberrypi.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": "Handle multiple scaler crops being set through the rpi::ScalerCrops\ncontrol. We now populate the cropParams_ map in the loop where we handle\nthe output stream configuration items. The key of this map is the index\nof the stream configuration structure set by the application. This will\nalso be the same index used to specify the crop rectangles through the\nScalerCrops control.\n\nCameraData::applyScalerCrop() has been adapted to look at either\ncontrols::ScalerCrop or controls::rpi::ScalerCrops. The former takes\npriority over the latter, and if present, will apply the same scaler\ncrop to all output streams.\n\nFinally return all crops through the same ScalerCrops control via\nrequest metadata. The first configure stream's crop rectangle is also\nreturned via the ScalerCrop control in the request metadata.\n\nSigned-off-by: Naushir Patuck <naush@raspberrypi.com>\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n .../pipeline/rpi/common/pipeline_base.cpp     | 76 ++++++++++++++-----\n 1 file changed, 59 insertions(+), 17 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\nindex 267e6bd9cd70..eba4dad6d212 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n@@ -181,12 +181,14 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \n \trawStreams_.clear();\n \toutStreams_.clear();\n+\tunsigned int rawStreamIndex = 0;\n+\tunsigned int outStreamIndex = 0;\n \n-\tfor (const auto &[index, cfg] : utils::enumerate(config_)) {\n+\tfor (auto &cfg : config_) {\n \t\tif (PipelineHandlerBase::isRaw(cfg.pixelFormat))\n-\t\t\trawStreams_.emplace_back(index, &cfg);\n+\t\t\trawStreams_.emplace_back(rawStreamIndex++, &cfg);\n \t\telse\n-\t\t\toutStreams_.emplace_back(index, &cfg);\n+\t\t\toutStreams_.emplace_back(outStreamIndex++, &cfg);\n \t}\n \n \t/* Sort the streams so the highest resolution is first. */\n@@ -565,10 +567,24 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)\n \tconst auto cropParamsIt = data->cropParams_.find(0);\n \tif (cropParamsIt != data->cropParams_.end()) {\n \t\tconst CameraData::CropParams &cropParams = cropParamsIt->second;\n-\t\t/* Add the ScalerCrop control limits based on the current mode. */\n+\t\t/*\n+\t\t * Add the ScalerCrop control limits based on the current mode and\n+\t\t * the first configured stream.\n+\t\t */\n \t\tRectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize));\n \t\tctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,\n \t\t\t\t\t\t\t     data->scaleIspCrop(cropParams.ispCrop));\n+\t\tif (data->cropParams_.size() == 2) {\n+\t\t\t/*\n+\t\t\t * The control map for rpi::ScalerCrops has the min value\n+\t\t\t * as the default crop for stream 0, max value as the default\n+\t\t\t * value for stream 1.\n+\t\t\t */\n+\t\t\tctrlMap[&controls::rpi::ScalerCrops] =\n+\t\t\t\tControlInfo(data->scaleIspCrop(data->cropParams_.at(0).ispCrop),\n+\t\t\t\t\t    data->scaleIspCrop(data->cropParams_.at(1).ispCrop),\n+\t\t\t\t\t    ctrlMap[&controls::ScalerCrop].def());\n+\t\t}\n \t}\n \n \tdata->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());\n@@ -1295,11 +1311,29 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const\n \n void CameraData::applyScalerCrop(const ControlList &controls)\n {\n-\tconst auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);\n-\tconst auto cropParamsIt = cropParams_.find(0);\n-\tif (scalerCrop && cropParamsIt != cropParams_.end()) {\n-\t\tRectangle nativeCrop = *scalerCrop;\n-\t\tCropParams &cropParams = cropParamsIt->second;\n+\tconst auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);\n+\tconst auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);\n+\tstd::vector<Rectangle> scalerCrops;\n+\n+\t/*\n+\t * First thing to do is create a vector of crops to apply to each ISP output\n+\t * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if\n+\t * present.\n+\t *\n+\t * If controls::ScalerCrop is present, apply the same crop to all ISP output\n+\t * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops\n+\t * to the ISP output streams, indexed by the same order in which they had\n+\t * been configured. This is not the same as the ISP output index.\n+\t */\n+\tfor (unsigned int i = 0; i < cropParams_.size(); i++) {\n+\t\tif (scalerCropRPi && i < scalerCropRPi->size())\n+\t\t\tscalerCrops.push_back(scalerCropRPi->data()[i]);\n+\t\telse if (scalerCropCore)\n+\t\t\tscalerCrops.push_back(*scalerCropCore);\n+\t}\n+\n+\tfor (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {\n+\t\tRectangle nativeCrop = scalerCrop;\n \n \t\tif (!nativeCrop.width || !nativeCrop.height)\n \t\t\tnativeCrop = { 0, 0, 1, 1 };\n@@ -1315,13 +1349,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)\n \t\t * 2. With the same mid-point, if possible.\n \t\t * 3. But it can't go outside the sensor area.\n \t\t */\n-\t\tSize minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n+\t\tSize minSize = cropParams_.at(i).ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n \t\tSize size = ispCrop.size().expandedTo(minSize);\n \t\tispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));\n \n-\t\tif (ispCrop != cropParams.ispCrop) {\n-\t\t\tcropParams.ispCrop = ispCrop;\n-\t\t\tplatformSetIspCrop(cropParams.ispIndex, ispCrop);\n+\t\tif (ispCrop != cropParams_.at(i).ispCrop) {\n+\t\t\tcropParams_.at(i).ispCrop = ispCrop;\n+\t\t\tplatformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop);\n \t\t}\n \t}\n }\n@@ -1478,10 +1512,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request\n \trequest->metadata().set(controls::SensorTimestamp,\n \t\t\t\tbufferControls.get(controls::SensorTimestamp).value_or(0));\n \n-\tconst auto cropParamsIt = cropParams_.find(0);\n-\tif (cropParamsIt != cropParams_.end())\n-\t\trequest->metadata().set(controls::ScalerCrop,\n-\t\t\t\t\tscaleIspCrop(cropParamsIt->second.ispCrop));\n+\tif (cropParams_.size()) {\n+\t\tstd::vector<Rectangle> crops;\n+\n+\t\tfor (auto const &[k, v] : cropParams_)\n+\t\t\tcrops.push_back(scaleIspCrop(v.ispCrop));\n+\n+\t\trequest->metadata().set(controls::ScalerCrop, crops[0]);\n+\t\tif (crops.size() > 1) {\n+\t\t\trequest->metadata().set(controls::rpi::ScalerCrops,\n+\t\t\t\t\t\tSpan<const Rectangle>(crops.data(), crops.size()));\n+\t\t}\n+\t}\n }\n \n } /* namespace libcamera */\n",
    "prefixes": [
        "v3",
        "7/7"
    ]
}