Show a patch.

GET /api/1.1/patches/25678/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 25678,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/25678/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/25678/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260107093821.14600-1-david.plowman@raspberrypi.com>",
    "date": "2026-01-07T09:37:36",
    "name": "libcamera: rpi: Make the controller min frame duration configurable",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "dc7a45452d0d4211cd41e1f11edc117f43489e32",
    "submitter": {
        "id": 42,
        "url": "https://patchwork.libcamera.org/api/1.1/people/42/?format=api",
        "name": "David Plowman",
        "email": "david.plowman@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/25678/mbox/",
    "series": [
        {
            "id": 5690,
            "url": "https://patchwork.libcamera.org/api/1.1/series/5690/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5690",
            "date": "2026-01-07T09:37:36",
            "name": "libcamera: rpi: Make the controller min frame duration configurable",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5690/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/25678/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/25678/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 34350BE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 Jan 2026 09:38:29 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4202C61FC1;\n\tWed,  7 Jan 2026 10:38:28 +0100 (CET)",
            "from mail-wm1-x333.google.com (mail-wm1-x333.google.com\n\t[IPv6:2a00:1450:4864:20::333])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C72FA61F61\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 Jan 2026 10:38:25 +0100 (CET)",
            "by mail-wm1-x333.google.com with SMTP id\n\t5b1f17b1804b1-477a219dbcaso15250235e9.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 07 Jan 2026 01:38:25 -0800 (PST)",
            "from davidp-pi5.pitowers.org\n\t([2a00:1098:3142:1f:88ea:c658:5b20:5e46])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-47d8717d78fsm7445015e9.9.2026.01.07.01.38.23\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 07 Jan 2026 01:38:24 -0800 (PST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"VeAtPhGG\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1767778705; x=1768383505;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:from:to:cc:subject:date:message-id:reply-to;\n\tbh=5ynmxmxJ7YUm/yGx64w2BfC9qQ0Z4qqdhAUa19x8DEU=;\n\tb=VeAtPhGGjDn07BMIu5S/CdaCYlCm0PW/zTp8OZsmqTq0SqrlP4VTAk5eADGY5AzmVE\n\tzui7S+4qdFuuS8FZKML69yJ2ytcjdRzj7h6l8m3rWek9ORS/s4otK1U7Eax1S21KH4vE\n\tKWVbMEHlJoyGBVyNKH41+JqqQ/o0cs5HSBRCFdrEet5zuUA/vKi3OO14vA+7/kuF5aNA\n\tDwpVVprtoWVJgoWMGAYqWml5yYe5rXKYBGXbLtJ746qEScU8bLdY5LQHYNJM3a4rmC37\n\tWVLRmAc6+DdvDW4Vo/LjV5bvzJfvK9CwHEt/0ce2ZLY97+nEhNNXaQhXENUrw3yzxlVK\n\ty7zg==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1767778705; x=1768383505;\n\th=content-transfer-encoding:mime-version:message-id:date:subject:cc\n\t:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=5ynmxmxJ7YUm/yGx64w2BfC9qQ0Z4qqdhAUa19x8DEU=;\n\tb=kJBoXO7D9xTpZmZIJ7HkkdUAL3Ivsh5xiFZYvl/rNJl5MuMOphdvUyyvYWxPdodWXy\n\t5tg89217HyfC5A1/19yphUC6ILPjnN8nPJ0KyqRWhYAED0GFLwjdLudSooXotE9UqY15\n\tTX5iM/KVKHvjkQ2Cw6EyAKVZtPd3Fvio2/5/1aRXkGFL9qVKwGZkFZun763mBzj9CG+E\n\t4ZxzWB9PGwCHoFzttMkrmFCe9c0TFQJ7x4dxsAgMlrdyPB715q6vMYBXJFIC+GWTSp5p\n\tPUgvChV0Iv84wog/sBYDSLtDg/4hFu7yIiGdR32rPWaDa9jwiKCr6+TS6YzqVeJh6c0O\n\tHWpw==",
        "X-Gm-Message-State": "AOJu0YzvfsLxgSzlZ/kTbvOtPtmhOvWxgWF2VY1GxezoL3MxeV56tKKg\n\tWWPVs69ZsB4/YSsruC/gOhsMGjblXCy9GSz+GIDkL11LQkEGTHqUr8iOm6DKxIRbRiWbVi956rh\n\t561xGjJE=",
        "X-Gm-Gg": "AY/fxX5n1ZLujEKHjJoiSke6ltmgaDEyux1zmaM/xijyYBhxVXi3lBOobH4hvdvLTFN\n\tpI7ymEsyo+yrWNnbapTlhA+9pjEPqs5rSxxqylYK5qGFZmU8KsSc8HsAQnqWshWFnWXwRsZqAqh\n\tmHNdQ6vuQ9BwSAogIwpRFZHS3oNRhJOdForfLK9fNUSFTHDUoaddPqsTQBAVVFKkEpyyUcp+Xy9\n\tQ2pZ+QiG//NcRjyX9QGgKUMDgC4xNbFaqWDDRsU8ZCmKHNdwv9EKkqTZS+fy7D3cqGwldUeH9x3\n\tsWWq+bsxMvUpOLElLJ43dDAuWTsHjRiRiakO1yb8RluLwXJaMxb/4ws3geUkSv7qnUEOBx02R82\n\tVbJ5f2d/7JzbevVHDGCX8i1S67R/Y7PAiFdorL4yTLv2k3nBFwJ3Yc3yQrLbYA3piaSE9TdA+48\n\turxpD62qibjJwuml/i5NtiWJ+8shOU2LmsWX78nHTDmQJ5Q3zDzaDaRvkXvavGVe5lKYbcDWvLG\n\tfopyGZl+aNDxE6G4dLP63uDLTguDw==",
        "X-Google-Smtp-Source": "AGHT+IExf18Z4bW6i8sWXM3C7vWDRka4CLkIHXjr9zOf0h6ujOSWCWJ7CVLOdJ0cu6oqtr1XMo+iZA==",
        "X-Received": "by 2002:a05:600c:470c:b0:477:557b:691d with SMTP id\n\t5b1f17b1804b1-47d84b39d1cmr17666925e9.25.1767778704686; \n\tWed, 07 Jan 2026 01:38:24 -0800 (PST)",
        "From": "David Plowman <david.plowman@raspberrypi.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "David Plowman <david.plowman@raspberrypi.com>",
        "Subject": "[PATCH] libcamera: rpi: Make the controller min frame duration\n\tconfigurable",
        "Date": "Wed,  7 Jan 2026 09:37:36 +0000",
        "Message-ID": "<20260107093821.14600-1-david.plowman@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.47.3",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "The controller min frame duration is used to rate limit how often we\nrun IPAs. Historically this has been set to 33333us, meaning that the\nalgorithms effectively skip frames when the camera is running faster\nthan 30fps.\n\nThis patch adds a small amount of plumbing that allows this value to\nbe set in the Raspberry Pi configuration file. Some applications or\nplatforms (such as Pi 5) are easily capable of running these more\noften, should there be a need to do so.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n include/libcamera/ipa/raspberrypi.mojom       |  1 +\n src/ipa/rpi/common/ipa_base.cpp               | 11 ++++++++-\n src/ipa/rpi/common/ipa_base.h                 |  2 ++\n .../pipeline/rpi/common/pipeline_base.cpp     | 24 ++++++++++++++-----\n .../pipeline/rpi/common/pipeline_base.h       |  5 ++++\n .../pipeline/rpi/pisp/data/example.yaml       |  6 +++++\n .../pipeline/rpi/vc4/data/example.yaml        |  6 +++++\n 7 files changed, 48 insertions(+), 7 deletions(-)",
    "diff": "diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\nindex 12b083e9..1b7e0358 100644\n--- a/include/libcamera/ipa/raspberrypi.mojom\n+++ b/include/libcamera/ipa/raspberrypi.mojom\n@@ -18,6 +18,7 @@ struct SensorConfig {\n struct InitParams {\n \tbool lensPresent;\n \tlibcamera.IPACameraSensorInfo sensorInfo;\n+\tfloat controllerMinFrameDurationUs;\n \t/* PISP specific */\n \tlibcamera.SharedFD fe;\n \tlibcamera.SharedFD be;\ndiff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\nindex 14aba450..2fd101da 100644\n--- a/src/ipa/rpi/common/ipa_base.cpp\n+++ b/src/ipa/rpi/common/ipa_base.cpp\n@@ -184,6 +184,15 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini\n \n \tresult->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);\n \n+\t/*\n+\t * This determines the minimum allowable inter-frame duration to run the\n+\t * controller algorithms. If the pipeline handler provider frames at a\n+\t * rate higher than this, we rate-limit the controller Prepare() and\n+\t * Process() calls to lower than or equal to this rate.\n+\t */\n+\tdouble dur_us = params.controllerMinFrameDurationUs;\n+\tcontrollerMinFrameDuration_ = std::chrono::duration<double, std::micro>(dur_us);\n+\n \treturn platformInit(params, result);\n }\n \n@@ -465,7 +474,7 @@ void IpaBase::prepareIsp(const PrepareParams &params)\n \t/* Allow a 10% margin on the comparison below. */\n \tDuration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns;\n \tif (lastRunTimestamp_ && frameCount_ > invalidCount_ &&\n-\t    delta < controllerMinFrameDuration * 0.9 && !hdrChange) {\n+\t    delta < controllerMinFrameDuration_ * 0.9 && !hdrChange) {\n \t\t/*\n \t\t * Ensure we merge the previous frame's metadata with the current\n \t\t * frame. This will not overwrite exposure/gain values for the\ndiff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\nindex 5348f2ea..90f018b2 100644\n--- a/src/ipa/rpi/common/ipa_base.h\n+++ b/src/ipa/rpi/common/ipa_base.h\n@@ -142,6 +142,8 @@ private:\n \t} flickerState_;\n \n \tbool awbEnabled_;\n+\n+\tutils::Duration controllerMinFrameDuration_;\n };\n \n } /* namespace ipa::RPi */\ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\nindex fb8e466f..b7655d8d 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n@@ -33,6 +33,12 @@ LOG_DEFINE_CATEGORY(RPI)\n \n using StreamFlag = RPi::Stream::StreamFlag;\n \n+/*\n+ * The IPA's algorithms will not be called more often than this many\n+ * microseconds. The default corresponds to 30fps.\n+ */\n+constexpr float defaultControllerMinimumFrameDurationUs = 1000000.0 / 30.0;\n+\n namespace {\n \n constexpr unsigned int defaultRawBitDepth = 12;\n@@ -800,6 +806,12 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n \tif (!data->sensor_)\n \t\treturn -EINVAL;\n \n+\tret = data->loadPipelineConfiguration();\n+\tif (ret) {\n+\t\tLOG(RPI, Error) << \"Unable to load pipeline configuration\";\n+\t\treturn ret;\n+\t}\n+\n \t/* Populate the map of sensor supported formats and sizes. */\n \tfor (auto const mbusCode : data->sensor_->mbusCodes())\n \t\tdata->sensorFormats_.emplace(mbusCode,\n@@ -859,12 +871,6 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera\n \tif (ret)\n \t\treturn ret;\n \n-\tret = data->loadPipelineConfiguration();\n-\tif (ret) {\n-\t\tLOG(RPI, Error) << \"Unable to load pipeline configuration\";\n-\t\treturn ret;\n-\t}\n-\n \t/* Setup the general IPA signal handlers. */\n \tdata->frontendDevice()->dequeueTimeout.connect(data, &RPi::CameraData::cameraTimeout);\n \tdata->frontendDevice()->frameStart.connect(data, &RPi::CameraData::frameStarted);\n@@ -1096,6 +1102,7 @@ int CameraData::loadPipelineConfiguration()\n {\n \tconfig_ = {\n \t\t.cameraTimeoutValue = 0,\n+\t\t.controllerMinFrameDurationUs = defaultControllerMinimumFrameDurationUs,\n \t};\n \n \t/* Initial configuration of the platform, in case no config file is present */\n@@ -1145,6 +1152,9 @@ int CameraData::loadPipelineConfiguration()\n \t\tfrontendDevice()->setDequeueTimeout(config_.cameraTimeoutValue * 1ms);\n \t}\n \n+\tconfig_.controllerMinFrameDurationUs =\n+\t\tphConfig[\"controller_min_frame_duration_us\"].get<double>(config_.controllerMinFrameDurationUs);\n+\n \treturn platformPipelineConfigure(root);\n }\n \n@@ -1173,6 +1183,8 @@ int CameraData::loadIPA(ipa::RPi::InitResult *result)\n \t}\n \n \tparams.lensPresent = !!sensor_->focusLens();\n+\tparams.controllerMinFrameDurationUs = config_.controllerMinFrameDurationUs;\n+\n \tret = platformInitIpa(params);\n \tif (ret)\n \t\treturn ret;\ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\nindex 6257a934..597eb587 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n@@ -169,6 +169,11 @@ public:\n \t\t * on frame durations.\n \t\t */\n \t\tunsigned int cameraTimeoutValue;\n+\t\t/*\n+\t\t * The minimum frame duration between the IPA's calls to the\n+\t\t * algorithms themselves (in microseconds).\n+\t\t */\n+\t\tfloat controllerMinFrameDurationUs;\n \t};\n \n \tConfig config_;\ndiff --git a/src/libcamera/pipeline/rpi/pisp/data/example.yaml b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\nindex baf03be7..c5edbba0 100644\n--- a/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n+++ b/src/libcamera/pipeline/rpi/pisp/data/example.yaml\n@@ -36,5 +36,11 @@\n                 # framebuffers required for its operation.\n                 #\n                 # \"disable_hdr\": false,\n+\n+                # Limits the rate at which IPAs are called. The algorithms will\n+                # be skipped until this many microseconds have elapsed since\n+                # the last call. The default value represents a 30fps limit.\n+                #\n+                # \"controller_min_frame_duration_us\": 33333.333,\n         }\n }\ndiff --git a/src/libcamera/pipeline/rpi/vc4/data/example.yaml b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\nindex 27e54348..2ee2b864 100644\n--- a/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n+++ b/src/libcamera/pipeline/rpi/vc4/data/example.yaml\n@@ -37,5 +37,11 @@\n                 # timeout value.\n                 #\n                 # \"camera_timeout_value_ms\": 0,\n+\n+                # Limits the rate at which IPAs are called. The algorithms will\n+                # be skipped until this many microseconds have elapsed since\n+                # the last call. The default value represents a 30fps limit.\n+                #\n+                # \"controller_min_frame_duration_us\": 33333.333,\n         }\n }\n",
    "prefixes": []
}