[v2,2/3] libcamera: v4l2_device: Emit signals for V4L2Events generically
diff mbox series

Message ID 20261005-v4l2-events-v2-2-e4883190489d@ideasonboard.com
State New
Headers show
Series
  • Add generic V4L2 Events support
Related show

Commit Message

Daniel Scally Oct. 5, 2026, 3:40 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<const v4l2_event &> 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 signature.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
Changes in v2:

	- Reworked to drop V4L2Event class and simply use the kernel
	  uAPI directly for the new Signal.
---
 include/libcamera/internal/v4l2_device.h            |  2 +-
 src/libcamera/pipeline/ipu3/cio2.h                  |  4 +++-
 src/libcamera/pipeline/ipu3/ipu3.cpp                | 18 ++++++++++++------
 src/libcamera/pipeline/mali-c55/mali-c55.cpp        | 18 ++++++++++++++----
 src/libcamera/pipeline/rkisp1/rkisp1.cpp            | 14 ++++++++++++--
 src/libcamera/pipeline/rpi/common/pipeline_base.cpp | 13 +++++++++----
 src/libcamera/pipeline/rpi/common/pipeline_base.h   |  4 +++-
 src/libcamera/pipeline/simple/simple.cpp            | 19 +++++++++++++++----
 src/libcamera/v4l2_device.cpp                       | 16 ++++------------
 9 files changed, 73 insertions(+), 35 deletions(-)

Patch
diff mbox series

diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
index 03e4d3dbb480557de98a5fa16fd41d4f2e7431c5..6b77dd46f654e4b95db0327e73164719c47a77d9 100644
--- a/include/libcamera/internal/v4l2_device.h
+++ b/include/libcamera/internal/v4l2_device.h
@@ -48,7 +48,7 @@  public:
 
 	bool supportsEvents(v4l2_event_subscription &sub);
 	int setFrameStartEnabled(bool enable);
-	Signal<uint32_t> frameStart;
+	Signal<const v4l2_event &> eventReady;
 
 	void updateControlInfo();
 
diff --git a/src/libcamera/pipeline/ipu3/cio2.h b/src/libcamera/pipeline/ipu3/cio2.h
index 91651a16404e2c524b13829b4909474e579ad217..a3165fcc49c571b10fd104c025f7004d3b70341f 100644
--- a/src/libcamera/pipeline/ipu3/cio2.h
+++ b/src/libcamera/pipeline/ipu3/cio2.h
@@ -16,6 +16,8 @@ 
 #include "libcamera/internal/v4l2_subdevice.h"
 #include "libcamera/internal/v4l2_videodevice.h"
 
+struct v4l2_event;
+
 namespace libcamera {
 
 class CameraSensor;
@@ -58,7 +60,7 @@  public:
 	FrameBuffer *queueBuffer(FrameBuffer *rawBuffer);
 	void tryReturnBuffer(FrameBuffer *buffer);
 	Signal<FrameBuffer *> &bufferReady() { return output_->bufferReady; }
-	Signal<uint32_t> &frameStart() { return csi2_->frameStart; }
+	Signal<const v4l2_event &> eventReady() { return csi2_->eventReady; }
 
 	Signal<> bufferAvailable;
 
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 14cab9e5559b809328e1a408368c906b72e6c027..e48fa327055e372f42487ffc09f55441cc8e3871 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -11,6 +11,7 @@ 
 #include <vector>
 
 #include <linux/intel-ipu3.h>
+#include <linux/videodev2.h>
 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
@@ -63,7 +64,7 @@  public:
 	void statBufferReady(FrameBuffer *buffer);
 	void queuePendingRequests();
 	void cancelPendingRequests();
-	void frameStart(uint32_t sequence);
+	void handleEvent(const v4l2_event &event);
 
 	CIO2Device cio2_;
 	ImgUDevice *imgu_;
@@ -1089,8 +1090,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 +1314,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 frame sync events.
 	 */
 	request->_d()->metadata().set(controls::SensorTimestamp,
 				      buffer->metadata().timestamp);
@@ -1382,9 +1383,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(const v4l2_event &event)
 {
-	delayedCtrls_->applyControls(sequence);
+	if (event.type != V4L2_EVENT_FRAME_SYNC)
+		return;
+
+	auto frameSyncEvent = &event.u.frame_sync;
+	delayedCtrls_->applyControls(frameSyncEvent->frame_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..da3153ad7fafc133cdde6accb5e3bd54f80520c9 100644
--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
@@ -17,6 +17,7 @@ 
 #include <linux/media-bus-format.h>
 #include <linux/media.h>
 #include <linux/media/arm/mali-c55-config.h>
+#include <linux/videodev2.h>
 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
@@ -198,6 +199,8 @@  public:
 	PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const;
 	Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const;
 
+	void handleEvent(const v4l2_event &event);
+
 	Stream frStream_;
 	Stream dsStream_;
 
@@ -451,6 +454,15 @@  Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &si
 	return bestSize;
 }
 
+void MaliC55CameraData::handleEvent(const v4l2_event &event)
+{
+	if (event.type != V4L2_EVENT_FRAME_SYNC)
+		return;
+
+	auto frameSyncEvent = &event.u.frame_sync;
+	delayedCtrls_->applyControls(frameSyncEvent->frame_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..e49dd546ee0a5207c75a364564d5673fd8463b30 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -106,6 +106,8 @@  public:
 	const PipelineHandlerRkISP1 *pipe() const;
 	int loadIPA(unsigned int hwRevision, uint32_t supportedBlocks);
 
+	void handleEvent(const v4l2_event &event);
+
 	Stream mainPathStream_;
 	Stream selfPathStream_;
 	std::unique_ptr<CameraSensor> sensor_;
@@ -430,6 +432,15 @@  int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks)
 	return 0;
 }
 
+void RkISP1CameraData::handleEvent(const v4l2_event &event)
+{
+	if (event.type != V4L2_EVENT_FRAME_SYNC)
+		return;
+
+	auto frameSyncEvent = &event.u.frame_sync;
+	delayedCtrls_->applyControls(frameSyncEvent->frame_sequence);
+}
+
 int RkISP1CameraData::loadTuningFile(const std::string &path)
 {
 	int ret;
@@ -1472,8 +1483,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..2315a1c47dc26ed88fa149106a25dcf1ffbc3ec0 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -873,7 +873,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 +1392,17 @@  void CameraData::cameraTimeout()
 	clearIncompleteRequests();
 }
 
-void CameraData::frameStarted(uint32_t sequence)
+void CameraData::handleEvent(const v4l2_event &event)
 {
-	LOG(RPI, Debug) << "Frame start " << sequence;
+	if (event.type != V4L2_EVENT_FRAME_SYNC)
+		return;
+
+	auto frameSyncEvent = &event.u.frame_sync;
+
+	LOG(RPI, Debug) << "Frame start " << frameSyncEvent->frame_sequence;
 
 	/* Write any controls for the next frame as soon as we can. */
-	delayedCtrls_->applyControls(sequence);
+	delayedCtrls_->applyControls(frameSyncEvent->frame_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..f076721c659330150b01ff4079a83a7ac719e9d3 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h
@@ -36,6 +36,8 @@ 
 
 using namespace std::chrono_literals;
 
+struct v4l2_event;
+
 namespace libcamera {
 
 namespace RPi {
@@ -86,7 +88,7 @@  public:
 	virtual void platformSetIspCrop(unsigned int index, const Rectangle &ispCrop) = 0;
 
 	void cameraTimeout();
-	void frameStarted(uint32_t sequence);
+	void handleEvent(const v4l2_event &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 4631d5450b4ac4c47a78a631917d8b226a5269b0..75e068bf516d80af6c8aeeb94bf00ba265744fdd 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(const v4l2_event &event);
+
 	struct Entity {
 		/* The media entity, always valid. */
 		MediaEntity *entity;
@@ -988,6 +990,15 @@  void SimpleCameraData::clearIncompleteRequests()
 	}
 }
 
+void SimpleCameraData::handleEvent(const v4l2_event &event)
+{
+	if (event.type != V4L2_EVENT_FRAME_SYNC)
+		return;
+
+	auto frameSyncEvent = &event.u.frame_sync;
+	delayedCtrls_->applyControls(frameSyncEvent->frame_sequence);
+}
+
 void SimpleCameraData::tryCompleteRequest(Request *request)
 {
 	if (request->hasPendingBuffers())
@@ -1676,8 +1687,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();
@@ -1716,8 +1727,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 225b938649dcfe86f5528c3a82ed1b5ca0c94a5a..192edd802a22d3edc7055132eb84ca00e095dd26 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -493,7 +493,7 @@  bool V4L2Device::supportsEvents(v4l2_event_subscription &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
  */
@@ -518,8 +518,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
  */
 
 /**
@@ -857,15 +857,7 @@  void V4L2Device::eventAvailable()
 		return;
 	}
 
-	if (event.type != V4L2_EVENT_FRAME_SYNC) {
-		LOG(V4L2, Error)
-			<< "Spurious event (" << event.type
-			<< "), disabling event notifier";
-		fdEventNotifier_->setEnabled(false);
-		return;
-	}
-
-	frameStart.emit(event.u.frame_sync.frame_sequence);
+	eventReady.emit(event);
 }
 
 static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = {