From patchwork Thu Oct 31 09:49:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 21788 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 72B8AC3292 for ; Thu, 31 Oct 2024 09:52:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 981E6653AB; Thu, 31 Oct 2024 10:52:57 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="O8hLKb+5"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6759F653A9 for ; Thu, 31 Oct 2024 10:52:47 +0100 (CET) Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-4314e64eba4so422625e9.2 for ; Thu, 31 Oct 2024 02:52:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1730368367; x=1730973167; 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=X/8CZh5p/MrzA7UDIgjfjtdpHXu5yfJKtsg4HkJ59cg=; b=O8hLKb+5Y7EerScYKo5vM51IlMJaKU0Mj770Oks+xzoJsXOc1A2CbbwTtcCXoVSdJ/ kkh9wDhGLkYq+sUeCsD4/IveBP8iiHsn63eqfg0aWD8GFUQpEL5Z+TKsT25GQ7etoAoR lDDfWkaTqPu/5Bg5a1UFRn0XNLDyImfxJU2XVPAcGL361vNPUXVy7+c+ZLav28HANzis Ix5H89pePuKJ5jCmip8at/CoU1VJCcxGt1A60uVhTab2d8MB3oi7GPZs1bXjE5wK2YbE NrMGn5CpNgnh04y07/Hiz3dnQYo5iAfwTkrilMTJ3Iy+jM4aSvSf+b4v8yWWeC12vQrV g5ow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1730368367; x=1730973167; 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=X/8CZh5p/MrzA7UDIgjfjtdpHXu5yfJKtsg4HkJ59cg=; b=Il7wJO/iwOeghDPk7fpLOABBnuKl3begi6cl9kOA+pBLqTS/NYDgK3NmnAOiNB1djU Dq80ZhrM3nMndLBiCh9F67CfpVrGWs8OE1n30bmb7L17kMUErnL2StAfvKhJTepiz2yl YOQWtfgDB7AO6kA/XlZpXqOoHVSLnpt04oh02iJ/+EEQInwGy19a9jbxqy/KjoDdUCOv sKXcPnn7hpb+zJaBVWSzgqVFIj9sHaHasFnS5mbSaulCwPlx14s+a0cSC2Gy/2W+tAW7 HFwOmQdG7SQyb9X0U5jT8k4kPo4DoJX64xY/OH1XnYRTHm3aJy4pzk0I9/zIGci9e+VM uBdQ== X-Gm-Message-State: AOJu0Yz3dNWWL1H5EknGA5BfndOpktxgtMjgr8lQhQlII/HmtO4eGSpx mqAL/Zi71+G7fcLmRTn8EWjgXio/hHQ/WKbu9w4RjYRan6/80cJE9DugXKKtqaEBjkbNfY0fPlO u X-Google-Smtp-Source: AGHT+IHN9Tr5riV7Xf/eKQ+hy/AFqibJtE81+OejXMClrP3o/L969b/O1/uXi2K08gokgEqvFGI5dQ== X-Received: by 2002:a05:600c:3b9e:b0:42c:b55f:f4f with SMTP id 5b1f17b1804b1-4319ad4094emr72052595e9.6.1730368366594; Thu, 31 Oct 2024 02:52:46 -0700 (PDT) Received: from naush-laptop.pitowers.org ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4327d6983c2sm19789835e9.42.2024.10.31.02.52.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 31 Oct 2024 02:52:46 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck , Jacopo Mondi Subject: [PATCH v4 7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops Date: Thu, 31 Oct 2024 09:49:57 +0000 Message-Id: <20241031094957.18471-8-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241031094957.18471-1-naush@raspberrypi.com> References: <20241031094957.18471-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 latter takes priority over the former. If only controls::ScalerCrop is provided, the pipeline handler 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 Reviewed-by: Laurent Pinchart --- .../pipeline/rpi/common/pipeline_base.cpp | 77 +++++++++++++++---- 1 file changed, 60 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..917c45b3f052 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_.at(0).ispCrop), + data->scaleIspCrop(data->cropParams_.at(1).ispCrop), + ctrlMap[&controls::ScalerCrop].def()); + } } data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap()); @@ -1295,11 +1311,30 @@ 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::rpi::ScalerCrops is preset, 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. Otherwise + * if controls::ScalerCrop is present, apply the same crop to all ISP + * output streams. + */ + 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 +1350,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_.at(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_.at(i).ispCrop) { + cropParams_.at(i).ispCrop = ispCrop; + platformSetIspCrop(cropParams_.at(i).ispIndex, ispCrop); } } } @@ -1478,10 +1513,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 */