From patchwork Mon Sep 30 14:14:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 21432 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 52087C3257 for ; Mon, 30 Sep 2024 14:14:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C09F263523; Mon, 30 Sep 2024 16:14:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="dnYd0snh"; dkim-atps=neutral Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6F9136351A for ; Mon, 30 Sep 2024 16:14:04 +0200 (CEST) Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-37cdc7bd941so145878f8f.0 for ; Mon, 30 Sep 2024 07:14:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1727705644; x=1728310444; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Td/TBRZGbHBx4CYqK/k1N9Zms1qp/+XMuth4qZeDX9k=; b=dnYd0snhGb3e/lnlkCPEDvS31/B3H552CCgJuoQAUXuV3abMQE1f1Qr1NZhSg8rKy+ NbyPwSWrL4DvijqbC/aFMWt3PsjFLJ0J71MYRjXw2EozwF+54ze5gJeKtmIvK40JCQgq CqQGSr/Nls675ePPCk3FNq/li6Eh7r/HVRlK0zjtNWkxsMNcrKjeSw8EA87JBjLoKXsK 5JQyfeAg37G+e4q3EUH8A09Y9qyh1eY5XPrkIvXd7z4nP/lhP5P8RwQhCf/TrJrbIVbM SV3e+Sz+Ak5TWViNpr4sKODnvMFiCT+HQVIhUE7QcNALcr3kZwiFDSWpaCZtTpm/Z0uY HZ7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727705644; x=1728310444; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Td/TBRZGbHBx4CYqK/k1N9Zms1qp/+XMuth4qZeDX9k=; b=SXFWcQyEOtRiHWoAgQBlsfxsukBncQLR15aa+Pgrov/3XZhWgRAmHg71zRr/OZBXaq evfaEjATt0trw7NKXqOzUYrCOQi0poGOM0GpEC7mdveGnQbdh5Tgmc9TEKJTzJVg0gnQ WlGUQEdBF9eTc0BndGwmtB9Fj6SiI0mVqR4rwji/xAN5fVWeTrFOe2NFtfpe+KpGUR0P mnsyBm6VxUvuZK1kA6Czw4qRxRQTRK2uRNyh3Bn/0WxGbXUTB71Cdd20KzfuyUXmIK8B f3LcisjsQxb7MCMCUgxEPE5t7Ti6G2YOlOtcmEHJUXtamSn07MN5eR5lVwtFQy9Z6Ymu Bsww== X-Gm-Message-State: AOJu0YyCZLxb9LXX5K37g+7CJKiJzCg1ZNeCjaDgSVKzZMqrL2gRMYM0 tGj4AKs5jxSC5GGWZn5FcO7oaoo0fs7/u8TjFHbSZxTGn2wSw1B4LKfo8b+l0Katb8l8r9H+s+f A X-Google-Smtp-Source: AGHT+IHQepeeSWAHIz4G/igmDNRsPs4UrioTef6jHOdIEA07eyPm1uy1VTif4T5vEVZeNCYmrNbdAA== X-Received: by 2002:a5d:6c6e:0:b0:374:cd01:8b72 with SMTP id ffacd0b85a97d-37cde446cffmr2482820f8f.9.1727705643603; Mon, 30 Sep 2024 07:14:03 -0700 (PDT) Received: from naush-laptop.pitowers.org ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-37cd56e65c3sm9142524f8f.60.2024.09.30.07.14.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Sep 2024 07:14:02 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [PATCH v2 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops Date: Mon, 30 Sep 2024 15:14:15 +0100 Message-Id: <20240930141415.8857-8-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240930141415.8857-1-naush@raspberrypi.com> References: <20240930141415.8857-1-naush@raspberrypi.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Handle multiple scaler crops being set through the rpi::ScalerCrops control. We now populate the cropParams_ map in the loop where we handle the output stream configuration items. The key of this map is the index of the stream configuration structure set by the application. This will also be the same index used to specify the crop rectangles through the ScalerCrops control. CameraData::applyScalerCrop() has been adapted to look at either controls::ScalerCrop or controls::rpi::ScalerCrops. The former takes priority over the latter, and if present, will apply the same scaler crop to all output streams. Finally return all crops through the same ScalerCrops control via request metadata. The first configure stream's crop rectangle is also returned via the ScalerCrop control in the request metadata. Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi --- .../pipeline/rpi/common/pipeline_base.cpp | 76 ++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 267e6bd9cd70..9393e79ae4c7 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -181,12 +181,14 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() rawStreams_.clear(); outStreams_.clear(); + unsigned int rawStreamIndex = 0; + unsigned int outStreamIndex = 0; - for (const auto &[index, cfg] : utils::enumerate(config_)) { + for (auto &cfg : config_) { if (PipelineHandlerBase::isRaw(cfg.pixelFormat)) - rawStreams_.emplace_back(index, &cfg); + rawStreams_.emplace_back(rawStreamIndex++, &cfg); else - outStreams_.emplace_back(index, &cfg); + outStreams_.emplace_back(outStreamIndex++, &cfg); } /* Sort the streams so the highest resolution is first. */ @@ -565,10 +567,24 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config) const auto cropParamsIt = data->cropParams_.find(0); if (cropParamsIt != data->cropParams_.end()) { const CameraData::CropParams &cropParams = cropParamsIt->second; - /* Add the ScalerCrop control limits based on the current mode. */ + /* + * Add the ScalerCrop control limits based on the current mode and + * the first configured stream. + */ Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(cropParams.ispMinCropSize)); ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop, data->scaleIspCrop(cropParams.ispCrop)); + if (data->cropParams_.size() == 2) { + /* + * The control map for rpi::ScalerCrops has the min value + * as the default crop for stream 0, max value as the default + * value for stream 1. + */ + ctrlMap[&controls::rpi::ScalerCrops] = + ControlInfo(data->scaleIspCrop(data->cropParams_[0].ispCrop), + data->scaleIspCrop(data->cropParams_[1].ispCrop), + ctrlMap[&controls::ScalerCrop].def()); + } } data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap()); @@ -1295,11 +1311,29 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const void CameraData::applyScalerCrop(const ControlList &controls) { - const auto &scalerCrop = controls.get(controls::ScalerCrop); - const auto cropParamsIt = cropParams_.find(0); - if (scalerCrop && cropParamsIt != cropParams_.end()) { - Rectangle nativeCrop = *scalerCrop; - CropParams &cropParams = cropParamsIt->second; + const auto &scalerCropRPi = controls.get>(controls::rpi::ScalerCrops); + const auto &scalerCropCore = controls.get(controls::ScalerCrop); + std::vector scalerCrops; + + /* + * First thing to do is create a vector of crops to apply to each ISP output + * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if + * present. + * + * If controls::ScalerCrop is present, apply the same crop to all ISP output + * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops + * to the ISP output streams, indexed by the same order in which they had + * been configured. This is not the same as the ISP output index. + */ + for (unsigned int i = 0; i < cropParams_.size(); i++) { + if (scalerCropRPi && i < scalerCropRPi->size()) + scalerCrops.push_back(scalerCropRPi->data()[i]); + else if (scalerCropCore) + scalerCrops.push_back(*scalerCropCore); + } + + for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) { + Rectangle nativeCrop = scalerCrop; if (!nativeCrop.width || !nativeCrop.height) nativeCrop = { 0, 0, 1, 1 }; @@ -1315,13 +1349,13 @@ void CameraData::applyScalerCrop(const ControlList &controls) * 2. With the same mid-point, if possible. * 3. But it can't go outside the sensor area. */ - Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size()); + Size minSize = cropParams_[i].ispMinCropSize.expandedToAspectRatio(nativeCrop.size()); Size size = ispCrop.size().expandedTo(minSize); ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize)); - if (ispCrop != cropParams.ispCrop) { - cropParams.ispCrop = ispCrop; - platformSetIspCrop(cropParams.ispIndex, ispCrop); + if (ispCrop != cropParams_[i].ispCrop) { + cropParams_[i].ispCrop = ispCrop; + platformSetIspCrop(cropParams_[i].ispIndex, ispCrop); } } } @@ -1478,10 +1512,18 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request request->metadata().set(controls::SensorTimestamp, bufferControls.get(controls::SensorTimestamp).value_or(0)); - const auto cropParamsIt = cropParams_.find(0); - if (cropParamsIt != cropParams_.end()) - request->metadata().set(controls::ScalerCrop, - scaleIspCrop(cropParamsIt->second.ispCrop)); + if (cropParams_.size()) { + std::vector crops; + + for (auto const &[k, v] : cropParams_) + crops.push_back(scaleIspCrop(v.ispCrop)); + + request->metadata().set(controls::ScalerCrop, crops[0]); + if (crops.size() > 1) { + request->metadata().set(controls::rpi::ScalerCrops, + Span(crops.data(), crops.size())); + } + } } } /* namespace libcamera */