From patchwork Fri Sep 25 15:16:23 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Scally X-Patchwork-Id: 28379 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 1AFF4BDCBD for ; Fri, 25 Sep 2026 15:16:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C66BF689D0; Fri, 25 Sep 2026 17:16:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="V549uhg/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0714768983 for ; Fri, 25 Sep 2026 17:16:31 +0200 (CEST) Received: from [127.0.1.1] (chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net [82.19.20.230]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CC1BEC1; Fri, 25 Sep 2026 17:14:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1790349282; bh=IXgfCSkPphXpGKYeviQkrjnOKI7HtKvIj5Wmq/Tagi8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=V549uhg/4lo4a4ceXfxALrbl2VMl6ESo0Zov///5fqCo7e9ZpKVp9twfZKGYNzfhU ZjhAjlOzp2G41mKvnToPxeXP1UhgE4Sd+MlGsTeAkFUFEcWsdNarln+b+kChlDsPZd 6CFHpSVy50+CkP4B55KZQ6YLZgUKFhiZCEor4XYk= From: Daniel Scally Date: Fri, 25 Sep 2026 16:16:23 +0100 Subject: [PATCH 3/4] libcamera: v4l2_device: Emit signals for V4L2Events generically MIME-Version: 1.0 Message-Id: <20260925-v4l2-events-v1-3-b2ec212eb5d0@ideasonboard.com> References: <20260925-v4l2-events-v1-0-b2ec212eb5d0@ideasonboard.com> In-Reply-To: <20260925-v4l2-events-v1-0-b2ec212eb5d0@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Daniel Scally X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=15278; i=dan.scally@ideasonboard.com; h=from:subject:message-id; bh=IXgfCSkPphXpGKYeviQkrjnOKI7HtKvIj5Wmq/Tagi8=; b=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqtpBNKSSyq+AJQJHK53hYACgaOxqRIQ7xx2P+Z uYSVtIaPEWJAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCaraQTQAKCRDISVd6bEV1 Mr/eEACaHFuyfNZ5oxisHRkr6wED0tDsDTgnagKbo2L8+n+3piDt1XTgb9aHIXcSLmlImcKitPa nyP7GZj8L1F4vyVGo3hGfsXFJGcJikPhFpvk+lrgRKQbG4ENGBCExxsfpUwUF+S4/8ZAp2kIPhR Njza71jBkG5AsP6pFywjpYr+UzqkwKw1/uqdsOfkapVQsg9KxPDwXpzb2kbJ+QKYmZrizNEO1vk ZvBdHwMMprUlpNqsMWMjqTx6EohBwPI3bdAddckotR9+fCdeOmOdZeb+dtm0w5nBW7dp4MTidOe nHNebMK0e4aKZ95fzqxmw1XRi6bhqroaWkSELJ8UBW7cw3pxkPv8LgVBiKoeg0M9j+I3cuQ3/5/ hkm/R8bqhmVjLkxcA0yB+FPNCElcO5l3ANhdFmPsfPv0RWK5estahUPu4NwHuCjQ07NrQXBj2Qm KcHzgkM+4TJgEM9fIdqO9e0N6GJto5cqxZYaPrpfZEFXQQe3M8sOAPKnJvQcaR9WZg0JgPTCxlo Ozx35rhimFw1denl5ZY06VyYWvTr3ozOOz6XLnbYTO1QmmKopJJcH2sv1nW2pir+nTFAf+GAFbr kDqSlRU3agowk/VitoZrsRRPODweGjBLQ7TGAskbLG4Hqcek+GLQXH8y/UfffNHlJTmBsd/cvok 26P9dgRyznQDEYg== X-Developer-Key: i=dan.scally@ideasonboard.com; a=openpgp; fpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6 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" Currently V4L2Device emits a signal for the V4L2_EVENT_FRAME_SYNC event, with a Signal signature that is specific to it. Replace this with a generic Signal and leave the Slots to decide how to handle the event. This will allow us to easily extend handling to other events in the future. Update the existing slots to handle the new Signal. Signed-off-by: Daniel Scally --- With this change, in V4L2Device::eventAvailable() I am creating a std::shared_ptr and emitting the signal to the handler for it. I *think* this is ok and that once eventAvailable() and the Slot handler have returned it is cleaned up, but I thought I'd mention it incase I'm wrong. --- include/libcamera/internal/v4l2_device.h | 2 +- src/libcamera/pipeline/ipu3/cio2.h | 3 ++- src/libcamera/pipeline/ipu3/ipu3.cpp | 17 +++++++++++------ src/libcamera/pipeline/mali-c55/mali-c55.cpp | 18 ++++++++++++++---- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 15 +++++++++++++-- src/libcamera/pipeline/rpi/common/pipeline_base.cpp | 14 ++++++++++---- src/libcamera/pipeline/rpi/common/pipeline_base.h | 3 ++- src/libcamera/pipeline/simple/simple.cpp | 19 +++++++++++++++---- src/libcamera/v4l2_device.cpp | 14 +++++++------- 9 files changed, 75 insertions(+), 30 deletions(-) diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h index cc8116874e9eeecac76f5c25e8b5719c07976781..0c3bbcf2df39f581ceffb9afa6b008368b302156 100644 --- a/include/libcamera/internal/v4l2_device.h +++ b/include/libcamera/internal/v4l2_device.h @@ -49,7 +49,7 @@ public: bool supportsEvents(V4L2EventSubscription &sub); int setFrameStartEnabled(bool enable); - Signal frameStart; + Signal> eventReady; void updateControlInfo(); diff --git a/src/libcamera/pipeline/ipu3/cio2.h b/src/libcamera/pipeline/ipu3/cio2.h index 91651a16404e2c524b13829b4909474e579ad217..1d29f19e01ae16b7d5500d0584ee226ee4e6453e 100644 --- a/src/libcamera/pipeline/ipu3/cio2.h +++ b/src/libcamera/pipeline/ipu3/cio2.h @@ -13,6 +13,7 @@ #include +#include "libcamera/internal/v4l2_event.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" @@ -58,7 +59,7 @@ public: FrameBuffer *queueBuffer(FrameBuffer *rawBuffer); void tryReturnBuffer(FrameBuffer *buffer); Signal &bufferReady() { return output_->bufferReady; } - Signal &frameStart() { return csi2_->frameStart; } + Signal> &eventReady() { return csi2_->eventReady; } Signal<> bufferAvailable; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 14cab9e5559b809328e1a408368c906b72e6c027..63aa2aab20d3917b4acdaf508123a46e7fc1bec0 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -63,7 +63,7 @@ public: void statBufferReady(FrameBuffer *buffer); void queuePendingRequests(); void cancelPendingRequests(); - void frameStart(uint32_t sequence); + void handleEvent(std::shared_ptr event); CIO2Device cio2_; ImgUDevice *imgu_; @@ -1089,8 +1089,8 @@ int PipelineHandlerIPU3::registerCameras() data->delayedCtrls_ = std::make_unique(cio2->sensor()->device(), params); - data->cio2_.frameStart().connect(data.get(), - &IPU3CameraData::frameStart); + data->cio2_.eventReady().connect(data.get(), + &IPU3CameraData::handleEvent); /* Convert the sensor rotation to a transformation */ const auto &rotation = data->properties_.get(properties::Rotation); @@ -1313,7 +1313,7 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer) * Record the sensor's timestamp in the request metadata. * * \todo The sensor timestamp should be better estimated by connecting - * to the V4L2Device::frameStart signal. + * to the V4L2Device::eventReady signal for FrameSync events. */ request->_d()->metadata().set(controls::SensorTimestamp, buffer->metadata().timestamp); @@ -1382,9 +1382,14 @@ void IPU3CameraData::statBufferReady(FrameBuffer *buffer) * TestPatternMode one. Other controls are handled through the delayed * controls class. */ -void IPU3CameraData::frameStart(uint32_t sequence) + +void IPU3CameraData::handleEvent(std::shared_ptr event) { - delayedCtrls_->applyControls(sequence); + if (event->type() != V4L2Event::Type::FrameSync) + return; + + auto frameSyncEvent = static_cast(event.get()); + delayedCtrls_->applyControls(frameSyncEvent->sequence()); if (processingRequests_.empty()) return; diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 73a03373c8334ea41478d18047c90c97aeb52c2e..627882f194447912cbed7d97e20bd8baec0e982a 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -42,6 +42,7 @@ #include "libcamera/internal/media_device.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" +#include "libcamera/internal/v4l2_event.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" @@ -198,6 +199,8 @@ public: PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const; Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const; + void handleEvent(std::shared_ptr event); + Stream frStream_; Stream dsStream_; @@ -451,6 +454,15 @@ Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &si return bestSize; } +void MaliC55CameraData::handleEvent(std::shared_ptr event) +{ + if (event->type() != V4L2Event::Type::FrameSync) + return; + + auto frameSyncEvent = static_cast(event.get()); + delayedCtrls_->applyControls(frameSyncEvent->sequence()); +} + int MaliC55CameraData::loadIPA() { int ret; @@ -1849,8 +1861,7 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink) V4L2Subdevice *sensorSubdev = in->sensor_->device(); data->delayedCtrls_ = std::make_unique(sensorSubdev, params); - isp_->frameStart.connect(data->delayedCtrls_.get(), - &DelayedControls::applyControls); + isp_->eventReady.connect(data.get(), &MaliC55CameraData::handleEvent); /* \todo Init properties. */ @@ -1913,8 +1924,7 @@ bool PipelineHandlerMaliC55::registerMemoryInputCamera(MediaLink *link) data->delayedCtrls_ = std::make_unique(sensor->device(), params); - isp_->frameStart.connect(data->delayedCtrls_.get(), - &DelayedControls::applyControls); + isp_->eventReady.connect(data.get(), &MaliC55CameraData::handleEvent); ivc_->bufferReady.connect(mem->cru_.get(), &RZG2LCRU::returnBuffer); diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 96382c93a4273297e439891d9cea0287fd84fdad..53ec0007cc95c5ab2da0a5d8a86b5a3330d55f99 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -44,6 +44,7 @@ #include "libcamera/internal/media_pipeline.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" +#include "libcamera/internal/v4l2_event.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" #include "libcamera/internal/yaml_parser.h" @@ -106,6 +107,8 @@ public: const PipelineHandlerRkISP1 *pipe() const; int loadIPA(unsigned int hwRevision, uint32_t supportedBlocks); + void handleEvent(std::shared_ptr event); + Stream mainPathStream_; Stream selfPathStream_; std::unique_ptr sensor_; @@ -430,6 +433,15 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks) return 0; } +void RkISP1CameraData::handleEvent(std::shared_ptr event) +{ + if (event->type() != V4L2Event::Type::FrameSync) + return; + + auto frameSyncEvent = static_cast(event.get()); + delayedCtrls_->applyControls(frameSyncEvent->sequence()); +} + int RkISP1CameraData::loadTuningFile(const std::string &path) { int ret; @@ -1472,8 +1484,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor) data->delayedCtrls_ = std::make_unique(data->sensor_->device(), params); - isp_->frameStart.connect(data->delayedCtrls_.get(), - &DelayedControls::applyControls); + isp_->eventReady.connect(data.get(), &RkISP1CameraData::handleEvent); uint32_t supportedBlocks = kDefaultExtParamsBlocks; diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 71f3f26a4ffeb933176665677231549269782369..c6b51e8c335635788cdc99b7a33e37df7de8a1cb 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -20,6 +20,7 @@ #include #include "libcamera/internal/camera_lens.h" +#include "libcamera/internal/v4l2_event.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/yaml_parser.h" @@ -873,7 +874,7 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr &camera /* Setup the general IPA signal handlers. */ data->frontendDevice()->dequeueTimeout.connect(data, &RPi::CameraData::cameraTimeout); - data->frontendDevice()->frameStart.connect(data, &RPi::CameraData::frameStarted); + data->frontendDevice()->eventReady.connect(data, &RPi::CameraData::handleEvent); data->ipa_->setDelayedControls.connect(data, &CameraData::setDelayedControls); data->ipa_->setLensControls.connect(data, &CameraData::setLensControls); data->ipa_->metadataReady.connect(data, &CameraData::metadataReady); @@ -1392,12 +1393,17 @@ void CameraData::cameraTimeout() clearIncompleteRequests(); } -void CameraData::frameStarted(uint32_t sequence) +void CameraData::handleEvent(std::shared_ptr event) { - LOG(RPI, Debug) << "Frame start " << sequence; + if (event->type() != V4L2Event::Type::FrameSync) + return; + + auto frameSyncEvent = static_cast(event.get()); + + LOG(RPI, Debug) << "Frame start " << frameSyncEvent->sequence(); /* Write any controls for the next frame as soon as we can. */ - delayedCtrls_->applyControls(sequence); + delayedCtrls_->applyControls(frameSyncEvent->sequence()); } void CameraData::clearIncompleteRequests() diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index ca12a4875faf4f1b8b8ad1d637a20414857fe2ff..eeee0d4414b819effb97c1d3d12771e50012be6a 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -25,6 +25,7 @@ #include "libcamera/internal/media_object.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" +#include "libcamera/internal/v4l2_event.h" #include "libcamera/internal/v4l2_videodevice.h" #include "libcamera/internal/value_node.h" @@ -86,7 +87,7 @@ public: virtual void platformSetIspCrop(unsigned int index, const Rectangle &ispCrop) = 0; void cameraTimeout(); - void frameStarted(uint32_t sequence); + void handleEvent(std::shared_ptr event); void clearIncompleteRequests(); void handleStreamBuffer(FrameBuffer *buffer, Stream *stream); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 4d70d50ce50bb1b94e4c30c3fecfb78af678e592..af3caf74ff56d4e6cc45ad098a93e9fde45449c2 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -299,6 +299,8 @@ public: return stream - &streams_.front(); } + void handleEvent(std::shared_ptr event); + struct Entity { /* The media entity, always valid. */ MediaEntity *entity; @@ -985,6 +987,15 @@ void SimpleCameraData::clearIncompleteRequests() } } +void SimpleCameraData::handleEvent(std::shared_ptr event) +{ + if (event->type() != V4L2Event::Type::FrameSync) + return; + + auto frameSyncEvent = static_cast(event.get()); + delayedCtrls_->applyControls(frameSyncEvent->sequence()); +} + void SimpleCameraData::tryCompleteRequest(Request *request) { if (request->hasPendingBuffers()) @@ -1673,8 +1684,8 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL stop(camera); return ret; } - frameStartEmitter->frameStart.connect(data->delayedCtrls_.get(), - &DelayedControls::applyControls); + frameStartEmitter->eventReady.connect(data, + &SimpleCameraData::handleEvent); } ret = video->streamOn(); @@ -1713,8 +1724,8 @@ void SimplePipelineHandler::stopDevice(Camera *camera) if (frameStartEmitter) { frameStartEmitter->setFrameStartEnabled(false); - frameStartEmitter->frameStart.disconnect(data->delayedCtrls_.get(), - &DelayedControls::applyControls); + frameStartEmitter->eventReady.connect(data, + &SimpleCameraData::handleEvent); } if (data->useConversion_) { diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 654472286d4645ff96a404a09ff8de382a49678c..f231bbb17d8528aca274388b8dcaccc0dc339ad3 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -503,7 +503,7 @@ bool V4L2Device::supportsEvents(V4L2EventSubscription &sub) * \param[in] enable True to enable frame start events, false to disable them * * This function enables or disables generation of frame start events. Once - * enabled, the events are signalled through the frameStart signal. + * enabled, the events are signalled through the eventReady signal. * * \return 0 on success, a negative error code otherwise */ @@ -528,8 +528,8 @@ int V4L2Device::setFrameStartEnabled(bool enable) } /** - * \var V4L2Device::frameStart - * \brief A Signal emitted when capture of a frame has started + * \var V4L2Device::eventReady + * \brief A Signal emitted when a V4L2 event is received */ /** @@ -867,15 +867,15 @@ void V4L2Device::eventAvailable() return; } - if (event.type != V4L2_EVENT_FRAME_SYNC) { + auto v4l2Event = V4L2Event::createEvent(&event); + if (!v4l2Event) { LOG(V4L2, Error) - << "Spurious event (" << event.type - << "), disabling event notifier"; + << "Invalid V4L2 Event type, disabling event notifier"; fdEventNotifier_->setEnabled(false); return; } - frameStart.emit(event.u.frame_sync.frame_sequence); + eventReady.emit(v4l2Event); } static const std::map v4l2ToColorSpace = {