[libcamera-devel,v4,15/15] py: Hack for different event enable mechanism
diff mbox series

Message ID 20230309142601.70556-16-tomi.valkeinen@ideasonboard.com
State Superseded
Headers show
Series
  • py: New python bindings event handling
Related show

Commit Message

Tomi Valkeinen March 9, 2023, 2:26 p.m. UTC
Instead of connecting all the event signals automatically (except
bufferCompletedEvent), add properties to Camera object to enable/disable
the events. E.g.

cam.requestCompletedEnabled = True

would enable the delivery of request completed events.

Also, as (I think) almost everyone wants request completed events,
subscribe to them automatically for all cameras when creating the
PyCameraManager singleton. If someone doesn't want request completed
events, they can set the cam.requestCompletedEnabled to False.

This is just a quick hack, a proper implementation would need a bit more
abstraction functionality to the PyCameraManager.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
 src/py/libcamera/py_camera_manager.cpp |   3 -
 src/py/libcamera/py_camera_manager.h   |   5 +-
 src/py/libcamera/py_main.cpp           | 113 ++++++++++++++++++++-----
 3 files changed, 96 insertions(+), 25 deletions(-)

Comments

Tomi Valkeinen March 9, 2023, 3:40 p.m. UTC | #1
On 09/03/2023 16:26, Tomi Valkeinen wrote:
> Instead of connecting all the event signals automatically (except
> bufferCompletedEvent), add properties to Camera object to enable/disable
> the events. E.g.
> 
> cam.requestCompletedEnabled = True
> 
> would enable the delivery of request completed events.
> 
> Also, as (I think) almost everyone wants request completed events,
> subscribe to them automatically for all cameras when creating the
> PyCameraManager singleton. If someone doesn't want request completed
> events, they can set the cam.requestCompletedEnabled to False.
> 
> This is just a quick hack, a proper implementation would need a bit more
> abstraction functionality to the PyCameraManager.

Also, the Camera Manager events (camera added/removed) are still always 
connected. We could add similar properties to enable/disable those.

  Tomi

> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
>   src/py/libcamera/py_camera_manager.cpp |   3 -
>   src/py/libcamera/py_camera_manager.h   |   5 +-
>   src/py/libcamera/py_main.cpp           | 113 ++++++++++++++++++++-----
>   3 files changed, 96 insertions(+), 25 deletions(-)
> 
> diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp
> index c3b9646f..ca0f6f7a 100644
> --- a/src/py/libcamera/py_camera_manager.cpp
> +++ b/src/py/libcamera/py_camera_manager.cpp
> @@ -182,9 +182,6 @@ std::vector<PyCameraEvent> PyCameraManager::getPyCameraEvents(std::shared_ptr<Ca
>   /* Note: Called from another thread */
>   void PyCameraManager::handleBufferCompleted(std::shared_ptr<Camera> cam, Request *req, FrameBuffer *fb)
>   {
> -	if (!bufferCompletedEventActive_)
> -		return;
> -
>   	CameraEvent ev(CameraEventType::BufferCompleted, cam, req, fb);
>   
>   	pushEvent(ev);
> diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h
> index 648d78af..41105de7 100644
> --- a/src/py/libcamera/py_camera_manager.h
> +++ b/src/py/libcamera/py_camera_manager.h
> @@ -77,10 +77,11 @@ public:
>   	void handleCameraAdded(std::shared_ptr<Camera> cam);
>   	void handleCameraRemoved(std::shared_ptr<Camera> cam);
>   
> -	bool bufferCompletedEventActive_ = false;
> +	uint32_t event_mask_;
> +	std::map<std::shared_ptr<Camera>, uint32_t> camera_event_masks_;
> +	std::unique_ptr<CameraManager> cameraManager_;
>   
>   private:
> -	std::unique_ptr<CameraManager> cameraManager_;
>   
>   	UniqueFD eventFd_;
>   	libcamera::Mutex eventsMutex_;
> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> index cb7088b1..54b58366 100644
> --- a/src/py/libcamera/py_main.cpp
> +++ b/src/py/libcamera/py_main.cpp
> @@ -48,6 +48,57 @@ void init_py_geometry(py::module &m);
>   void init_py_properties_generated(py::module &m);
>   void init_py_transform(py::module &m);
>   
> +static bool py_camera_get_event_flag(std::shared_ptr<Camera> camera, CameraEventType event_type)
> +{
> +	const uint32_t evbit = 1 << (uint32_t)event_type;
> +
> +	auto cm = gCameraManager.lock();
> +	ASSERT(cm);
> +
> +	auto it = cm->camera_event_masks_.find(camera);
> +
> +	uint32_t mask = 0;
> +
> +	if (it != cm->camera_event_masks_.end())
> +		mask = it->second;
> +
> +	return !!(mask & evbit);
> +}
> +
> +static void py_camera_set_event_flag(std::shared_ptr<Camera> camera, CameraEventType event_type, bool value)
> +{
> +	const uint32_t evbit = 1 << (uint32_t)event_type;
> +
> +	auto cm = gCameraManager.lock();
> +	ASSERT(cm);
> +
> +	uint32_t mask = 0;
> +
> +	auto it = cm->camera_event_masks_.find(camera);
> +	if (it != cm->camera_event_masks_.end())
> +		mask = it->second;
> +
> +	bool old_val = !!(mask & evbit);
> +
> +	if (old_val == value)
> +		return;
> +
> +	if (value)
> +		mask |= evbit;
> +	else
> +		mask &= ~evbit;
> +
> +	cm->camera_event_masks_[camera] = mask;
> +
> +	if (value) {
> +		camera->requestCompleted.connect(camera.get(), [cm, camera](Request *req) {
> +			cm->handleRequestCompleted(camera, req);
> +		});
> +	} else {
> +		camera->requestCompleted.disconnect();
> +	}
> +}
> +
>   PYBIND11_MODULE(_libcamera, m)
>   {
>   	init_py_enums(m);
> @@ -121,6 +172,10 @@ PYBIND11_MODULE(_libcamera, m)
>   			if (!cm) {
>   				cm = std::make_shared<PyCameraManager>();
>   				gCameraManager = cm;
> +
> +				/* Always enable RequestCompleted for all cameras */
> +				for (auto cam : cm->cameraManager_->cameras())
> +					py_camera_set_event_flag(cam, CameraEventType::RequestCompleted, true);
>   			}
>   
>   			return cm;
> @@ -132,12 +187,47 @@ PYBIND11_MODULE(_libcamera, m)
>   
>   		.def_property_readonly("event_fd", &PyCameraManager::eventFd)
>   
> -		.def("get_events", &PyCameraManager::getPyEvents)
> -
> -		.def_readwrite("buffer_completed_active", &PyCameraManager::bufferCompletedEventActive_);
> +		.def("get_events", &PyCameraManager::getPyEvents);
>   
>   	pyCamera
>   		.def_property_readonly("id", &Camera::id)
> +
> +		.def_property(
> +			"requestCompletedEnabled",
> +			[](Camera &self) {
> +				return py_camera_get_event_flag(self.shared_from_this(),
> +								CameraEventType::RequestCompleted);
> +			},
> +			[](Camera &self, bool val) {
> +				py_camera_set_event_flag(self.shared_from_this(),
> +							 CameraEventType::RequestCompleted,
> +							 val);
> +			})
> +
> +		.def_property(
> +			"bufferCompletedEnabled",
> +			[](Camera &self) {
> +				return py_camera_get_event_flag(self.shared_from_this(),
> +								CameraEventType::BufferCompleted);
> +			},
> +			[](Camera &self, bool val) {
> +				py_camera_set_event_flag(self.shared_from_this(),
> +							 CameraEventType::BufferCompleted,
> +							 val);
> +			})
> +
> +		.def_property(
> +			"disconnectEnabled",
> +			[](Camera &self) {
> +				return py_camera_get_event_flag(self.shared_from_this(),
> +								CameraEventType::Disconnect);
> +			},
> +			[](Camera &self, bool val) {
> +				py_camera_set_event_flag(self.shared_from_this(),
> +							 CameraEventType::Disconnect,
> +							 val);
> +			})
> +
>   		.def("acquire", [](Camera &self) {
>   			int ret = self.acquire();
>   			if (ret)
> @@ -157,18 +247,6 @@ PYBIND11_MODULE(_libcamera, m)
>   			auto cm = gCameraManager.lock();
>   			ASSERT(cm);
>   
> -			self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) {
> -				cm->handleRequestCompleted(camera, req);
> -			});
> -
> -			self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) {
> -				cm->handleBufferCompleted(camera, req, fb);
> -			});
> -
> -			self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() {
> -				cm->handleDisconnected(camera);
> -			});
> -
>   			ControlList controlList(self.controls());
>   
>   			for (const auto& [id, obj]: controls) {
> @@ -178,7 +256,6 @@ PYBIND11_MODULE(_libcamera, m)
>   
>   			int ret = self.start(&controlList);
>   			if (ret) {
> -				self.requestCompleted.disconnect();
>   				throw std::system_error(-ret, std::generic_category(),
>   							"Failed to start camera");
>   			}
> @@ -187,10 +264,6 @@ PYBIND11_MODULE(_libcamera, m)
>   		.def("stop", [](Camera &self) {
>   			int ret = self.stop();
>   
> -			self.requestCompleted.disconnect();
> -			self.bufferCompleted.disconnect();
> -			self.disconnected.disconnect();
> -
>   			auto cm = gCameraManager.lock();
>   			ASSERT(cm);
>

Patch
diff mbox series

diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp
index c3b9646f..ca0f6f7a 100644
--- a/src/py/libcamera/py_camera_manager.cpp
+++ b/src/py/libcamera/py_camera_manager.cpp
@@ -182,9 +182,6 @@  std::vector<PyCameraEvent> PyCameraManager::getPyCameraEvents(std::shared_ptr<Ca
 /* Note: Called from another thread */
 void PyCameraManager::handleBufferCompleted(std::shared_ptr<Camera> cam, Request *req, FrameBuffer *fb)
 {
-	if (!bufferCompletedEventActive_)
-		return;
-
 	CameraEvent ev(CameraEventType::BufferCompleted, cam, req, fb);
 
 	pushEvent(ev);
diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h
index 648d78af..41105de7 100644
--- a/src/py/libcamera/py_camera_manager.h
+++ b/src/py/libcamera/py_camera_manager.h
@@ -77,10 +77,11 @@  public:
 	void handleCameraAdded(std::shared_ptr<Camera> cam);
 	void handleCameraRemoved(std::shared_ptr<Camera> cam);
 
-	bool bufferCompletedEventActive_ = false;
+	uint32_t event_mask_;
+	std::map<std::shared_ptr<Camera>, uint32_t> camera_event_masks_;
+	std::unique_ptr<CameraManager> cameraManager_;
 
 private:
-	std::unique_ptr<CameraManager> cameraManager_;
 
 	UniqueFD eventFd_;
 	libcamera::Mutex eventsMutex_;
diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
index cb7088b1..54b58366 100644
--- a/src/py/libcamera/py_main.cpp
+++ b/src/py/libcamera/py_main.cpp
@@ -48,6 +48,57 @@  void init_py_geometry(py::module &m);
 void init_py_properties_generated(py::module &m);
 void init_py_transform(py::module &m);
 
+static bool py_camera_get_event_flag(std::shared_ptr<Camera> camera, CameraEventType event_type)
+{
+	const uint32_t evbit = 1 << (uint32_t)event_type;
+
+	auto cm = gCameraManager.lock();
+	ASSERT(cm);
+
+	auto it = cm->camera_event_masks_.find(camera);
+
+	uint32_t mask = 0;
+
+	if (it != cm->camera_event_masks_.end())
+		mask = it->second;
+
+	return !!(mask & evbit);
+}
+
+static void py_camera_set_event_flag(std::shared_ptr<Camera> camera, CameraEventType event_type, bool value)
+{
+	const uint32_t evbit = 1 << (uint32_t)event_type;
+
+	auto cm = gCameraManager.lock();
+	ASSERT(cm);
+
+	uint32_t mask = 0;
+
+	auto it = cm->camera_event_masks_.find(camera);
+	if (it != cm->camera_event_masks_.end())
+		mask = it->second;
+
+	bool old_val = !!(mask & evbit);
+
+	if (old_val == value)
+		return;
+
+	if (value)
+		mask |= evbit;
+	else
+		mask &= ~evbit;
+
+	cm->camera_event_masks_[camera] = mask;
+
+	if (value) {
+		camera->requestCompleted.connect(camera.get(), [cm, camera](Request *req) {
+			cm->handleRequestCompleted(camera, req);
+		});
+	} else {
+		camera->requestCompleted.disconnect();
+	}
+}
+
 PYBIND11_MODULE(_libcamera, m)
 {
 	init_py_enums(m);
@@ -121,6 +172,10 @@  PYBIND11_MODULE(_libcamera, m)
 			if (!cm) {
 				cm = std::make_shared<PyCameraManager>();
 				gCameraManager = cm;
+
+				/* Always enable RequestCompleted for all cameras */
+				for (auto cam : cm->cameraManager_->cameras())
+					py_camera_set_event_flag(cam, CameraEventType::RequestCompleted, true);
 			}
 
 			return cm;
@@ -132,12 +187,47 @@  PYBIND11_MODULE(_libcamera, m)
 
 		.def_property_readonly("event_fd", &PyCameraManager::eventFd)
 
-		.def("get_events", &PyCameraManager::getPyEvents)
-
-		.def_readwrite("buffer_completed_active", &PyCameraManager::bufferCompletedEventActive_);
+		.def("get_events", &PyCameraManager::getPyEvents);
 
 	pyCamera
 		.def_property_readonly("id", &Camera::id)
+
+		.def_property(
+			"requestCompletedEnabled",
+			[](Camera &self) {
+				return py_camera_get_event_flag(self.shared_from_this(),
+								CameraEventType::RequestCompleted);
+			},
+			[](Camera &self, bool val) {
+				py_camera_set_event_flag(self.shared_from_this(),
+							 CameraEventType::RequestCompleted,
+							 val);
+			})
+
+		.def_property(
+			"bufferCompletedEnabled",
+			[](Camera &self) {
+				return py_camera_get_event_flag(self.shared_from_this(),
+								CameraEventType::BufferCompleted);
+			},
+			[](Camera &self, bool val) {
+				py_camera_set_event_flag(self.shared_from_this(),
+							 CameraEventType::BufferCompleted,
+							 val);
+			})
+
+		.def_property(
+			"disconnectEnabled",
+			[](Camera &self) {
+				return py_camera_get_event_flag(self.shared_from_this(),
+								CameraEventType::Disconnect);
+			},
+			[](Camera &self, bool val) {
+				py_camera_set_event_flag(self.shared_from_this(),
+							 CameraEventType::Disconnect,
+							 val);
+			})
+
 		.def("acquire", [](Camera &self) {
 			int ret = self.acquire();
 			if (ret)
@@ -157,18 +247,6 @@  PYBIND11_MODULE(_libcamera, m)
 			auto cm = gCameraManager.lock();
 			ASSERT(cm);
 
-			self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) {
-				cm->handleRequestCompleted(camera, req);
-			});
-
-			self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) {
-				cm->handleBufferCompleted(camera, req, fb);
-			});
-
-			self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() {
-				cm->handleDisconnected(camera);
-			});
-
 			ControlList controlList(self.controls());
 
 			for (const auto& [id, obj]: controls) {
@@ -178,7 +256,6 @@  PYBIND11_MODULE(_libcamera, m)
 
 			int ret = self.start(&controlList);
 			if (ret) {
-				self.requestCompleted.disconnect();
 				throw std::system_error(-ret, std::generic_category(),
 							"Failed to start camera");
 			}
@@ -187,10 +264,6 @@  PYBIND11_MODULE(_libcamera, m)
 		.def("stop", [](Camera &self) {
 			int ret = self.stop();
 
-			self.requestCompleted.disconnect();
-			self.bufferCompleted.disconnect();
-			self.disconnected.disconnect();
-
 			auto cm = gCameraManager.lock();
 			ASSERT(cm);