[4/6] ipa: rpi: Add base classes and plumbing for sync algorithm
diff mbox series

Message ID 20241004115558.9166-5-david.plowman@raspberrypi.com
State New
Headers show
Series
  • Raspberry Pi software camera sync algorithm
Related show

Commit Message

David Plowman Oct. 4, 2024, 11:55 a.m. UTC
From: Naushir Patuck <naush@raspberrypi.com>

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 <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
---
 src/ipa/rpi/common/ipa_base.cpp         | 66 +++++++++++++++++++++++--
 src/ipa/rpi/common/ipa_base.h           |  4 +-
 src/ipa/rpi/controller/sync_algorithm.h | 30 +++++++++++
 src/ipa/rpi/controller/sync_status.h    | 27 ++++++++++
 4 files changed, 122 insertions(+), 5 deletions(-)
 create mode 100644 src/ipa/rpi/controller/sync_algorithm.h
 create mode 100644 src/ipa/rpi/controller/sync_status.h

Patch
diff mbox series

diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
index ee3848b5..ac228791 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 {
 
@@ -74,6 +76,7 @@  const ControlInfoMap::Map ipaControls{
 	{ &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) },
 	{ &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) },
 	{ &controls::rpi::StatsOutputEnable, ControlInfo(false, true, false) },
+	{ &controls::rpi::SyncMode, ControlInfo(controls::rpi::SyncModeValues) },
 };
 
 /* IPA controls handled conditionally, if the sensor is not mono */
@@ -387,6 +390,7 @@  void IpaBase::prepareIsp(const PrepareParams &params)
 
 	rpiMetadata.clear();
 	fillDeviceStatus(params.sensorControls, ipaContext);
+	fillSyncParams(params, ipaContext);
 
 	if (params.buffers.embedded) {
 		/*
@@ -485,10 +489,23 @@  void IpaBase::processStats(const ProcessParams &params)
 		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::SyncWait, !syncStatus.ready);
+			if (syncStatus.lagKnown)
+				libcameraMetadata_.set(controls::rpi::SyncLag, syncStatus.lag);
+		}
+
 		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();
 		}
@@ -721,6 +738,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();
@@ -1247,6 +1265,24 @@  void IpaBase::applyControls(const ControlList &controls)
 			statsMetadataOutput_ = ctrl.second.get<bool>();
 			break;
 
+		case controls::rpi::SYNC_MODE: {
+			SyncAlgorithm *sync = dynamic_cast<SyncAlgorithm *>(controller_.getAlgorithm("sync"));
+
+			if (sync) {
+				int mode = ctrl.second.get<int32_t>();
+				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;
+		}
+
 		default:
 			LOG(IPARPI, Warning)
 				<< "Ctrl " << controls::controls.at(ctrl.first)->name()
@@ -1283,6 +1319,19 @@  void IpaBase::fillDeviceStatus(const ControlList &sensorControls, unsigned int i
 	rpiMetadata_[ipaContext].set("device.status", deviceStatus);
 }
 
+void IpaBase::fillSyncParams(const PrepareParams &params, unsigned int ipaContext)
+{
+	RPiController::SyncAlgorithm *sync = dynamic_cast<RPiController::SyncAlgorithm *>(
+		controller_.getAlgorithm("sync"));
+	if (!sync)
+		return;
+
+	SyncParams syncParams;
+	syncParams.wallClock = *params.sensorControls.get(controls::rpi::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];
@@ -1451,14 +1500,22 @@  void IpaBase::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDu
 	 * value possible.
 	 */
 	Duration maxShutter = Duration::max();
-	helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_);
+	auto [vblank, hblank] = helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_);
 
 	RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
 		controller_.getAlgorithm("agc"));
 	agc->setMaxShutter(maxShutter);
+
+	RPiController::SyncAlgorithm *sync = dynamic_cast<RPiController::SyncAlgorithm *>(
+		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);
@@ -1473,7 +1530,8 @@  void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
 
 	/* getBlanking might clip exposure time to the fps limits. */
 	Duration exposure = agcStatus->shutterTime;
-	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 &params, 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<unsigned int, MappedFrameBuffer> 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..298b5951
--- /dev/null
+++ b/src/ipa/rpi/controller/sync_algorithm.h
@@ -0,0 +1,30 @@ 
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2024, Raspberry Pi Ltd
+ *
+ * sync_algorithm.h - Camera sync algorithm interface
+ */
+#pragma once
+
+#include <libcamera/base/utils.h>
+
+#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 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..289f0182
--- /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 <libcamera/base/utils.h>
+
+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;
+	/* Lag between camera frame and the "ready time" */
+	int64_t lag;
+	/* Whether lag is known (client has to wait for a server message) */
+	bool lagKnown;
+};