From patchwork Mon Jul 21 10:46:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 23884 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 D40ECC3323 for ; Mon, 21 Jul 2025 10:46:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 533A569005; Mon, 21 Jul 2025 12:46:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kGGccKcn"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3921F68FE6 for ; Mon, 21 Jul 2025 12:46:35 +0200 (CEST) Received: from pb-laptop.local (185.221.140.39.nat.pool.zt.hu [185.221.140.39]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 451077972; Mon, 21 Jul 2025 12:45:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1753094758; bh=yUD2aBtOpzzuNczp3uhMWgInss8zgNxEv8sH9JSEaYc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kGGccKcn04NvUwGINH8YXkdIXgmMYfct4WsTy6mfXrkWd6NOXMVCXntH0rK97STwA C4OnCh1Zvn8efL+IDS0QEDHo+ESA6HSiN35jEi4AbqnorFf1bEr3WhPwlbaJ3vekgG uXW9EBVkEyiUOc/9V+1quR9N+YUU3uI8Vn3zcqeA= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi Subject: [RFC PATCH v2 16/22] libcamera: pipeline_handler: Add metadataAvailable() function Date: Mon, 21 Jul 2025 12:46:16 +0200 Message-ID: <20250721104622.1550908-17-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250721104622.1550908-1-barnabas.pocze@ideasonboard.com> References: <20250721104622.1550908-1-barnabas.pocze@ideasonboard.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: Jacopo Mondi Currently the way pipeline handlers have to store metadata results for a Request is to access the Request::metadata_ list directly and either merge a ControlList or set a single control value there. Direct access to Request::metadata_ is however problematic as even if metadata would be available earlier, pipeline handlers can only accumulate the results in Request::metadata_ and they're only available for applications at Request complete time. Instead of letting pipeline handlers access Request::metadata_ directly provide two helper functions, similar in spirit to PipelineHandler::completeBuffer() and PipelineHandler::completeRequest(), to allow pipeline handlers to notify early availability of metadata. Provide three overloads, one that accepts a ControlList and merges it into Request::metadata_, one that allows to set a single metadata result there without going through an intermediate copy, and one that runs a callback for conditional metadata reporting without intermediate an ControlList. The newly provided helpers trigger the Camera::availableMetadata signal from where applications can retrieve the list of controls that have just been made available by the pipeline handler. Signed-off-by: Jacopo Mondi [Fill both lists, use `type_identity`, new overload, adjust commit message.] Signed-off-by: Barnabás Pőcze --- Original: https://patchwork.libcamera.org/patch/22229/ changes in v2: * add new overload that takes an invocable object for more flexibility --- include/libcamera/internal/pipeline_handler.h | 49 +++++++++++ src/libcamera/pipeline_handler.cpp | 84 +++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index e89d6a33e..49c907178 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -7,16 +7,21 @@ #pragma once +#include #include #include #include #include +#include #include +#include #include #include +#include "libcamera/internal/request.h" + namespace libcamera { class Camera; @@ -58,6 +63,50 @@ public: void registerRequest(Request *request); void queueRequest(Request *request); + void metadataAvailable(Request *request, const ControlList &metadata); + + template + void metadataAvailable(Request *request, const Control &ctrl, + const details::cxx20::type_identity_t &value) + { + auto &m = request->metadata2(); + const auto c = m.checkpoint(); + + m.set(ctrl, value); + request->metadata().set(ctrl, value); + + const auto d = c.diffSince(); + if (d) + request->_d()->camera()->metadataAvailable.emit(request, d); + } + +#ifndef __DOXYGEN__ + struct MetadataSetter { + Request *request; + + template + void operator()(const Control &ctrl, + const details::cxx20::type_identity_t &value) const + { + request->metadata().set(ctrl, value); + request->metadata2().set(ctrl, value); + } + }; + + template> * = nullptr> +#else + template +#endif + void metadataAvailable(Request *request, Func func) + { + const auto c = request->metadata2().checkpoint(); + + std::invoke(func, MetadataSetter{ request }); + + if (const auto d = c.diffSince()) + request->_d()->camera()->metadataAvailable.emit(request, d); + } + bool completeBuffer(Request *request, FrameBuffer *buffer); void completeRequest(Request *request); void cancelRequest(Request *request); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index e5f9e55c9..edfa9cf58 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -532,6 +532,90 @@ void PipelineHandler::doQueueRequests(Camera *camera) * \return 0 on success or a negative error code otherwise */ +/** + * \brief Signal the availability of metadata for \a request + * \param[in] request The request the metadata belongs to + * \param[in] metadata The collection of metadata items + * + * This function copies metadata items from \a metadata to the cumulative metadata + * collection of \a request. This function may be called multiple times, but metadata + * items already present in Request::metadata() are ignored. Afterwards the function + * notifies the application by triggering the Camera::availableMetadata signal with + * the just added metadata items. + * + * Early metadata completion allows pipeline handlers to fast track delivery of + * metadata results as soon as they are available before the completion of \a + * request. The full list of metadata results of a Request is available at + * Request completion time in Request::metadata(). + * + * \context This function shall be called from the CameraManager thread. + * + * \sa PipelineHandler::metadataAvailable(Request *request, Func func) + * \sa PipelineHandler::metadataAvailable(Request *request, + * const Control &ctrl, + * const details::cxx20::type_identity_t &value) + */ +void PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) +{ + request->metadata().merge(metadata); + + const auto d = request->metadata2().merge(metadata); + if (d) + request->_d()->camera()->metadataAvailable.emit(request, d); +} + +/** + * \fn void PipelineHandler::metadataAvailable(Request *request, const Control &ctrl, + * const details::cxx20::type_identity_t &value) + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + * \param[in] request The request the metadata belongs to + * \param[in] ctrl The control id of the metadata item + * \param[in] value The value of the metadata item + * + * This function servers the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + * but it allows a single metadata item to be reported directly, + * without creating a ControlList. + * + * \context This function shall be called from the CameraManager thread. + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + */ + +/** + * \fn void PipelineHandler::metadataAvailable(Request *request, Func func) + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + * \param[in] request The request the metadata belongs to + * \param[in] func The callback to invoke + * + * This function serves the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + * but it provides more flexibility for pipeline handlers. This function invokes + * \a func, which receives as its sole argument an object of unspecified type whose + * operator() can be used to add metadata items. + * + * For example, a PipelineHandler might use this function to conditionally report two + * metadata items without creating an intermediate ControlList: + * + * \code + metadataAvailable(request, [&](auto set) { + if (x) + set(controls::X, x); + if (y) + set(controls::Y, y); + // ... + }); + * \endcode + * + * The advantage of the above over two calls to PipelineHandler::metadataAvailable(Request *request, const Control &ctrl, const details::cxx20::type_identity_t &value) + * is that the application is only notified once, after \a func has returned. + * + * \note Calling any overload of metadataAvailable() inside \a func is not allowed. + * \note The object passed to \a func is only usable while \a func runs, it must not + * be saved or reused. + * + * \context This function shall be called from the CameraManager thread. + * + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata) + */ + /** * \brief Complete a buffer for a request * \param[in] request The request the buffer belongs to