[3/4] libcamera: v4l2_device: Emit signals for V4L2Events generically
diff mbox series

Message ID 20260925-v4l2-events-v1-3-b2ec212eb5d0@ideasonboard.com
State New
Headers show
Series
  • Add generic V4L2 Events support
Related show

Commit Message

Daniel Scally Sept. 25, 2026, 3:16 p.m. UTC
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<V4L2Event> 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<V4L2Event>.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
With this change, in V4L2Device::eventAvailable() I am creating a
std::shared_ptr<V4L2Event> 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(-)

Patch
diff mbox series

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<uint32_t> frameStart;
+	Signal<std::shared_ptr<V4L2Event>> 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 <libcamera/base/signal.h>
 
+#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<FrameBuffer *> &bufferReady() { return output_->bufferReady; }
-	Signal<uint32_t> &frameStart() { return csi2_->frameStart; }
+	Signal<std::shared_ptr<V4L2Event>> &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<V4L2Event> event);
 
 	CIO2Device cio2_;
 	ImgUDevice *imgu_;
@@ -1089,8 +1089,8 @@  int PipelineHandlerIPU3::registerCameras()
 		data->delayedCtrls_ =
 			std::make_unique<DelayedControls>(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<V4L2Event> event)
 {
-	delayedCtrls_->applyControls(sequence);
+	if (event->type() != V4L2Event::Type::FrameSync)
+		return;
+
+	auto frameSyncEvent = static_cast<V4L2FrameSyncEvent *>(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<V4L2Event> 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<V4L2Event> event)
+{
+	if (event->type() != V4L2Event::Type::FrameSync)
+		return;
+
+	auto frameSyncEvent = static_cast<V4L2FrameSyncEvent *>(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<DelayedControls>(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<DelayedControls>(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<V4L2Event> event);
+
 	Stream mainPathStream_;
 	Stream selfPathStream_;
 	std::unique_ptr<CameraSensor> sensor_;
@@ -430,6 +433,15 @@  int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks)
 	return 0;
 }
 
+void RkISP1CameraData::handleEvent(std::shared_ptr<V4L2Event> event)
+{
+	if (event->type() != V4L2Event::Type::FrameSync)
+		return;
+
+	auto frameSyncEvent = static_cast<V4L2FrameSyncEvent *>(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<DelayedControls>(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 <libcamera/property_ids.h>
 
 #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<RPi::CameraData> &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<V4L2Event> event)
 {
-	LOG(RPI, Debug) << "Frame start " << sequence;
+	if (event->type() != V4L2Event::Type::FrameSync)
+		return;
+
+	auto frameSyncEvent = static_cast<V4L2FrameSyncEvent *>(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<V4L2Event> 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<V4L2Event> event);
+
 	struct Entity {
 		/* The media entity, always valid. */
 		MediaEntity *entity;
@@ -985,6 +987,15 @@  void SimpleCameraData::clearIncompleteRequests()
 	}
 }
 
+void SimpleCameraData::handleEvent(std::shared_ptr<V4L2Event> event)
+{
+	if (event->type() != V4L2Event::Type::FrameSync)
+		return;
+
+	auto frameSyncEvent = static_cast<V4L2FrameSyncEvent *>(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<uint32_t, ColorSpace> v4l2ToColorSpace = {