libcamera: rpi: Make the controller min frame duration configurable

The controller min frame duration is used to rate limit how often we
run IPAs. Historically this has been set to 33333us, meaning that the
algorithms effectively skip frames when the camera is running faster
than 30fps.

This patch adds a small amount of plumbing that allows this value to
be set in the Raspberry Pi configuration file. Some applications or
platforms (such as Pi 5) are easily capable of running these more
often, should there be a need to do so.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
David Plowman
2026-01-07 18:14:26 +00:00
committed by Kieran Bingham
parent 71c5c08fcf
commit cc74afcdbd
7 changed files with 47 additions and 15 deletions

View File

@@ -18,6 +18,7 @@ struct SensorConfig {
struct InitParams {
bool lensPresent;
libcamera.IPACameraSensorInfo sensorInfo;
float controllerMinFrameDurationUs;
/* PISP specific */
libcamera.SharedFD fe;
libcamera.SharedFD be;

View File

@@ -46,14 +46,6 @@ constexpr Duration defaultExposureTime = 20.0ms;
constexpr Duration defaultMinFrameDuration = 1.0s / 30.0;
constexpr Duration defaultMaxFrameDuration = 250.0s;
/*
* Determine the minimum allowable inter-frame duration to run the controller
* algorithms. If the pipeline handler provider frames at a rate higher than this,
* we rate-limit the controller Prepare() and Process() calls to lower than or
* equal to this rate.
*/
constexpr Duration controllerMinFrameDuration = 1.0s / 30.0;
/* List of controls handled by the Raspberry Pi IPA */
const ControlInfoMap::Map ipaControls{
/* \todo Move this to the Camera class */
@@ -186,6 +178,14 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini
result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
/*
* This determines the minimum allowable inter-frame duration to run the
* controller algorithms. If the pipeline handler provider frames at a
* rate higher than this, we rate-limit the controller Prepare() and
* Process() calls to lower than or equal to this rate.
*/
controllerMinFrameDuration_ = params.controllerMinFrameDurationUs * 1us;
return platformInit(params, result);
}
@@ -467,7 +467,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)
/* Allow a 10% margin on the comparison below. */
Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;
if (lastRunTimestamp_ && frameCount_ > invalidCount_ &&
delta < controllerMinFrameDuration * 0.9 && !hdrChange) {
delta < controllerMinFrameDuration_ * 0.9 && !hdrChange) {
/*
* Ensure we merge the previous frame's metadata with the current
* frame. This will not overwrite exposure/gain values for the

View File

@@ -142,6 +142,8 @@ private:
} flickerState_;
bool awbEnabled_;
utils::Duration controllerMinFrameDuration_;
};
} /* namespace ipa::RPi */

View File

@@ -33,6 +33,12 @@ LOG_DEFINE_CATEGORY(RPI)
using StreamFlag = RPi::Stream::StreamFlag;
/*
* The IPA's algorithms will not be called more often than this many
* microseconds. The default corresponds to 30fps.
*/
constexpr float defaultControllerMinimumFrameDurationUs = 1000000.0 / 30.0;
namespace {
constexpr unsigned int defaultRawBitDepth = 12;
@@ -800,6 +806,12 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
if (!data->sensor_)
return -EINVAL;
ret = data->loadPipelineConfiguration();
if (ret) {
LOG(RPI, Error) << "Unable to load pipeline configuration";
return ret;
}
/* Populate the map of sensor supported formats and sizes. */
for (const auto mbusCode : data->sensor_->mbusCodes())
data->sensorFormats_.emplace(mbusCode,
@@ -859,12 +871,6 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
if (ret)
return ret;
ret = data->loadPipelineConfiguration();
if (ret) {
LOG(RPI, Error) << "Unable to load pipeline configuration";
return ret;
}
/* Setup the general IPA signal handlers. */
data->frontendDevice()->dequeueTimeout.connect(data, &RPi::CameraData::cameraTimeout);
data->frontendDevice()->frameStart.connect(data, &RPi::CameraData::frameStarted);
@@ -1095,6 +1101,7 @@ int CameraData::loadPipelineConfiguration()
{
config_ = {
.cameraTimeoutValue = 0,
.controllerMinFrameDurationUs = defaultControllerMinimumFrameDurationUs,
};
/* Initial configuration of the platform, in case no config file is present */
@@ -1144,6 +1151,9 @@ int CameraData::loadPipelineConfiguration()
frontendDevice()->setDequeueTimeout(config_.cameraTimeoutValue * 1ms);
}
config_.controllerMinFrameDurationUs =
phConfig["controller_min_frame_duration_us"].get<double>(config_.controllerMinFrameDurationUs);
return platformPipelineConfigure(root);
}
@@ -1172,6 +1182,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)
}
params.lensPresent = !!sensor_->focusLens();
params.controllerMinFrameDurationUs = config_.controllerMinFrameDurationUs;
ret = platformInitIpa(params);
if (ret)
return ret;

View File

@@ -169,6 +169,11 @@ public:
* on frame durations.
*/
unsigned int cameraTimeoutValue;
/*
* The minimum frame duration between the IPA's calls to the
* algorithms themselves (in microseconds).
*/
float controllerMinFrameDurationUs;
};
Config config_;

View File

@@ -36,5 +36,11 @@
# framebuffers required for its operation.
#
# "disable_hdr": false,
# Limits the rate at which IPAs are called. The algorithms will
# be skipped until this many microseconds have elapsed since
# the last call. The default value represents a 30fps limit.
#
# "controller_min_frame_duration_us": 33333.333,
}
}

View File

@@ -37,5 +37,11 @@
# timeout value.
#
# "camera_timeout_value_ms": 0,
# Limits the rate at which IPAs are called. The algorithms will
# be skipped until this many microseconds have elapsed since
# the last call. The default value represents a 30fps limit.
#
# "controller_min_frame_duration_us": 33333.333,
}
}