[{"id":31997,"web_url":"https://patchwork.libcamera.org/comment/31997/","msgid":"<CAEmqJPrgtaw22U7yTEs-SjwbJ4RKFzg+jGfSvnbt38u4DhhLKw@mail.gmail.com>","date":"2024-11-04T09:05:04","subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi all,\n\nOne last tag needed on this patch and the series can then be merged.\n\nRegards,\nNaush\n\nOn Thu, 31 Oct 2024 at 09:52, Naushir Patuck <naush@raspberrypi.com> wrote:\n>\n> Handle multiple scaler crops being set through the rpi::ScalerCrops\n> control. We now populate the cropParams_ map in the loop where we handle\n> the output stream configuration items. The key of this map is the index\n> of the stream configuration structure set by the application. This will\n> also be the same index used to specify the crop rectangles through the\n> ScalerCrops control.\n>\n> CameraData::applyScalerCrop() has been adapted to look at either\n> controls::ScalerCrop or controls::rpi::ScalerCrops. The latter takes\n> priority over the former. If only controls::ScalerCrop is provided, the\n> pipeline handler will apply the same scaler crop to all output streams.\n>\n> Finally return all crops through the same ScalerCrops control via\n> request metadata. The first configure stream's crop rectangle is also\n> returned via the ScalerCrop control in the request metadata.\n>\n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> ---\n>  .../pipeline/rpi/common/pipeline_base.cpp     | 77 +++++++++++++++----\n>  1 file changed, 60 insertions(+), 17 deletions(-)\n>\n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> index 267e6bd9cd70..917c45b3f052 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>         rawStreams_.clear();\n>         outStreams_.clear();\n> +       unsigned int rawStreamIndex = 0;\n> +       unsigned int outStreamIndex = 0;\n>\n> -       for (const auto &[index, cfg] : utils::enumerate(config_)) {\n> +       for (auto &cfg : config_) {\n>                 if (PipelineHandlerBase::isRaw(cfg.pixelFormat))\n> -                       rawStreams_.emplace_back(index, &cfg);\n> +                       rawStreams_.emplace_back(rawStreamIndex++, &cfg);\n>                 else\n> -                       outStreams_.emplace_back(index, &cfg);\n> +                       outStreams_.emplace_back(outStreamIndex++, &cfg);\n>         }\n>\n>         /* Sort the streams so the highest resolution is first. */\n> @@ -565,10 +567,24 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)\n>         const auto cropParamsIt = data->cropParams_.find(0);\n>         if (cropParamsIt != data->cropParams_.end()) {\n>                 const CameraData::CropParams &cropParams = cropParamsIt->second;\n> -               /* Add the ScalerCrop control limits based on the current mode. */\n> +               /*\n> +                * Add the ScalerCrop control limits based on the current mode and\n> +                * the first configured stream.\n> +                */\n>                 Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize));\n>                 ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,\n>                                                              data->scaleIspCrop(cropParams.ispCrop));\n> +               if (data->cropParams_.size() == 2) {\n> +                       /*\n> +                        * The control map for rpi::ScalerCrops has the min value\n> +                        * as the default crop for stream 0, max value as the default\n> +                        * value for stream 1.\n> +                        */\n> +                       ctrlMap[&controls::rpi::ScalerCrops] =\n> +                               ControlInfo(data->scaleIspCrop(data->cropParams_.at(0).ispCrop),\n> +                                           data->scaleIspCrop(data->cropParams_.at(1).ispCrop),\n> +                                           ctrlMap[&controls::ScalerCrop].def());\n> +               }\n>         }\n>\n>         data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());\n> @@ -1295,11 +1311,30 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const\n>\n>  void CameraData::applyScalerCrop(const ControlList &controls)\n>  {\n> -       const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);\n> -       const auto cropParamsIt = cropParams_.find(0);\n> -       if (scalerCrop && cropParamsIt != cropParams_.end()) {\n> -               Rectangle nativeCrop = *scalerCrop;\n> -               CropParams &cropParams = cropParamsIt->second;\n> +       const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);\n> +       const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);\n> +       std::vector<Rectangle> scalerCrops;\n> +\n> +       /*\n> +        * First thing to do is create a vector of crops to apply to each ISP output\n> +        * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if\n> +        * present.\n> +        *\n> +        * If controls::rpi::ScalerCrops is preset, apply the given crops to the\n> +        * ISP output streams, indexed by the same order in which they had been\n> +        * configured. This is not the same as the ISP output index. Otherwise\n> +        * if controls::ScalerCrop is present, apply the same crop to all ISP\n> +        * output streams.\n> +        */\n> +       for (unsigned int i = 0; i < cropParams_.size(); i++) {\n> +               if (scalerCropRPi && i < scalerCropRPi->size())\n> +                       scalerCrops.push_back(scalerCropRPi->data()[i]);\n> +               else if (scalerCropCore)\n> +                       scalerCrops.push_back(*scalerCropCore);\n> +       }\n> +\n> +       for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {\n> +               Rectangle nativeCrop = scalerCrop;\n>\n>                 if (!nativeCrop.width || !nativeCrop.height)\n>                         nativeCrop = { 0, 0, 1, 1 };\n> @@ -1315,13 +1350,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)\n>                  * 2. With the same mid-point, if possible.\n>                  * 3. But it can't go outside the sensor area.\n>                  */\n> -               Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n> +               Size minSize = cropParams_.at(i).ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n>                 Size size = ispCrop.size().expandedTo(minSize);\n>                 ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));\n>\n> -               if (ispCrop != cropParams.ispCrop) {\n> -                       cropParams.ispCrop = ispCrop;\n> -                       platformSetIspCrop(cropParams.ispIndex, ispCrop);\n> +               if (ispCrop != cropParams_.at(i).ispCrop) {\n> +                       cropParams_.at(i).ispCrop = ispCrop;\n> +                       platformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop);\n>                 }\n>         }\n>  }\n> @@ -1478,10 +1513,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request\n>         request->metadata().set(controls::SensorTimestamp,\n>                                 bufferControls.get(controls::SensorTimestamp).value_or(0));\n>\n> -       const auto cropParamsIt = cropParams_.find(0);\n> -       if (cropParamsIt != cropParams_.end())\n> -               request->metadata().set(controls::ScalerCrop,\n> -                                       scaleIspCrop(cropParamsIt->second.ispCrop));\n> +       if (cropParams_.size()) {\n> +               std::vector<Rectangle> crops;\n> +\n> +               for (auto const &[k, v] : cropParams_)\n> +                       crops.push_back(scaleIspCrop(v.ispCrop));\n> +\n> +               request->metadata().set(controls::ScalerCrop, crops[0]);\n> +               if (crops.size() > 1) {\n> +                       request->metadata().set(controls::rpi::ScalerCrops,\n> +                                               Span<const Rectangle>(crops.data(), crops.size()));\n> +               }\n> +       }\n>  }\n>\n>  } /* namespace libcamera */\n> --\n> 2.34.1\n>","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 D69B1BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Nov 2024 09:05:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F1D9A653C5;\n\tMon,  4 Nov 2024 10:05:43 +0100 (CET)","from mail-yb1-xb2f.google.com (mail-yb1-xb2f.google.com\n\t[IPv6:2607:f8b0:4864:20::b2f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 50B8760396\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Nov 2024 10:05:41 +0100 (CET)","by mail-yb1-xb2f.google.com with SMTP id\n\t3f1490d57ef6-e3337b09c9dso84084276.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 04 Nov 2024 01:05:41 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"Dop6qI8N\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1730711140; x=1731315940;\n\tdarn=lists.libcamera.org; \n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc:subject:date:message-id:reply-to;\n\tbh=lRuyBHRs7ZOKnIc0EqoilfigImkA9n5KtjRbYt1riMA=;\n\tb=Dop6qI8NomrdPskDByOFXrPwwlXHF23PA1s/fx5Ldi+c7RJM+LVlz5JO25ZECChnIh\n\t0AVNY35th7Rw4siNiu49lE64dFPvqIYzLc1Mo6icisXWWWj8TBek1AJag7HQjlNJeWB5\n\tRI2+BAuKd3GJNzPYlpda+MB6g+nT2NP/gp0rUr4mMtxjFvFWvhTLaoWxHKTctQXn2tgv\n\tV6CLLfepnzxbdORXQP9hiAjelpliP8BKX6dgBnsHMdNDxLZajUPD5baJSla4uFWlrM4W\n\teqsYNoZiFLLdk2jS/uf5QM06xdqA4IVOLS0xQ8iXxkqB4eZsNHW+h/a/eo5upESx403h\n\tonXA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1730711140; x=1731315940;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-message-state:from:to:cc:subject:date:message-id\n\t:reply-to;\n\tbh=lRuyBHRs7ZOKnIc0EqoilfigImkA9n5KtjRbYt1riMA=;\n\tb=bzn6V26wg6ufr/2BPiLxCIXrA0FQmo/vbv3lBbUdZxPrcjLjwflUkTCm0YNQqYllMZ\n\t5Z3CGHZrMKgyJubC1pK/KNeHhWpZpXQ60F0ncFmGZrpKqG78MR8lyQzTc61kiQA05GoN\n\trxC7mMbDqAZn7Us04HCVxzpUNWQBdq1QlSXKfMvFAbs2oycI1kcmr0S6HgTgxl24mGdZ\n\tMFn69BOsZa6jXX7oMiHDHNyJida9rGNG25DwUqiRb3LeiwRr7nCDFx8izQcoSWoDPYwK\n\typ1LuaegoygPJxdxq0rKLVTfTazQ8hz2KzP8se6ZeidVp1gWQSQ3iSeEXd79lVSr3XLJ\n\tw7FQ==","X-Gm-Message-State":"AOJu0YxYIdHfYAGf8hUgystFoWMTaCvWQAd+qJFawPdvQQfPqFngkx+3\n\tksc4LutpTnFCQiqEI/gE6flcPrPxzZkuJkaJceFgPIMhZfJaz3konP46EBzygBkRop1bMsMo35C\n\ti0yiCRj+2ZuI+xHs70bktTFx/TTOEHSFvoSu8Vo071c5pSSCh","X-Google-Smtp-Source":"AGHT+IH3y8Vf9Gfroo2M9tMoyayvbpmMw2QhRISFwsntoczMv6D3CfQZwhNn61v+EQHBnZEVxEP8HlZxVp7MRLWa9Jw=","X-Received":"by 2002:a05:6902:1b84:b0:e33:1ed4:2a02 with SMTP id\n\t3f1490d57ef6-e331ed42c5amr2016375276.11.1730711139581;\n\tMon, 04 Nov 2024 01:05:39 -0800 (PST)","MIME-Version":"1.0","References":"<20241031094957.18471-1-naush@raspberrypi.com>\n\t<20241031094957.18471-8-naush@raspberrypi.com>","In-Reply-To":"<20241031094957.18471-8-naush@raspberrypi.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Mon, 4 Nov 2024 09:05:04 +0000","Message-ID":"<CAEmqJPrgtaw22U7yTEs-SjwbJ4RKFzg+jGfSvnbt38u4DhhLKw@mail.gmail.com>","Subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","To":"libcamera-devel@lists.libcamera.org","Cc":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","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>"}},{"id":32011,"web_url":"https://patchwork.libcamera.org/comment/32011/","msgid":"<20241104152333.GB2725@pendragon.ideasonboard.com>","date":"2024-11-04T15:23:33","subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Naush,\n\nThank you for the patch.\n\nOn Thu, Oct 31, 2024 at 09:49:57AM +0000, Naushir Patuck wrote:\n> Handle multiple scaler crops being set through the rpi::ScalerCrops\n> control. We now populate the cropParams_ map in the loop where we handle\n> the output stream configuration items. The key of this map is the index\n> of the stream configuration structure set by the application. This will\n> also be the same index used to specify the crop rectangles through the\n> ScalerCrops control.\n> \n> CameraData::applyScalerCrop() has been adapted to look at either\n> controls::ScalerCrop or controls::rpi::ScalerCrops. The latter takes\n> priority over the former. If only controls::ScalerCrop is provided, the\n> pipeline handler will apply the same scaler crop to all output streams.\n> \n> Finally return all crops through the same ScalerCrops control via\n> request metadata. The first configure stream's crop rectangle is also\n> returned via the ScalerCrop control in the request metadata.\n> \n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> ---\n>  .../pipeline/rpi/common/pipeline_base.cpp     | 77 +++++++++++++++----\n>  1 file changed, 60 insertions(+), 17 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> index 267e6bd9cd70..917c45b3f052 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,30 @@ 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::rpi::ScalerCrops is preset, apply the given crops to the\n> +\t * ISP output streams, indexed by the same order in which they had been\n> +\t * configured. This is not the same as the ISP output index. Otherwise\n> +\t * if controls::ScalerCrop is present, apply the same crop to all ISP\n> +\t * output streams.\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 +1350,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 +1513,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 */","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 BD004BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Nov 2024 15:23:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E2EAA653D9;\n\tMon,  4 Nov 2024 16:23:39 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1E5AA653D3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Nov 2024 16:23:39 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 152F622E;\n\tMon,  4 Nov 2024 16:23:32 +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=\"UXRrv5BE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1730733812;\n\tbh=BJ/Ii4FF4JJCbV6Gy6PaPM5Q8b9FQiHNT2A0yY/FnuE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=UXRrv5BE0RRIq9b6O9zrfT4mDdhXmXrF73FwsWY1UADAuK196lv6TA3PfNkVtwI9z\n\tBjOBIRStYz3uke6GvbmWYG0OLu+dkW7c9JymvcVdrZ8WbiBY8afqb9hZAd5cXy3psC\n\ttJ3HyN2HBfEeQFXkJy2t5xqYvGGJWjGcrnIXeSyQ=","Date":"Mon, 4 Nov 2024 17:23:33 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Naushir Patuck <naush@raspberrypi.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>","Subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","Message-ID":"<20241104152333.GB2725@pendragon.ideasonboard.com>","References":"<20241031094957.18471-1-naush@raspberrypi.com>\n\t<20241031094957.18471-8-naush@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20241031094957.18471-8-naush@raspberrypi.com>","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>"}},{"id":32012,"web_url":"https://patchwork.libcamera.org/comment/32012/","msgid":"<20241104152500.GC2725@pendragon.ideasonboard.com>","date":"2024-11-04T15:25:00","subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, Nov 04, 2024 at 09:05:04AM +0000, Naushir Patuck wrote:\n> Hi all,\n> \n> One last tag needed on this patch and the series can then be merged.\n\nReviewed and pushed :-)\n\n> On Thu, 31 Oct 2024 at 09:52, Naushir Patuck <naush@raspberrypi.com> wrote:\n> >\n> > Handle multiple scaler crops being set through the rpi::ScalerCrops\n> > control. We now populate the cropParams_ map in the loop where we handle\n> > the output stream configuration items. The key of this map is the index\n> > of the stream configuration structure set by the application. This will\n> > also be the same index used to specify the crop rectangles through the\n> > ScalerCrops control.\n> >\n> > CameraData::applyScalerCrop() has been adapted to look at either\n> > controls::ScalerCrop or controls::rpi::ScalerCrops. The latter takes\n> > priority over the former. If only controls::ScalerCrop is provided, the\n> > pipeline handler will apply the same scaler crop to all output streams.\n> >\n> > Finally return all crops through the same ScalerCrops control via\n> > request metadata. The first configure stream's crop rectangle is also\n> > returned via the ScalerCrop control in the request metadata.\n> >\n> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> > ---\n> >  .../pipeline/rpi/common/pipeline_base.cpp     | 77 +++++++++++++++----\n> >  1 file changed, 60 insertions(+), 17 deletions(-)\n> >\n> > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> > index 267e6bd9cd70..917c45b3f052 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> >         rawStreams_.clear();\n> >         outStreams_.clear();\n> > +       unsigned int rawStreamIndex = 0;\n> > +       unsigned int outStreamIndex = 0;\n> >\n> > -       for (const auto &[index, cfg] : utils::enumerate(config_)) {\n> > +       for (auto &cfg : config_) {\n> >                 if (PipelineHandlerBase::isRaw(cfg.pixelFormat))\n> > -                       rawStreams_.emplace_back(index, &cfg);\n> > +                       rawStreams_.emplace_back(rawStreamIndex++, &cfg);\n> >                 else\n> > -                       outStreams_.emplace_back(index, &cfg);\n> > +                       outStreams_.emplace_back(outStreamIndex++, &cfg);\n> >         }\n> >\n> >         /* Sort the streams so the highest resolution is first. */\n> > @@ -565,10 +567,24 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)\n> >         const auto cropParamsIt = data->cropParams_.find(0);\n> >         if (cropParamsIt != data->cropParams_.end()) {\n> >                 const CameraData::CropParams &cropParams = cropParamsIt->second;\n> > -               /* Add the ScalerCrop control limits based on the current mode. */\n> > +               /*\n> > +                * Add the ScalerCrop control limits based on the current mode and\n> > +                * the first configured stream.\n> > +                */\n> >                 Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize));\n> >                 ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,\n> >                                                              data->scaleIspCrop(cropParams.ispCrop));\n> > +               if (data->cropParams_.size() == 2) {\n> > +                       /*\n> > +                        * The control map for rpi::ScalerCrops has the min value\n> > +                        * as the default crop for stream 0, max value as the default\n> > +                        * value for stream 1.\n> > +                        */\n> > +                       ctrlMap[&controls::rpi::ScalerCrops] =\n> > +                               ControlInfo(data->scaleIspCrop(data->cropParams_.at(0).ispCrop),\n> > +                                           data->scaleIspCrop(data->cropParams_.at(1).ispCrop),\n> > +                                           ctrlMap[&controls::ScalerCrop].def());\n> > +               }\n> >         }\n> >\n> >         data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());\n> > @@ -1295,11 +1311,30 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const\n> >\n> >  void CameraData::applyScalerCrop(const ControlList &controls)\n> >  {\n> > -       const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);\n> > -       const auto cropParamsIt = cropParams_.find(0);\n> > -       if (scalerCrop && cropParamsIt != cropParams_.end()) {\n> > -               Rectangle nativeCrop = *scalerCrop;\n> > -               CropParams &cropParams = cropParamsIt->second;\n> > +       const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);\n> > +       const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);\n> > +       std::vector<Rectangle> scalerCrops;\n> > +\n> > +       /*\n> > +        * First thing to do is create a vector of crops to apply to each ISP output\n> > +        * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if\n> > +        * present.\n> > +        *\n> > +        * If controls::rpi::ScalerCrops is preset, apply the given crops to the\n> > +        * ISP output streams, indexed by the same order in which they had been\n> > +        * configured. This is not the same as the ISP output index. Otherwise\n> > +        * if controls::ScalerCrop is present, apply the same crop to all ISP\n> > +        * output streams.\n> > +        */\n> > +       for (unsigned int i = 0; i < cropParams_.size(); i++) {\n> > +               if (scalerCropRPi && i < scalerCropRPi->size())\n> > +                       scalerCrops.push_back(scalerCropRPi->data()[i]);\n> > +               else if (scalerCropCore)\n> > +                       scalerCrops.push_back(*scalerCropCore);\n> > +       }\n> > +\n> > +       for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {\n> > +               Rectangle nativeCrop = scalerCrop;\n> >\n> >                 if (!nativeCrop.width || !nativeCrop.height)\n> >                         nativeCrop = { 0, 0, 1, 1 };\n> > @@ -1315,13 +1350,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)\n> >                  * 2. With the same mid-point, if possible.\n> >                  * 3. But it can't go outside the sensor area.\n> >                  */\n> > -               Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n> > +               Size minSize = cropParams_.at(i).ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n> >                 Size size = ispCrop.size().expandedTo(minSize);\n> >                 ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));\n> >\n> > -               if (ispCrop != cropParams.ispCrop) {\n> > -                       cropParams.ispCrop = ispCrop;\n> > -                       platformSetIspCrop(cropParams.ispIndex, ispCrop);\n> > +               if (ispCrop != cropParams_.at(i).ispCrop) {\n> > +                       cropParams_.at(i).ispCrop = ispCrop;\n> > +                       platformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop);\n> >                 }\n> >         }\n> >  }\n> > @@ -1478,10 +1513,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request\n> >         request->metadata().set(controls::SensorTimestamp,\n> >                                 bufferControls.get(controls::SensorTimestamp).value_or(0));\n> >\n> > -       const auto cropParamsIt = cropParams_.find(0);\n> > -       if (cropParamsIt != cropParams_.end())\n> > -               request->metadata().set(controls::ScalerCrop,\n> > -                                       scaleIspCrop(cropParamsIt->second.ispCrop));\n> > +       if (cropParams_.size()) {\n> > +               std::vector<Rectangle> crops;\n> > +\n> > +               for (auto const &[k, v] : cropParams_)\n> > +                       crops.push_back(scaleIspCrop(v.ispCrop));\n> > +\n> > +               request->metadata().set(controls::ScalerCrop, crops[0]);\n> > +               if (crops.size() > 1) {\n> > +                       request->metadata().set(controls::rpi::ScalerCrops,\n> > +                                               Span<const Rectangle>(crops.data(), crops.size()));\n> > +               }\n> > +       }\n> >  }\n> >\n> >  } /* namespace libcamera */","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 07C77BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Nov 2024 15:25:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B26A1653DE;\n\tMon,  4 Nov 2024 16:25:07 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 030C1653D3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Nov 2024 16:25:06 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 19A5922E;\n\tMon,  4 Nov 2024 16:24:59 +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=\"mlymPSDg\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1730733899;\n\tbh=surquOHGL/Y87uhxt+F2ZcUh1T4URuQubhw//yuHDLQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=mlymPSDgFXcnZEeGR7tXAn56i4SLdXsWHLzjpTkBGBfmAo2nEbH6jxH0zzWsGDELc\n\tifHayRopfrHIDN9ilyx5jXVIRgmdnHIpYfPVXpRLRld7ZvV/A/Ywr66Hm6dR9aWb5k\n\tJR912gX+X2xMIW7SuYAZhiy8nEsaWTpV6K35qldQ=","Date":"Mon, 4 Nov 2024 17:25:00 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Naushir Patuck <naush@raspberrypi.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>","Subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","Message-ID":"<20241104152500.GC2725@pendragon.ideasonboard.com>","References":"<20241031094957.18471-1-naush@raspberrypi.com>\n\t<20241031094957.18471-8-naush@raspberrypi.com>\n\t<CAEmqJPrgtaw22U7yTEs-SjwbJ4RKFzg+jGfSvnbt38u4DhhLKw@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAEmqJPrgtaw22U7yTEs-SjwbJ4RKFzg+jGfSvnbt38u4DhhLKw@mail.gmail.com>","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>"}},{"id":32013,"web_url":"https://patchwork.libcamera.org/comment/32013/","msgid":"<CAEmqJPro4d8yvjSJ1dwm3wsCLDn5pcW+XegGRpMMRCMobfVm7g@mail.gmail.com>","date":"2024-11-04T15:28:39","subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"On Mon, 4 Nov 2024 at 15:25, Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> On Mon, Nov 04, 2024 at 09:05:04AM +0000, Naushir Patuck wrote:\n> > Hi all,\n> >\n> > One last tag needed on this patch and the series can then be merged.\n>\n> Reviewed and pushed :-)\n\nThanks Laurent!\n\n>\n> > On Thu, 31 Oct 2024 at 09:52, Naushir Patuck <naush@raspberrypi.com> wrote:\n> > >\n> > > Handle multiple scaler crops being set through the rpi::ScalerCrops\n> > > control. We now populate the cropParams_ map in the loop where we handle\n> > > the output stream configuration items. The key of this map is the index\n> > > of the stream configuration structure set by the application. This will\n> > > also be the same index used to specify the crop rectangles through the\n> > > ScalerCrops control.\n> > >\n> > > CameraData::applyScalerCrop() has been adapted to look at either\n> > > controls::ScalerCrop or controls::rpi::ScalerCrops. The latter takes\n> > > priority over the former. If only controls::ScalerCrop is provided, the\n> > > pipeline handler will apply the same scaler crop to all output streams.\n> > >\n> > > Finally return all crops through the same ScalerCrops control via\n> > > request metadata. The first configure stream's crop rectangle is also\n> > > returned via the ScalerCrop control in the request metadata.\n> > >\n> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> > > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> > > ---\n> > >  .../pipeline/rpi/common/pipeline_base.cpp     | 77 +++++++++++++++----\n> > >  1 file changed, 60 insertions(+), 17 deletions(-)\n> > >\n> > > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> > > index 267e6bd9cd70..917c45b3f052 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> > >         rawStreams_.clear();\n> > >         outStreams_.clear();\n> > > +       unsigned int rawStreamIndex = 0;\n> > > +       unsigned int outStreamIndex = 0;\n> > >\n> > > -       for (const auto &[index, cfg] : utils::enumerate(config_)) {\n> > > +       for (auto &cfg : config_) {\n> > >                 if (PipelineHandlerBase::isRaw(cfg.pixelFormat))\n> > > -                       rawStreams_.emplace_back(index, &cfg);\n> > > +                       rawStreams_.emplace_back(rawStreamIndex++, &cfg);\n> > >                 else\n> > > -                       outStreams_.emplace_back(index, &cfg);\n> > > +                       outStreams_.emplace_back(outStreamIndex++, &cfg);\n> > >         }\n> > >\n> > >         /* Sort the streams so the highest resolution is first. */\n> > > @@ -565,10 +567,24 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)\n> > >         const auto cropParamsIt = data->cropParams_.find(0);\n> > >         if (cropParamsIt != data->cropParams_.end()) {\n> > >                 const CameraData::CropParams &cropParams = cropParamsIt->second;\n> > > -               /* Add the ScalerCrop control limits based on the current mode. */\n> > > +               /*\n> > > +                * Add the ScalerCrop control limits based on the current mode and\n> > > +                * the first configured stream.\n> > > +                */\n> > >                 Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize));\n> > >                 ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,\n> > >                                                              data->scaleIspCrop(cropParams.ispCrop));\n> > > +               if (data->cropParams_.size() == 2) {\n> > > +                       /*\n> > > +                        * The control map for rpi::ScalerCrops has the min value\n> > > +                        * as the default crop for stream 0, max value as the default\n> > > +                        * value for stream 1.\n> > > +                        */\n> > > +                       ctrlMap[&controls::rpi::ScalerCrops] =\n> > > +                               ControlInfo(data->scaleIspCrop(data->cropParams_.at(0).ispCrop),\n> > > +                                           data->scaleIspCrop(data->cropParams_.at(1).ispCrop),\n> > > +                                           ctrlMap[&controls::ScalerCrop].def());\n> > > +               }\n> > >         }\n> > >\n> > >         data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());\n> > > @@ -1295,11 +1311,30 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const\n> > >\n> > >  void CameraData::applyScalerCrop(const ControlList &controls)\n> > >  {\n> > > -       const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);\n> > > -       const auto cropParamsIt = cropParams_.find(0);\n> > > -       if (scalerCrop && cropParamsIt != cropParams_.end()) {\n> > > -               Rectangle nativeCrop = *scalerCrop;\n> > > -               CropParams &cropParams = cropParamsIt->second;\n> > > +       const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);\n> > > +       const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);\n> > > +       std::vector<Rectangle> scalerCrops;\n> > > +\n> > > +       /*\n> > > +        * First thing to do is create a vector of crops to apply to each ISP output\n> > > +        * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if\n> > > +        * present.\n> > > +        *\n> > > +        * If controls::rpi::ScalerCrops is preset, apply the given crops to the\n> > > +        * ISP output streams, indexed by the same order in which they had been\n> > > +        * configured. This is not the same as the ISP output index. Otherwise\n> > > +        * if controls::ScalerCrop is present, apply the same crop to all ISP\n> > > +        * output streams.\n> > > +        */\n> > > +       for (unsigned int i = 0; i < cropParams_.size(); i++) {\n> > > +               if (scalerCropRPi && i < scalerCropRPi->size())\n> > > +                       scalerCrops.push_back(scalerCropRPi->data()[i]);\n> > > +               else if (scalerCropCore)\n> > > +                       scalerCrops.push_back(*scalerCropCore);\n> > > +       }\n> > > +\n> > > +       for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {\n> > > +               Rectangle nativeCrop = scalerCrop;\n> > >\n> > >                 if (!nativeCrop.width || !nativeCrop.height)\n> > >                         nativeCrop = { 0, 0, 1, 1 };\n> > > @@ -1315,13 +1350,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)\n> > >                  * 2. With the same mid-point, if possible.\n> > >                  * 3. But it can't go outside the sensor area.\n> > >                  */\n> > > -               Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n> > > +               Size minSize = cropParams_.at(i).ispMinCropSize.expandedToAspectRatio(nativeCrop.size());\n> > >                 Size size = ispCrop.size().expandedTo(minSize);\n> > >                 ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));\n> > >\n> > > -               if (ispCrop != cropParams.ispCrop) {\n> > > -                       cropParams.ispCrop = ispCrop;\n> > > -                       platformSetIspCrop(cropParams.ispIndex, ispCrop);\n> > > +               if (ispCrop != cropParams_.at(i).ispCrop) {\n> > > +                       cropParams_.at(i).ispCrop = ispCrop;\n> > > +                       platformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop);\n> > >                 }\n> > >         }\n> > >  }\n> > > @@ -1478,10 +1513,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request\n> > >         request->metadata().set(controls::SensorTimestamp,\n> > >                                 bufferControls.get(controls::SensorTimestamp).value_or(0));\n> > >\n> > > -       const auto cropParamsIt = cropParams_.find(0);\n> > > -       if (cropParamsIt != cropParams_.end())\n> > > -               request->metadata().set(controls::ScalerCrop,\n> > > -                                       scaleIspCrop(cropParamsIt->second.ispCrop));\n> > > +       if (cropParams_.size()) {\n> > > +               std::vector<Rectangle> crops;\n> > > +\n> > > +               for (auto const &[k, v] : cropParams_)\n> > > +                       crops.push_back(scaleIspCrop(v.ispCrop));\n> > > +\n> > > +               request->metadata().set(controls::ScalerCrop, crops[0]);\n> > > +               if (crops.size() > 1) {\n> > > +                       request->metadata().set(controls::rpi::ScalerCrops,\n> > > +                                               Span<const Rectangle>(crops.data(), crops.size()));\n> > > +               }\n> > > +       }\n> > >  }\n> > >\n> > >  } /* namespace libcamera */\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","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 E9D03BDB1C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Nov 2024 15:29:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A0070653DE;\n\tMon,  4 Nov 2024 16:29:18 +0100 (CET)","from mail-yw1-x112e.google.com (mail-yw1-x112e.google.com\n\t[IPv6:2607:f8b0:4864:20::112e])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9642165399\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Nov 2024 16:29:16 +0100 (CET)","by mail-yw1-x112e.google.com with SMTP id\n\t00721157ae682-6e381b1bc1dso3689907b3.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 04 Nov 2024 07:29:16 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"ZGYe6mKi\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1730734155; x=1731338955;\n\tdarn=lists.libcamera.org; \n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc:subject:date:message-id:reply-to;\n\tbh=cxs7Y5VZcIrtYpaJ/E5nEGNXSckKwbEwa5MHtEfX/qc=;\n\tb=ZGYe6mKi/WWiM6zWv7Z72KA6pcfjJ2pfaZUFzV50wphz/HC3M35ewB8t5AsbllRnIl\n\tN2K3wjNqzVv66Ki10i9Ze5ppzCJcBpM3mRoOBWAb7+8fJJPufbFT42oA/TPBf/CSxASU\n\tL3IIhRiK5k0adN9naxwOLJaxIC3KVeTr8JT4LNC3Fe1Ba3ZN6mv5pgK8umflJZTKgeNg\n\t0z0FM10oUiYnA5X5Z2iai2tc7lvU9jFKW8WMvWmqgCFanuq2FYUZhns5/lH5dX2ljYgL\n\t7GRsPKd53SjlmwOKO7j7Vv3oOc5jymbxF6DJu5vuuga4CqB4sWE1cUnQ/fqYk260Aypr\n\tYTQA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1730734155; x=1731338955;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-message-state:from:to:cc:subject:date:message-id\n\t:reply-to;\n\tbh=cxs7Y5VZcIrtYpaJ/E5nEGNXSckKwbEwa5MHtEfX/qc=;\n\tb=awWyU68NZEr+/dZx9eagRIOqjxuhheDi5/A+gOO9IDUGQhavJWBatd4MX2vCvVkvN5\n\tAIWpseqmtak8Cn33+G7NuyjCrmsf9w5ka/+HySCVbLj34lvlsojcNrvDlqpA1OZ7WrtX\n\tmJYun0elMI5aUZCvXy+slHmAP8uaLRWJPi8iQncKk+0H8y0y5QuPmhbU55SaNgSDfkEF\n\tYPzl8Wfj+32QIQob7ntelTD9lv5aJXtoNqbuTbn5Z4p6GGV5rVfTf5nuxlBzp3iUIusD\n\tmMnda2EFBXr4ppWyUeDXTDBDFb0MA2JnPtDkMHc92QrJm8g+8a6AFGcGfFeguPP0+q2S\n\teCjQ==","X-Gm-Message-State":"AOJu0YxRsHkM4xBMOiIOZfKYGikY9iFLgZGK66O0zTSrmXl3BrE429Kl\n\tMCQ3nY8BQSQyVAUhL4j7rhLuz0QhCrNxpqWillqSJwxOFZDmdJz0M2sneu+yjbcUZekHc8etX52\n\tN6JFsbaaOJAhLiOYij2a75ayTEL8F88XBc7FBOPhrZ/Zjb6Ec","X-Google-Smtp-Source":"AGHT+IFiGBwZpHbcd+9eoXfqaxKtqgsJlanfsKRLKgZTFEf0Gee8b/zank7LKodxqHhzIPNLZjnTfcZDc65gPOaOjcE=","X-Received":"by 2002:a05:690c:91:b0:6d3:c7d:5f93 with SMTP id\n\t00721157ae682-6e9d8b84faamr124918407b3.10.1730734155224;\n\tMon, 04 Nov 2024 07:29:15 -0800 (PST)","MIME-Version":"1.0","References":"<20241031094957.18471-1-naush@raspberrypi.com>\n\t<20241031094957.18471-8-naush@raspberrypi.com>\n\t<CAEmqJPrgtaw22U7yTEs-SjwbJ4RKFzg+jGfSvnbt38u4DhhLKw@mail.gmail.com>\n\t<20241104152500.GC2725@pendragon.ideasonboard.com>","In-Reply-To":"<20241104152500.GC2725@pendragon.ideasonboard.com>","From":"Naushir Patuck <naush@raspberrypi.com>","Date":"Mon, 4 Nov 2024 15:28:39 +0000","Message-ID":"<CAEmqJPro4d8yvjSJ1dwm3wsCLDn5pcW+XegGRpMMRCMobfVm7g@mail.gmail.com>","Subject":"Re: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, \n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","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>"}}]