Show a patch.

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

{
    "id": 24793,
    "url": "https://patchwork.libcamera.org/api/patches/24793/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/24793/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20251024085130.995967-28-stefan.klug@ideasonboard.com>",
    "date": "2025-10-24T08:50:51",
    "name": "[v1,27/35] pipeline: rkisp1: Decouple image, stats and param buffers",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "64295f3366864f6533f9bbbde2c862beee48b472",
    "submitter": {
        "id": 184,
        "url": "https://patchwork.libcamera.org/api/people/184/?format=api",
        "name": "Stefan Klug",
        "email": "stefan.klug@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/24793/mbox/",
    "series": [
        {
            "id": 5524,
            "url": "https://patchwork.libcamera.org/api/series/5524/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5524",
            "date": "2025-10-24T08:50:24",
            "name": "rkisp1: pipeline rework for PFC",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5524/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/24793/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/24793/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 A2186BE080\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 24 Oct 2025 08:53:01 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 627FD60920;\n\tFri, 24 Oct 2025 10:53:01 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0996360904\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 24 Oct 2025 10:53:00 +0200 (CEST)",
            "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:7edc:62f4:c118:1549])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 7012B4B0C; \n\tFri, 24 Oct 2025 10:51:13 +0200 (CEST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"H/MSe03x\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1761295874;\n\tbh=pPX6ZkvwcPT+gy/6082nsbDObFZgTGQBPPRh5DqgZqQ=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=H/MSe03x9uJPk40CA+QCctKWhFcuWOv8qJFxEiXf89Fbata2odWXcAJCpIsx/t7FT\n\tFT5lUgWf6eD9IQlS1kc7jZNTLjbsKpprfvye8Olh4+Q8an/Ap27xpMVTnDPHzKsKO0\n\tdq9Pqzno5FyhBkgtb353oClhnzqyU+OKNQqf2LcQ=",
        "From": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "Subject": "[PATCH v1 27/35] pipeline: rkisp1: Decouple image,\n\tstats and param buffers",
        "Date": "Fri, 24 Oct 2025 10:50:51 +0200",
        "Message-ID": "<20251024085130.995967-28-stefan.klug@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.48.1",
        "In-Reply-To": "<20251024085130.995967-1-stefan.klug@ideasonboard.com>",
        "References": "<20251024085130.995967-1-stefan.klug@ideasonboard.com>",
        "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 current code creates FrameInfo objects that tie together params,\nstats and image buffers and expect that these always stay in these\ngroupings. However there are cases where these sequences get out of sync\n(e.g. a scratch buffer is used for an image or a params buffer was too\nlate and therefore gets applied one frame later). As these situations\nare timing related and cpu dependent it is impossible to guarantee the\ninitial grouping of buffers.\n\nSplit the buffers into separate queues for stats, images and params.\nResynchronize on image buffers as soon as they are dequeued from V4L2 as\nthese are the only buffers tied to the libcamera requests coming from\nthe user application.\n\nNow handle the other buffer types according to their specific properties:\n\nStats buffers only need to be tracked after dequing and can be tied to\nthe corresponding request after the corresponding image buffer was\ndequeued.\n\nIf params buffers get out of sync we can either inject the same set of\nparameters twice or skip one set of params.\n\nIf image buffers get out of sync we need to update the expected sensor\nsequence, so that the next request is assigned the correct sensor\nsequence.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n\n---\n\nChanges in v0.6:\n- Fixed multiple assertions on start/stop and a few corner cases\n- Fixed crash in dewarpRequestReady on stop\n\nChanges in v0.5\n- Fixed possible use-after-free in RequestInfo\n---\n src/libcamera/pipeline/rkisp1/rkisp1.cpp | 733 +++++++++++++++--------\n 1 file changed, 475 insertions(+), 258 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex d83f7d787892..cd9364cb8950 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -6,6 +6,8 @@\n  */\n \n #include <algorithm>\n+#include <deque>\n+#include <iterator>\n #include <map>\n #include <memory>\n #include <numeric>\n@@ -61,44 +63,14 @@ LOG_DEFINE_CATEGORY(RkISP1Schedule)\n class PipelineHandlerRkISP1;\n class RkISP1CameraData;\n \n-struct RkISP1FrameInfo {\n-\tunsigned int frame;\n-\tRequest *request;\n \n-\tFrameBuffer *paramBuffer;\n-\tFrameBuffer *statBuffer;\n-\tFrameBuffer *mainPathBuffer;\n-\tFrameBuffer *selfPathBuffer;\n-\n-\tbool paramDequeued;\n-\tbool metadataProcessed;\n-};\n-\n-class RkISP1Frames\n-{\n-public:\n-\tRkISP1Frames(PipelineHandler *pipe);\n-\n-\tRkISP1FrameInfo *create(const RkISP1CameraData *data, Request *request,\n-\t\t\t\tbool isRaw);\n-\tint destroy(unsigned int frame);\n-\tvoid clear();\n-\n-\tRkISP1FrameInfo *find(unsigned int frame);\n-\tRkISP1FrameInfo *find(FrameBuffer *buffer);\n-\tRkISP1FrameInfo *find(Request *request);\n-\n-private:\n-\tPipelineHandlerRkISP1 *pipe_;\n-\tstd::map<unsigned int, RkISP1FrameInfo> frameInfo_;\n-};\n \n class RkISP1CameraData : public Camera::Private\n {\n public:\n \tRkISP1CameraData(PipelineHandler *pipe, RkISP1MainPath *mainPath,\n \t\t\t RkISP1SelfPath *selfPath)\n-\t\t: Camera::Private(pipe), frame_(0), frameInfo_(pipe),\n+\t\t: Camera::Private(pipe), frame_(0),\n \t\t  mainPath_(mainPath), selfPath_(selfPath)\n \t{\n \t}\n@@ -111,9 +83,12 @@ public:\n \tStream selfPathStream_;\n \tstd::unique_ptr<CameraSensor> sensor_;\n \tstd::unique_ptr<DelayedControls> delayedCtrls_;\n+\t/*\n+\t * The sensor frame sequence of the last request queued to the pipeline\n+\t * handler.\n+\t */\n \tunsigned int frame_;\n \tstd::vector<IPABuffer> ipaBuffers_;\n-\tRkISP1Frames frameInfo_;\n \n \tRkISP1MainPath *mainPath_;\n \tRkISP1SelfPath *selfPath_;\n@@ -169,21 +144,54 @@ private:\n \tTransform combinedTransform_;\n };\n \n+struct SensorFrameInfo {\n+\tRequest *request = nullptr;\n+\tFrameBuffer *statsBuffer = nullptr;\n+\tControlList metadata;\n+\tbool metadataProcessed = false;\n+};\n+\n+struct RequestInfo {\n+\tRequest *request = nullptr;\n+\t/*\n+\t * The estimated sensor sequence for this request. Only reliable when\n+\t * sequenceValid is true\n+\t */\n+\tsize_t sequence = 0;\n+\tbool sequenceValid = false;\n+};\n+\n+struct ParamBufferInfo {\n+\tFrameBuffer *buffer = nullptr;\n+\tsize_t expectedSequence = 0;\n+};\n+\n+struct DewarpBufferInfo {\n+\tFrameBuffer *inputBuffer;\n+\tFrameBuffer *outputBuffer;\n+};\n+\n namespace {\n \n /*\n- * Maximum number of requests that shall be queued into the pipeline to keep\n- * the regulation fast.\n- * \\todo This needs revisiting as soon as buffers got decoupled from requests\n- * and/or a fast path for controls was implemented.\n+ * This many buffers ensures that the pipeline runs smoothly, without frame\n+ * drops.\n  */\n-static constexpr unsigned int kRkISP1MaxQueuedRequests = 4;\n+static constexpr unsigned int kRkISP1MinBufferCount = 6;\n \n /*\n- * This many internal buffers (or rather parameter and statistics buffer\n- * pairs) ensures that the pipeline runs smoothly, without frame drops.\n+ * This many internal buffers (params and stats) are needed for smooth operation\n+ * \\todo In high framerate or high cpu load situations it might be necessary to\n+ * increase this number. \\todo: This also relates to max sensor delay and must\n+ * always be >= maxSensor delay\n  */\n-static constexpr unsigned int kRkISP1MinBufferCount = 4;\n+static constexpr unsigned int kRkISP1InternalBufferCount = 4;\n+\n+/*\n+ * This many internal image buffers between ISP and dewarper are needed for\n+ * smooth operation.\n+ */\n+static constexpr unsigned int kRkISP1DewarpImageBufferCount = 4;\n \n /*\n  * This flag allows to use dynamic dewarp maps to support pan, zoom, rotate when\n@@ -223,12 +231,11 @@ private:\n \n \tfriend RkISP1CameraData;\n \tfriend RkISP1CameraConfiguration;\n-\tfriend RkISP1Frames;\n \n \tint initLinks(Camera *camera, const RkISP1CameraConfiguration &config);\n \tint createCamera(MediaEntity *sensor);\n-\tvoid tryCompleteRequest(RkISP1FrameInfo *info);\n-\tvoid cancelDewarpRequest(RkISP1FrameInfo *info);\n+\tvoid tryCompleteRequests();\n+\tvoid cancelDewarpRequest(Request *request);\n \tvoid imageBufferReady(FrameBuffer *buffer);\n \tvoid paramBufferReady(FrameBuffer *buffer);\n \tvoid statBufferReady(FrameBuffer *buffer);\n@@ -236,6 +243,9 @@ private:\n \tvoid dewarpBufferReady(FrameBuffer *buffer);\n \tvoid frameStart(uint32_t sequence);\n \n+\tvoid queueInternalBuffers();\n+\tvoid computeParamBuffers(uint32_t maxSequence);\n+\n \tint allocateBuffers(Camera *camera);\n \tint freeBuffers(Camera *camera);\n \n@@ -261,139 +271,32 @@ private:\n \tstd::vector<std::unique_ptr<V4L2Request>> dewarpRequests_;\n \tstd::queue<V4L2Request *> availableDewarpRequests_;\n \n+\tbool running_ = false;\n+\n \tstd::vector<std::unique_ptr<FrameBuffer>> paramBuffers_;\n \tstd::vector<std::unique_ptr<FrameBuffer>> statBuffers_;\n \tstd::queue<FrameBuffer *> availableParamBuffers_;\n \tstd::queue<FrameBuffer *> availableStatBuffers_;\n \n-\tCamera *activeCamera_;\n-};\n+\tstd::deque<RequestInfo> queuedRequests_;\n \n-RkISP1Frames::RkISP1Frames(PipelineHandler *pipe)\n-\t: pipe_(static_cast<PipelineHandlerRkISP1 *>(pipe))\n-{\n-}\n-\n-RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *request,\n-\t\t\t\t      bool isRaw)\n-{\n-\tunsigned int frame = data->frame_;\n-\n-\tFrameBuffer *paramBuffer = nullptr;\n-\tFrameBuffer *statBuffer = nullptr;\n-\tFrameBuffer *mainPathBuffer = nullptr;\n-\tFrameBuffer *selfPathBuffer = nullptr;\n+\tstd::map<unsigned int, SensorFrameInfo> sensorFrameInfos_;\n \n-\tif (!isRaw) {\n-\t\tif (pipe_->availableParamBuffers_.empty()) {\n-\t\t\tLOG(RkISP1, Error) << \"Parameters buffer underrun\";\n-\t\t\treturn nullptr;\n-\t\t}\n-\n-\t\tif (pipe_->availableStatBuffers_.empty()) {\n-\t\t\tLOG(RkISP1, Error) << \"Statistic buffer underrun\";\n-\t\t\treturn nullptr;\n-\t\t}\n-\n-\t\tparamBuffer = pipe_->availableParamBuffers_.front();\n-\t\tpipe_->availableParamBuffers_.pop();\n-\n-\t\tstatBuffer = pipe_->availableStatBuffers_.front();\n-\t\tpipe_->availableStatBuffers_.pop();\n-\n-\t\tif (data->usesDewarper_) {\n-\t\t\tmainPathBuffer = pipe_->availableMainPathBuffers_.front();\n-\t\t\tpipe_->availableMainPathBuffers_.pop();\n-\t\t}\n-\t}\n+\tstd::deque<DewarpBufferInfo> queuedDewarpBuffers_;\n+\tSequenceSyncHelper paramsSyncHelper_;\n+\tSequenceSyncHelper imageSyncHelper_;\n \n-\tif (!mainPathBuffer)\n-\t\tmainPathBuffer = request->findBuffer(&data->mainPathStream_);\n-\tselfPathBuffer = request->findBuffer(&data->selfPathStream_);\n+\tstd::queue<ParamBufferInfo> computingParamBuffers_;\n+\tstd::queue<ParamBufferInfo> queuedParamBuffers_;\n \n-\tauto [it, inserted] = frameInfo_.try_emplace(frame);\n-\tASSERT(inserted);\n \n-\tauto &info = it->second;\n+\tuint32_t nextParamsSequence_;\n+\tuint32_t nextStatsToProcess_;\n \n-\tinfo.frame = frame;\n-\tinfo.request = request;\n-\tinfo.paramBuffer = paramBuffer;\n-\tinfo.mainPathBuffer = mainPathBuffer;\n-\tinfo.selfPathBuffer = selfPathBuffer;\n-\tinfo.statBuffer = statBuffer;\n-\tinfo.paramDequeued = false;\n-\tinfo.metadataProcessed = false;\n-\n-\treturn &info;\n-}\n-\n-int RkISP1Frames::destroy(unsigned int frame)\n-{\n-\tauto it = frameInfo_.find(frame);\n-\tif (it == frameInfo_.end())\n-\t\treturn -ENOENT;\n-\n-\tauto &info = it->second;\n-\n-\tpipe_->availableParamBuffers_.push(info.paramBuffer);\n-\tpipe_->availableStatBuffers_.push(info.statBuffer);\n-\tpipe_->availableMainPathBuffers_.push(info.mainPathBuffer);\n-\n-\tframeInfo_.erase(it);\n-\n-\treturn 0;\n-}\n-\n-void RkISP1Frames::clear()\n-{\n-\tfor (const auto &[frame, info] : frameInfo_) {\n-\t\tpipe_->availableParamBuffers_.push(info.paramBuffer);\n-\t\tpipe_->availableStatBuffers_.push(info.statBuffer);\n-\t\tpipe_->availableMainPathBuffers_.push(info.mainPathBuffer);\n-\t}\n-\n-\tframeInfo_.clear();\n-}\n-\n-RkISP1FrameInfo *RkISP1Frames::find(unsigned int frame)\n-{\n-\tauto itInfo = frameInfo_.find(frame);\n-\n-\tif (itInfo != frameInfo_.end())\n-\t\treturn &itInfo->second;\n-\n-\tLOG(RkISP1, Fatal) << \"Can't locate info from frame\";\n-\n-\treturn nullptr;\n-}\n-\n-RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer)\n-{\n-\tfor (auto &[frame, info] : frameInfo_) {\n-\t\tif (info.paramBuffer == buffer ||\n-\t\t    info.statBuffer == buffer ||\n-\t\t    info.mainPathBuffer == buffer ||\n-\t\t    info.selfPathBuffer == buffer)\n-\t\t\treturn &info;\n-\t}\n-\n-\tLOG(RkISP1, Fatal) << \"Can't locate info from buffer\";\n-\n-\treturn nullptr;\n-}\n-\n-RkISP1FrameInfo *RkISP1Frames::find(Request *request)\n-{\n-\tfor (auto &[frame, info] : frameInfo_) {\n-\t\tif (info.request == request)\n-\t\t\treturn &info;\n-\t}\n+\tCamera *activeCamera_;\n+};\n \n-\tLOG(RkISP1, Fatal) << \"Can't locate info from request\";\n \n-\treturn nullptr;\n-}\n \n PipelineHandlerRkISP1 *RkISP1CameraData::pipe()\n {\n@@ -504,44 +407,95 @@ int RkISP1CameraData::loadTuningFile(const std::string &path)\n void RkISP1CameraData::paramsComputed(unsigned int frame, unsigned int bytesused)\n {\n \tPipelineHandlerRkISP1 *pipe = RkISP1CameraData::pipe();\n-\tRkISP1FrameInfo *info = frameInfo_.find(frame);\n-\tif (!info)\n-\t\treturn;\n+\tParamBufferInfo &pInfo = pipe->computingParamBuffers_.front();\n+\tpipe->computingParamBuffers_.pop();\n+\n+\tASSERT(pInfo.expectedSequence == frame);\n+\tFrameBuffer *buffer = pInfo.buffer;\n \n-\tinfo->paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused;\n+\tLOG(RkISP1Schedule, Debug) << \"Queue params for \" << frame << \" \" << buffer;\n \n-\tint ret = pipe->param_->queueBuffer(info->paramBuffer);\n+\tbuffer->_d()->metadata().planes()[0].bytesused = bytesused;\n+\tint ret = pipe->param_->queueBuffer(buffer);\n \tif (ret < 0) {\n \t\tLOG(RkISP1, Error) << \"Failed to queue parameter buffer: \"\n \t\t\t\t   << strerror(-ret);\n+\t\tpipe->availableParamBuffers_.push(buffer);\n \t\treturn;\n \t}\n \n-\tpipe->stat_->queueBuffer(info->statBuffer);\n-\n-\tif (info->mainPathBuffer)\n-\t\tmainPath_->queueBuffer(info->mainPathBuffer);\n-\n-\tif (selfPath_ && info->selfPathBuffer)\n-\t\tselfPath_->queueBuffer(info->selfPathBuffer);\n+\tpipe->queuedParamBuffers_.push({ buffer, frame });\n }\n \n void RkISP1CameraData::setSensorControls(unsigned int frame,\n \t\t\t\t\t const ControlList &sensorControls)\n {\n+\t/* We know delayed controls is prewarmed for frame 0 */\n+\tif (frame == 0)\n+\t\treturn;\n+\n+\tLOG(RkISP1Schedule, Debug) << \"DelayedControls push \" << frame;\n \tdelayedCtrls_->push(frame, sensorControls);\n }\n \n void RkISP1CameraData::metadataReady(unsigned int frame, const ControlList &metadata)\n {\n-\tRkISP1FrameInfo *info = frameInfo_.find(frame);\n-\tif (!info)\n-\t\treturn;\n+\tPipelineHandlerRkISP1 *pipe = RkISP1CameraData::pipe();\n+\n+\tLOG(RkISP1Schedule, Debug) << \" metadataReady \" << frame;\n \n-\tinfo->request->metadata().merge(metadata);\n-\tinfo->metadataProcessed = true;\n+\tauto &info = pipe->sensorFrameInfos_[frame];\n \n-\tpipe()->tryCompleteRequest(info);\n+\t/*\n+\t * We don't necessarily know the request for that sequence number,\n+\t * as the dequeue of the image buffer might not have happened yet.\n+\t * So we check all known requests and store the metadata otherwise.\n+\t */\n+\tfor (auto &reqInfo : pipe->queuedRequests_) {\n+\t\tif (!reqInfo.sequenceValid) {\n+\t\t\tLOG(RkISP1Schedule, Debug)\n+\t\t\t\t<< \"Need to store metadata for later \" << frame;\n+\t\t\tinfo.metadata = metadata;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (frame > reqInfo.sequence) {\n+\t\t\t/*\n+\t\t\t * We will never get stats for that request. Log an\n+\t\t\t * error and return it.\n+\t\t\t */\n+\t\t\tLOG(RkISP1, Warning)\n+\t\t\t\t<< \"Stats for frame \" << reqInfo.sequence\n+\t\t\t\t<< \" got lost\";\n+\t\t\tauto &info2 = pipe->sensorFrameInfos_[reqInfo.sequence];\n+\t\t\tinfo2.metadataProcessed = true;\n+\t\t\tASSERT(info2.statsBuffer == nullptr);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (frame == reqInfo.sequence) {\n+\t\t\treqInfo.request->metadata().merge(metadata);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* We should never end up here */\n+\t\tLOG(RkISP1, Error) << \"Request for sequence \" << frame\n+\t\t\t\t   << \" is already handled. Metadata was too late\";\n+\n+\t\tbreak;\n+\t}\n+\n+\tinfo.metadataProcessed = true;\n+\t/*\n+\t * info.statsBuffer can be null, if ipa->processStats() was called\n+\t * without a buffer to just fill the metadata.\n+\t */\n+\tif (info.statsBuffer)\n+\t\tpipe->availableStatBuffers_.push(info.statsBuffer);\n+\tinfo.statsBuffer = nullptr;\n+\n+\tpipe->tryCompleteRequests();\n+\tpipe->queueInternalBuffers();\n }\n \n /* -----------------------------------------------------------------------------\n@@ -810,7 +764,7 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n  */\n \n PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)\n-\t: PipelineHandler(manager, kRkISP1MaxQueuedRequests), hasSelfPath_(true)\n+\t: PipelineHandler(manager), hasSelfPath_(true)\n {\n }\n \n@@ -1189,18 +1143,19 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)\n \t} };\n \n \tif (!isRaw_) {\n-\t\tret = param_->allocateBuffers(kRkISP1MinBufferCount, &paramBuffers_);\n+\t\tret = param_->allocateBuffers(kRkISP1InternalBufferCount, &paramBuffers_);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \n-\t\tret = stat_->allocateBuffers(kRkISP1MinBufferCount, &statBuffers_);\n+\t\tret = stat_->allocateBuffers(kRkISP1InternalBufferCount, &statBuffers_);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \t}\n \n \t/* If the dewarper is being used, allocate internal buffers for ISP. */\n \tif (data->usesDewarper_) {\n-\t\tret = mainPath_.exportBuffers(kRkISP1MinBufferCount, &mainPathBuffers_);\n+\t\tret = mainPath_.exportBuffers(kRkISP1DewarpImageBufferCount,\n+\t\t\t\t\t      &mainPathBuffers_);\n \t\tif (ret < 0)\n \t\t\treturn ret;\n \n@@ -1208,7 +1163,7 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera)\n \t\t\tavailableMainPathBuffers_.push(buffer.get());\n \n \t\tif (dewarper_->supportsRequests()) {\n-\t\t\tret = dewarper_->allocateRequests(kRkISP1MinBufferCount,\n+\t\t\tret = dewarper_->allocateRequests(kRkISP1DewarpImageBufferCount + 1,\n \t\t\t\t\t\t\t  &dewarpRequests_);\n \t\t\tif (ret < 0)\n \t\t\t\tLOG(RkISP1, Error) << \"Failed to allocate requests.\";\n@@ -1308,6 +1263,10 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL\n \tdata->delayedCtrls_->reset();\n \n \tdata->frame_ = 0;\n+\tnextParamsSequence_ = 0;\n+\tnextStatsToProcess_ = 0;\n+\tparamsSyncHelper_.reset();\n+\timageSyncHelper_.reset();\n \n \tif (!isRaw_) {\n \t\tret = param_->streamOn();\n@@ -1364,6 +1323,9 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL\n \tisp_->setFrameStartEnabled(true);\n \n \tactiveCamera_ = camera;\n+\trunning_ = true;\n+\n+\tqueueInternalBuffers();\n \n \tactions.release();\n \treturn 0;\n@@ -1373,6 +1335,9 @@ void PipelineHandlerRkISP1::stopDevice(Camera *camera)\n {\n \tRkISP1CameraData *data = cameraData(camera);\n \tint ret;\n+\trunning_ = false;\n+\n+\tLOG(RkISP1Schedule, Debug) << \"Stop device\";\n \n \tisp_->setFrameStartEnabled(false);\n \n@@ -1393,40 +1358,153 @@ void PipelineHandlerRkISP1::stopDevice(Camera *camera)\n \t\t\tLOG(RkISP1, Warning)\n \t\t\t\t<< \"Failed to stop parameters for \" << camera->id();\n \n+\t\t/*\n+\t\t * The param buffers are not returned in order, so the queue\n+\t\t * becomes useless.\n+\t\t */\n+\t\tqueuedParamBuffers_ = {};\n+\n \t\tif (data->usesDewarper_)\n \t\t\tdewarper_->stop();\n \t}\n \n-\tASSERT(data->queuedRequests_.empty());\n-\tdata->frameInfo_.clear();\n+\ttryCompleteRequests();\n+\n+\t/* There can still be requests that are either waiting for metadata\n+\t   or that contain buffers which were not yet queued at all. */\n+\twhile (!queuedRequests_.empty()) {\n+\t\tRequestInfo &reqInfo = queuedRequests_.front();\n+\t\tcancelRequest(reqInfo.request);\n+\t\tqueuedRequests_.pop_front();\n+\t}\n+\tsensorFrameInfos_.clear();\n+\n+\tASSERT(queuedDewarpBuffers_.empty());\n+\tASSERT(queuedParamBuffers_.empty());\n+\tASSERT(computingParamBuffers_.empty());\n \n \tfreeBuffers(camera);\n \n \tactiveCamera_ = nullptr;\n }\n \n-int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)\n+void PipelineHandlerRkISP1::queueInternalBuffers()\n {\n-\tRkISP1CameraData *data = cameraData(camera);\n+\tif (!running_)\n+\t\treturn;\n \n-\tRkISP1FrameInfo *info = data->frameInfo_.create(data, request, isRaw_);\n-\tif (!info)\n-\t\treturn -ENOENT;\n+\tRkISP1CameraData *data = cameraData(activeCamera_);\n+\n+\twhile (!availableStatBuffers_.empty()) {\n+\t\tFrameBuffer *buf = availableStatBuffers_.front();\n+\t\tavailableStatBuffers_.pop();\n+\t\tdata->pipe()->stat_->queueBuffer(buf);\n+\t}\n \n-\tdata->ipa_->queueRequest(data->frame_, request->controls());\n+\t/*\n+\t * In case of the dewarper, there is a seperate buffer loop for the main\n+\t * path\n+\t */\n+\twhile (!availableMainPathBuffers_.empty()) {\n+\t\tFrameBuffer *buf = availableMainPathBuffers_.front();\n+\t\tavailableMainPathBuffers_.pop();\n+\n+\t\tLOG(RkISP1Schedule, Debug) << \"Queue mainPath \" << buf;\n+\t\tdata->mainPath_->queueBuffer(buf);\n+\t}\n+}\n+\n+void PipelineHandlerRkISP1::computeParamBuffers(uint32_t maxSequence)\n+{\n+\tRkISP1CameraData *data = cameraData(activeCamera_);\n \tif (isRaw_) {\n-\t\tif (info->mainPathBuffer)\n-\t\t\tdata->mainPath_->queueBuffer(info->mainPathBuffer);\n+\t\t/*\n+\t\t * Call computeParams with an empty param buffer to trigger\n+\t\t * the setSensorControls signal.\n+\t\t */\n+\t\tdata->ipa_->computeParams(maxSequence, 0);\n+\t\treturn;\n+\t}\n \n-\t\tif (data->selfPath_ && info->selfPathBuffer)\n-\t\t\tdata->selfPath_->queueBuffer(info->selfPathBuffer);\n-\t} else {\n-\t\tdata->ipa_->computeParams(data->frame_,\n-\t\t\t\t\t  info->paramBuffer->cookie());\n+\twhile (nextParamsSequence_ <= maxSequence) {\n+\t\tif (availableParamBuffers_.empty()) {\n+\t\t\tLOG(RkISP1Schedule, Warning)\n+\t\t\t\t<< \"Ran out of parameter buffers\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tint correction = paramsSyncHelper_.correction();\n+\t\tif (correction != 0)\n+\t\t\tLOG(RkISP1Schedule, Warning)\n+\t\t\t\t<< \"Correcting params sequence \"\n+\t\t\t\t<< correction;\n+\n+\t\tuint32_t paramsSequence;\n+\t\tif (correction >= 0) {\n+\t\t\tnextParamsSequence_ += correction;\n+\t\t\tparamsSyncHelper_.pushCorrection(correction);\n+\t\t\tparamsSequence = nextParamsSequence_++;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * Inject the same sequence multiple times, to correct\n+\t\t\t * for the offset.\n+\t\t\t */\n+\t\t\tparamsSyncHelper_.pushCorrection(-1);\n+\t\t\tparamsSequence = nextParamsSequence_;\n+\t\t}\n+\n+\t\tFrameBuffer *buf = availableParamBuffers_.front();\n+\t\tavailableParamBuffers_.pop();\n+\t\tcomputingParamBuffers_.push({ buf, paramsSequence });\n+\t\tLOG(RkISP1Schedule, Debug) << \"Request params for \" << paramsSequence;\n+\t\tdata->ipa_->computeParams(paramsSequence, buf->cookie());\n \t}\n+}\n \n+int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)\n+{\n+\tRkISP1CameraData *data = cameraData(camera);\n+\n+\tRequestInfo info;\n+\tinfo.request = request;\n+\n+\tint correction = imageSyncHelper_.correction();\n+\tif (correction != 0)\n+\t\tLOG(RkISP1Schedule, Debug)\n+\t\t\t<< \"Correcting image sequence \"\n+\t\t\t<< data->frame_ << \" to \" << data->frame_ + correction;\n+\tdata->frame_ += correction;\n+\timageSyncHelper_.pushCorrection(correction);\n+\tinfo.sequence = data->frame_;\n \tdata->frame_++;\n \n+\tLOG(RkISP1Schedule, Debug) << \"Queue request. Request sequence: \"\n+\t\t\t\t   << request->sequence()\n+\t\t\t\t   << \" estimated sensor frame sequence: \" << info.sequence\n+\t\t\t\t   << \" queue size: \" << (queuedRequests_.size() + 1);\n+\n+\tdata->ipa_->queueRequest(info.sequence, request->controls());\n+\n+\t/*\n+\t * When the dewarper is used, the request buffers will be queued in\n+\t * imageBufferReady()\n+\t */\n+\tif (!data->usesDewarper_) {\n+\t\tFrameBuffer *mainPathBuffer = request->findBuffer(&data->mainPathStream_);\n+\t\tFrameBuffer *selfPathBuffer = request->findBuffer(&data->selfPathStream_);\n+\t\tif (mainPathBuffer)\n+\t\t\tdata->mainPath_->queueBuffer(mainPathBuffer);\n+\n+\t\tif (data->selfPath_ && selfPathBuffer)\n+\t\t\tdata->selfPath_->queueBuffer(selfPathBuffer);\n+\t}\n+\n+\tqueuedRequests_.push_back(info);\n+\n+\t/* Kickstart computation of parameters. */\n+\tif (info.sequence < kRkISP1InternalBufferCount)\n+\t\tcomputeParamBuffers(info.sequence);\n+\n \treturn 0;\n }\n \n@@ -1626,12 +1704,11 @@ void PipelineHandlerRkISP1::frameStart(uint32_t sequence)\n \t\treturn;\n \n \tRkISP1CameraData *data = cameraData(activeCamera_);\n+\tLOG(RkISP1Schedule, Debug) << \"frameStart \" << sequence;\n \tuint32_t sequenceToApply = sequence + data->delayedCtrls_->maxDelay();\n \tdata->delayedCtrls_->applyControls(sequenceToApply);\n \n-\tif (isRaw_) {\n-\t\tdata->ipa_->computeParams(sequenceToApply + 1, 0);\n-\t}\n+\tcomputeParamBuffers(sequenceToApply + 1);\n }\n \n bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)\n@@ -1724,29 +1801,51 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)\n  * Buffer Handling\n  */\n \n-void PipelineHandlerRkISP1::tryCompleteRequest(RkISP1FrameInfo *info)\n+void PipelineHandlerRkISP1::tryCompleteRequests()\n {\n-\tRkISP1CameraData *data = cameraData(activeCamera_);\n-\tRequest *request = info->request;\n+\tstd::optional<size_t> lastDeletedSequence;\n \n-\tif (request->hasPendingBuffers())\n-\t\treturn;\n+\t/* Complete finished requests */\n+\twhile (!queuedRequests_.empty()) {\n+\t\tRequestInfo info = queuedRequests_.front();\n \n-\tif (!info->metadataProcessed)\n-\t\treturn;\n+\t\tif (info.request->hasPendingBuffers())\n+\t\t\tbreak;\n+\n+\t\tif (!info.sequenceValid)\n+\t\t\tbreak;\n+\n+\t\tif (!sensorFrameInfos_[info.sequence].metadataProcessed)\n+\t\t\tbreak;\n+\n+\t\tqueuedRequests_.pop_front();\n+\n+\t\tLOG(RkISP1Schedule, Debug) << \"Complete request \" << info.sequence;\n+\t\tcompleteRequest(info.request);\n+\n+\t\tsensorFrameInfos_[info.sequence].request = nullptr;\n+\t\tlastDeletedSequence = info.sequence;\n+\t}\n \n-\tif (!isRaw_ && !info->paramDequeued)\n+\tif (!lastDeletedSequence.has_value())\n \t\treturn;\n \n-\tdata->frameInfo_.destroy(info->frame);\n+\t/* Drop all outdated sensor frame infos. */\n+\twhile (!sensorFrameInfos_.empty()) {\n+\t\tauto iter = sensorFrameInfos_.begin();\n+\t\tif (iter->first > lastDeletedSequence.value())\n+\t\t\tbreak;\n+\n+\t\tASSERT(iter->second.request == nullptr);\n+\t\tASSERT(iter->second.statsBuffer == nullptr);\n \n-\tcompleteRequest(request);\n+\t\tsensorFrameInfos_.erase(iter);\n+\t}\n }\n \n-void PipelineHandlerRkISP1::cancelDewarpRequest(RkISP1FrameInfo *info)\n+void PipelineHandlerRkISP1::cancelDewarpRequest(Request *request)\n {\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n-\tRequest *request = info->request;\n \t/*\n \t * i.MX8MP is the only known platform with dewarper. It has\n \t * no self path. Hence, only main path buffer completion is\n@@ -1765,51 +1864,109 @@ void PipelineHandlerRkISP1::cancelDewarpRequest(RkISP1FrameInfo *info)\n \t\t}\n \t}\n \n-\ttryCompleteRequest(info);\n+\ttryCompleteRequests();\n }\n \n void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n {\n \tASSERT(activeCamera_);\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n+\tconst FrameMetadata &metadata = buffer->metadata();\n+\tRequestInfo *reqInfo = nullptr;\n \n-\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n-\tif (!info)\n-\t\treturn;\n+\t/*\n+\t * When the dewarper is used, the buffer is not yet tied to a request,\n+\t * so find the first request without a valid sequence. Otherwise find\n+\t * the request for that buffer. This is not necessarily the same,\n+\t * because after streamoff the buffers are returned in arbitrary order.\n+\t */\n+\tfor (auto &info : queuedRequests_) {\n+\t\tif (data->usesDewarper_) {\n+\t\t\tif (!info.sequenceValid) {\n+\t\t\t\treqInfo = &info;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (info.request == buffer->request()) {\n+\t\t\t\treqInfo = &info;\n+\t\t\t}\n+\t\t}\n+\t}\n \n-\tconst FrameMetadata &metadata = buffer->metadata();\n-\tRequest *request = info->request;\n+\tif (!reqInfo) {\n+\t\tif (data->usesDewarper_) {\n+\t\t\tLOG(RkISP1Schedule, Info)\n+\t\t\t\t<< \"Image buffer ready, but no corresponding request\";\n+\t\t\tavailableMainPathBuffers_.push(buffer);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tLOG(RkISP1Schedule, Fatal)\n+\t\t\t<< \"Image buffer ready, but no corresponding request\";\n+\t}\n+\n+\tRequest *request = reqInfo->request;\n+\n+\tLOG(RkISP1Schedule, Debug) << \"Image buffer ready: \" << buffer\n+\t\t\t\t   << \" Expected sequence: \" << reqInfo->sequence\n+\t\t\t\t   << \" got: \" << metadata.sequence;\n+\n+\tuint32_t sequence = metadata.sequence;\n+\n+\t/*\n+\t * If the frame was cancelled, the metadata sequnce is usually wrong and\n+\t * we assume that our guess was right.\n+\t */\n+\tif (metadata.status == FrameMetadata::FrameCancelled)\n+\t\tsequence = reqInfo->sequence;\n+\n+\t/* We now know the buffer sequence that belongs to this request */\n+\tint droppedFrames = imageSyncHelper_.gotFrame(reqInfo->sequence, sequence);\n+\tif (droppedFrames != 0)\n+\t\tLOG(RkISP1Schedule, Warning)\n+\t\t\t<< \"Frame \" << reqInfo->sequence << \": Dropped \"\n+\t\t\t<< droppedFrames << \" frames\";\n+\n+\treqInfo->sequence = sequence;\n+\treqInfo->sequenceValid = true;\n+\n+\tif (sensorFrameInfos_[sequence].metadataProcessed) {\n+\t\tLOG(RkISP1Schedule, Debug)\n+\t\t\t<< \"Apply stored metadata \" << reqInfo->sequence;\n+\t\trequest->metadata().merge(sensorFrameInfos_[sequence].metadata);\n+\t}\n \n \tif (metadata.status != FrameMetadata::FrameCancelled) {\n \t\t/*\n \t\t * Record the sensor's timestamp in the request metadata.\n \t\t *\n-\t\t * \\todo The sensor timestamp should be better estimated by connecting\n-\t\t * to the V4L2Device::frameStart signal.\n+\t\t * \\todo The sensor timestamp should be better estimated by\n+\t\t * connecting to the V4L2Device::frameStart signal.\n \t\t */\n \t\trequest->metadata().set(controls::SensorTimestamp,\n \t\t\t\t\tmetadata.timestamp);\n \n+\t\t/* In raw mode call processStats() to fill the metadata */\n \t\tif (isRaw_) {\n \t\t\tconst ControlList &ctrls =\n \t\t\t\tdata->delayedCtrls_->get(metadata.sequence);\n-\t\t\tdata->ipa_->processStats(info->frame, 0, ctrls);\n+\t\t\tdata->ipa_->processStats(reqInfo->sequence, 0, ctrls);\n \t\t}\n \t} else {\n-\t\tif (isRaw_)\n-\t\t\tinfo->metadataProcessed = true;\n+\t\t/* No need to block waiting for metedata on that frame. */\n+\t\tsensorFrameInfos_[sequence].metadataProcessed = true;\n \t}\n \n \tif (!data->usesDewarper_) {\n-\t\tcompleteBuffer(request, buffer);\n-\t\ttryCompleteRequest(info);\n+\t\tcompleteBuffer(reqInfo->request, buffer);\n+\t\ttryCompleteRequests();\n \n \t\treturn;\n \t}\n \n-\t/* Do not queue cancelled frames to dewarper. */\n+\t/* Do not queue cancelled frames to the dewarper. */\n \tif (metadata.status == FrameMetadata::FrameCancelled) {\n-\t\tcancelDewarpRequest(info);\n+\t\tcancelDewarpRequest(reqInfo->request);\n \t\treturn;\n \t}\n \n@@ -1869,10 +2026,14 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n \t\tdewarper_->applyVertexMap(&data->mainPathStream_, dewarpRequest);\n \n \t/*\n-\t * Queue input and output buffers to the dewarper. The output\n-\t * buffers for the dewarper are the buffers of the request, supplied\n-\t * by the application.\n+\t * Queue input and output buffers to the dewarper. The output buffers\n+\t * for the dewarper are the buffers of the request, supplied by the\n+\t * application.\n \t */\n+\tDewarpBufferInfo dewarpInfo{ buffer, reqInfo->request->findBuffer(&data->mainPathStream_) };\n+\tqueuedDewarpBuffers_.push_back(dewarpInfo);\n+\tLOG(RkISP1Schedule, Debug) << \"Queue dewarper \" << dewarpInfo.inputBuffer\n+\t\t\t\t   << \" \" << dewarpInfo.outputBuffer;\n \tint ret = dewarper_->queueBuffers(buffer, request->buffers(), dewarpRequest);\n \tif (ret < 0) {\n \t\tLOG(RkISP1, Error) << \"Failed to queue buffers to dewarper: -\"\n@@ -1882,7 +2043,7 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n \t\tif (dewarpRequest)\n \t\t\tdewarpRequestReady(dewarpRequest);\n \n-\t\tcancelDewarpRequest(info);\n+\t\tcancelDewarpRequest(reqInfo->request);\n \n \t\treturn;\n \t}\n@@ -1895,7 +2056,7 @@ void PipelineHandlerRkISP1::imageBufferReady(FrameBuffer *buffer)\n \t\t\t/* Push it back into the queue. */\n \t\t\tdewarpRequestReady(dewarpRequest);\n \n-\t\t\tcancelDewarpRequest(info);\n+\t\t\tcancelDewarpRequest(reqInfo->request);\n \t\t}\n \t}\n \n@@ -1919,29 +2080,59 @@ void PipelineHandlerRkISP1::dewarpRequestReady(V4L2Request *request)\n \n void PipelineHandlerRkISP1::dewarpBufferReady(FrameBuffer *buffer)\n {\n-\tASSERT(activeCamera_);\n-\tRkISP1CameraData *data = cameraData(activeCamera_);\n \tRequest *request = buffer->request();\n+\tconst FrameMetadata &metadata = buffer->metadata();\n \n-\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer->request());\n-\tif (!info)\n-\t\treturn;\n+\t/*\n+\t * After stopping the dewarper, the buffers are returned out of order.\n+\t * Search the list for the corresponding info and handle it. In regular\n+\t * operation it will always be the first entry.\n+\t */\n+\tfor (DewarpBufferInfo &dwInfo : queuedDewarpBuffers_) {\n+\t\tif (dwInfo.outputBuffer != buffer)\n+\t\t\tcontinue;\n \n-\tcompleteBuffer(request, buffer);\n-\ttryCompleteRequest(info);\n+\t\tavailableMainPathBuffers_.push(dwInfo.inputBuffer);\n+\t\tdwInfo.inputBuffer = nullptr;\n+\t\tdwInfo.outputBuffer = nullptr;\n+\n+\t\tif (metadata.status == FrameMetadata::FrameCancelled)\n+\t\t\tbuffer->_d()->cancel();\n+\n+\t\tcompleteBuffer(request, buffer);\n+\t}\n+\n+\twhile (!queuedDewarpBuffers_.empty() &&\n+\t       queuedDewarpBuffers_.front().inputBuffer == nullptr)\n+\t\tqueuedDewarpBuffers_.pop_front();\n+\n+\ttryCompleteRequests();\n+\tqueueInternalBuffers();\n }\n \n void PipelineHandlerRkISP1::paramBufferReady(FrameBuffer *buffer)\n {\n-\tASSERT(activeCamera_);\n-\tRkISP1CameraData *data = cameraData(activeCamera_);\n+\tLOG(RkISP1Schedule, Debug) << \"Param buffer ready \" << buffer;\n \n-\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n-\tif (!info)\n+\t/* After stream off, the buffers are returned out of order, so\n+\t * we don't care about the rest.\n+\t */\n+\tif (!running_) {\n+\t\tavailableParamBuffers_.push(buffer);\n \t\treturn;\n+\t}\n \n-\tinfo->paramDequeued = true;\n-\ttryCompleteRequest(info);\n+\tParamBufferInfo pInfo = queuedParamBuffers_.front();\n+\tqueuedParamBuffers_.pop();\n+\n+\tASSERT(pInfo.buffer == buffer);\n+\n+\tsize_t metaSequence = buffer->metadata().sequence;\n+\tLOG(RkISP1Schedule, Debug) << \"Params buffer ready \"\n+\t\t\t\t   << \" Expected: \" << pInfo.expectedSequence\n+\t\t\t\t   << \" got: \" << metaSequence;\n+\tparamsSyncHelper_.gotFrame(pInfo.expectedSequence, metaSequence);\n+\tavailableParamBuffers_.push(buffer);\n }\n \n void PipelineHandlerRkISP1::statBufferReady(FrameBuffer *buffer)\n@@ -1949,21 +2140,47 @@ void PipelineHandlerRkISP1::statBufferReady(FrameBuffer *buffer)\n \tASSERT(activeCamera_);\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n \n-\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n-\tif (!info)\n-\t\treturn;\n+\tsize_t sequence = buffer->metadata().sequence;\n \n \tif (buffer->metadata().status == FrameMetadata::FrameCancelled) {\n-\t\tinfo->metadataProcessed = true;\n-\t\ttryCompleteRequest(info);\n+\t\tLOG(RkISP1Schedule, Warning) << \"Stats cancelled \" << sequence;\n+\t\t/*\n+\t\t * We can't assume that the sequence of the stat buffer is valid,\n+\t\t * so there is nothing left to do.\n+\t\t */\n+\t\tavailableStatBuffers_.push(buffer);\n+\t\treturn;\n+\t}\n+\n+\tLOG(RkISP1Schedule, Debug) << \"Stats ready \" << sequence;\n+\n+\tif (nextStatsToProcess_ != sequence)\n+\t\tLOG(RkISP1Schedule, Warning) << \"Stats sequence out of sync.\"\n+\t\t\t\t\t     << \" Expected: \" << nextStatsToProcess_\n+\t\t\t\t\t     << \" got: \" << sequence;\n+\n+\tif (nextStatsToProcess_ > sequence) {\n+\t\tLOG(RkISP1Schedule, Warning) << \"Stats were too late. Ignored\";\n+\t\tavailableStatBuffers_.push(buffer);\n \t\treturn;\n \t}\n \n-\tif (data->frame_ <= buffer->metadata().sequence)\n-\t\tdata->frame_ = buffer->metadata().sequence + 1;\n+\t/* Send empty stats to ensure metadata gets created*/\n+\twhile (nextStatsToProcess_ < sequence) {\n+\t\tLOG(RkISP1Schedule, Warning) << \"Send empty stats to fill metadata\";\n+\t\tdata->ipa_->processStats(nextStatsToProcess_, 0,\n+\t\t\t\t\t data->delayedCtrls_->get(nextStatsToProcess_));\n+\n+\t\tnextStatsToProcess_++;\n+\t}\n+\n+\tnextStatsToProcess_++;\n+\n+\tsensorFrameInfos_[sequence].statsBuffer = buffer;\n \n-\tdata->ipa_->processStats(info->frame, info->statBuffer->cookie(),\n-\t\t\t\t data->delayedCtrls_->get(buffer->metadata().sequence));\n+\tLOG(RkISP1Schedule, Debug) << \"Process stats \" << sequence;\n+\tdata->ipa_->processStats(sequence, buffer->cookie(),\n+\t\t\t\t data->delayedCtrls_->get(sequence));\n }\n \n REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1, \"rkisp1\")\n",
    "prefixes": [
        "v1",
        "27/35"
    ]
}