From patchwork Thu Jan 9 14:32:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 22505 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 E33E6C32EA for ; Thu, 9 Jan 2025 14:32:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6B11968503; Thu, 9 Jan 2025 15:32:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="TTT8QR2K"; dkim-atps=neutral Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0F01F68503 for ; Thu, 9 Jan 2025 15:32:22 +0100 (CET) Received: by mail-wr1-x433.google.com with SMTP id ffacd0b85a97d-385e87b25f0so1402834f8f.0 for ; Thu, 09 Jan 2025 06:32:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1736433141; x=1737037941; 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=yKDZcqefLc17FC1rrbX68afdo5XuZS6xj1yrJzIz0E8=; b=TTT8QR2KNPGsiR3lUhfdGNeXtV8TUe+btohZoqywftlnZZGsMy9FpsOFmyDlWD1kNF qFPnwrRP7ELH06fpIJu73/5EROJ/547XVNUUJU5z99R1ZIpE480FJ1yoq5UGJJ6cQ2Dn 10+VFRmJuwruLoLMx6XQm2PCoU56kyfIrIDiQhfF5POzQZ/4T1xoeyjeFfhVer1ZcfjP 7fqTkRVXjO3/Ftie6dVBH8CPHBZbn23n4TlYmQYPsM4Aazo6vVr1KmzAX1/k1DEaJx6m nR1h66SdxjrV3vqrNHikF253pwStHyG04ERyqlyHrh4LBbxyydJgrNK0vmTi9ZqUlQiE laag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736433141; x=1737037941; 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=yKDZcqefLc17FC1rrbX68afdo5XuZS6xj1yrJzIz0E8=; b=vmFcpuxvP/plzEfHDjg0sKzQiFSEaelPGMYu27LVLQFyLxrlRw27Erz+GcGck2h4BX 7y5b1c+7kzKfUQyYmL5KAJ8ooLyKPdclFRpazxhZegD0DzHNEQxmt2WQJzD93KrogFFc jENCslNbdwq/7GRk66dfyKqQQzglJ+FdjckaWhxb7G5I3Kr+ug0OH9pP7NlwJjNh/Y3m bQ7HExFqcGSveVUcpQUP/uCwGW7Csx0qbBy2O8u9slaDwZjOfEznkkOIlvaWbrJGI3AD kjQH64/Hg+iskC14NcQXxLQ87Fk34spl7EyxiX14o+gFfllcwUUfvxxG3/Il9cTfj1ei mG2A== X-Gm-Message-State: AOJu0Yy79YLY+Npp3ufd3H6mTfGlHa6MMXfR97L4t+QeK+yE1itZQCvV Ch1q+o9DfIbzrPqDOAJlXflFrLuvH4JtAeq25BlwP6Hd/k7OZmmbh6MKbyTFfqs7BclNwmKqkEr F X-Gm-Gg: ASbGncs4V4pcXS+tyd7x2Q0znZFH9PxQ8aJ37EKcCbe2+EBfY4Od0lz77cTcP8eUTG0 TGQoDFwhrzjp/cXZt4uqk8nP7QhqqFT2IMGqMkkWS3xrxEjQvtTCW5DIFrDWnxKZ4oesanXdHK7 uLYNO50iMiVa5K38gQxgG4EdjCzmb8iS3qWq2VctSUOZs+raxz2G1znPIgBhNBY7qjTjJ4TeLJv cPRT2CfOnFQ8z6ah5q9QF3y6g2TJ0igo11YzhQQnroVneqDdI9cMmohJ6e9H8fOPfKC76aMaeao 7PcYs8Ey8LSV X-Google-Smtp-Source: AGHT+IHq0xP6QSB6NgJEmFnmTsPGaBBNH1bK4P6K3KOrhv/7jSLPXI3Ao01cKK+ef4v6MdJmF++pJA== X-Received: by 2002:a05:6000:1f8a:b0:385:faec:d945 with SMTP id ffacd0b85a97d-38a8b0d3202mr3391183f8f.9.1736433139842; Thu, 09 Jan 2025 06:32:19 -0800 (PST) Received: from raspberrypi.pitowers.org ([2a00:1098:3142:1f:ffc9:aff6:7f7f:893b]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-38a8e4b80b2sm1952569f8f.80.2025.01.09.06.32.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 09 Jan 2025 06:32:19 -0800 (PST) From: David Plowman To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [PATCH v3 5/7] ipa: rpi: Add base classes and plumbing for sync algorithm Date: Thu, 9 Jan 2025 14:32:09 +0000 Message-Id: <20250109143211.11939-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250109143211.11939-1-david.plowman@raspberrypi.com> References: <20250109143211.11939-1-david.plowman@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" From: Naushir Patuck We add a base class for a "sync algorithm", and define its inputs and outputs in the SyncStatus class. We add the necessary plumbing to the base IPA code so as to arrange for the necessary parameters to be made available to such an algorithm, and also to handle the return values, passing them back as necessary to the pipeline handler. Signed-off-by: Naushir Patuck --- src/ipa/rpi/common/ipa_base.cpp | 78 +++++++++++++++++++++++-- src/ipa/rpi/common/ipa_base.h | 4 +- src/ipa/rpi/controller/sync_algorithm.h | 31 ++++++++++ src/ipa/rpi/controller/sync_status.h | 27 +++++++++ 4 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/ipa/rpi/controller/sync_algorithm.h create mode 100644 src/ipa/rpi/controller/sync_status.h diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index 6ff1e22b..f1e4a161 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -28,6 +28,8 @@ #include "controller/lux_status.h" #include "controller/sharpen_algorithm.h" #include "controller/statistics.h" +#include "controller/sync_algorithm.h" +#include "controller/sync_status.h" namespace libcamera { @@ -72,6 +74,8 @@ const ControlInfoMap::Map ipaControls{ { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, { &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) }, + { &controls::rpi::SyncMode, ControlInfo(controls::rpi::SyncModeValues) }, + { &controls::rpi::SyncFrames, ControlInfo(1, 1000000, 100) }, { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) }, { &controls::rpi::StatsOutputEnable, ControlInfo(false, true, false) }, }; @@ -390,6 +394,7 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) rpiMetadata.clear(); fillDeviceStatus(params.sensorControls, ipaContext); + fillSyncParams(params, ipaContext); if (params.buffers.embedded) { /* @@ -488,10 +493,23 @@ void IpaBase::processStats(const ProcessParams ¶ms) helper_->process(statistics, rpiMetadata); controller_.process(statistics, &rpiMetadata); + /* Send any sync algorithm outputs back to the pipeline handler */ + Duration offset(0s); + struct SyncStatus syncStatus; + if (rpiMetadata.get("sync.status", syncStatus) == 0) { + if (minFrameDuration_ != maxFrameDuration_) + LOG(IPARPI, Error) << "Sync algorithm enabled with variable framerate. " << minFrameDuration_ << " " << maxFrameDuration_; + offset = syncStatus.frameDurationOffset; + + libcameraMetadata_.set(controls::rpi::SyncReady, syncStatus.ready); + if (syncStatus.timerKnown) + libcameraMetadata_.set(controls::rpi::SyncTimer, syncStatus.timerValue); + } + struct AgcStatus agcStatus; if (rpiMetadata.get("agc.status", agcStatus) == 0) { ControlList ctrls(sensorCtrls_); - applyAGC(&agcStatus, ctrls); + applyAGC(&agcStatus, ctrls, offset); setDelayedControls.emit(ctrls, ipaContext); setCameraTimeoutValue(); } @@ -728,6 +746,7 @@ void IpaBase::applyControls(const ControlList &controls) using RPiController::ContrastAlgorithm; using RPiController::DenoiseAlgorithm; using RPiController::HdrAlgorithm; + using RPiController::SyncAlgorithm; /* Clear the return metadata buffer. */ libcameraMetadata_.clear(); @@ -1274,6 +1293,35 @@ void IpaBase::applyControls(const ControlList &controls) statsMetadataOutput_ = ctrl.second.get(); break; + case controls::rpi::SYNC_MODE: { + SyncAlgorithm *sync = dynamic_cast(controller_.getAlgorithm("sync")); + + if (sync) { + int mode = ctrl.second.get(); + SyncAlgorithm::Mode m = SyncAlgorithm::Mode::Off; + if (mode == controls::rpi::SyncModeServer) { + m = SyncAlgorithm::Mode::Server; + LOG(IPARPI, Info) << "Sync mode set to server"; + } else if (mode == controls::rpi::SyncModeClient) { + m = SyncAlgorithm::Mode::Client; + LOG(IPARPI, Info) << "Sync mode set to client"; + } + sync->setMode(m); + } + break; + } + + case controls::rpi::SYNC_FRAMES: { + SyncAlgorithm *sync = dynamic_cast(controller_.getAlgorithm("sync")); + + if (sync) { + int frames = ctrl.second.get(); + if (frames > 0) + sync->setReadyFrame(frames); + } + break; + } + default: LOG(IPARPI, Warning) << "Ctrl " << controls::controls.at(ctrl.first)->name() @@ -1310,6 +1358,19 @@ void IpaBase::fillDeviceStatus(const ControlList &sensorControls, unsigned int i rpiMetadata_[ipaContext].set("device.status", deviceStatus); } +void IpaBase::fillSyncParams(const PrepareParams ¶ms, unsigned int ipaContext) +{ + RPiController::SyncAlgorithm *sync = dynamic_cast( + controller_.getAlgorithm("sync")); + if (!sync) + return; + + SyncParams syncParams; + syncParams.wallClock = *params.sensorControls.get(controls::FrameWallClock); + syncParams.sensorTimestamp = *params.sensorControls.get(controls::SensorTimestamp); + rpiMetadata_[ipaContext].set("sync.params", syncParams); +} + void IpaBase::reportMetadata(unsigned int ipaContext) { RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; @@ -1478,14 +1539,22 @@ void IpaBase::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDu * value possible. */ Duration maxExposureTime = Duration::max(); - helper_->getBlanking(maxExposureTime, minFrameDuration_, maxFrameDuration_); + auto [vblank, hblank] = helper_->getBlanking(maxExposureTime, minFrameDuration_, maxFrameDuration_); RPiController::AgcAlgorithm *agc = dynamic_cast( controller_.getAlgorithm("agc")); agc->setMaxExposureTime(maxExposureTime); + + RPiController::SyncAlgorithm *sync = dynamic_cast( + controller_.getAlgorithm("sync")); + if (sync) { + Duration duration = (mode_.height + vblank) * ((mode_.width + hblank) * 1.0s / mode_.pixelRate); + LOG(IPARPI, Debug) << "setting sync frame duration to " << duration; + sync->setFrameDuration(duration); + } } -void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) +void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls, Duration frameDurationOffset) { const int32_t minGainCode = helper_->gainCode(mode_.minAnalogueGain); const int32_t maxGainCode = helper_->gainCode(mode_.maxAnalogueGain); @@ -1500,7 +1569,8 @@ void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) /* getBlanking might clip exposure time to the fps limits. */ Duration exposure = agcStatus->exposureTime; - auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); + auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_ - frameDurationOffset, + maxFrameDuration_ - frameDurationOffset); int32_t exposureLines = helper_->exposureLines(exposure, helper_->hblankToLineLength(hblank)); diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h index 1a811beb..b53d0bfb 100644 --- a/src/ipa/rpi/common/ipa_base.h +++ b/src/ipa/rpi/common/ipa_base.h @@ -95,9 +95,11 @@ private: void applyControls(const ControlList &controls); virtual void handleControls(const ControlList &controls) = 0; void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext); + void fillSyncParams(const PrepareParams ¶ms, unsigned int ipaContext); void reportMetadata(unsigned int ipaContext); void applyFrameDurations(utils::Duration minFrameDuration, utils::Duration maxFrameDuration); - void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls); + void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls, + utils::Duration frameDurationOffset = utils::Duration(0)); std::map buffers_; diff --git a/src/ipa/rpi/controller/sync_algorithm.h b/src/ipa/rpi/controller/sync_algorithm.h new file mode 100644 index 00000000..c242def6 --- /dev/null +++ b/src/ipa/rpi/controller/sync_algorithm.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2024, Raspberry Pi Ltd + * + * sync_algorithm.h - Camera sync algorithm interface + */ +#pragma once + +#include + +#include "algorithm.h" + +namespace RPiController { + +class SyncAlgorithm : public Algorithm +{ +public: + enum class Mode { + Off, + Server, + Client, + }; + + SyncAlgorithm(Controller *controller) + : Algorithm(controller) {} + virtual void setFrameDuration(libcamera::utils::Duration frameDuration) = 0; + virtual void setReadyFrame(unsigned int frame) = 0; + virtual void setMode(Mode mode) = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/sync_status.h b/src/ipa/rpi/controller/sync_status.h new file mode 100644 index 00000000..10f97502 --- /dev/null +++ b/src/ipa/rpi/controller/sync_status.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2024, Raspberry Pi Ltd + * + * sync_status.h - Sync algorithm params and status structures + */ +#pragma once + +#include + +struct SyncParams { + /* Wall clock time for this frame */ + uint64_t wallClock; + /* Kernel timestamp for this frame */ + uint64_t sensorTimestamp; +}; + +struct SyncStatus { + /* Frame length correction to apply */ + libcamera::utils::Duration frameDurationOffset; + /* Whether the "ready time" has been reached */ + bool ready; + /* Time until the "ready time" */ + int64_t timerValue; + /* Whether timerValue is known (client has to wait for a server message) */ + bool timerKnown; +};