[libcamera-devel,v2,13/14] py: Discard/Dispatch Request events on camera.stop()
diff mbox series

Message ID 20220629070416.17550-14-tomi.valkeinen@ideasonboard.com
State Superseded
Headers show
Series
  • Python bindings event handling
Related show

Commit Message

Tomi Valkeinen June 29, 2022, 7:04 a.m. UTC
To prevent old Request related events from being left in the event
queue, messing up the next camera.start(), force the events to be either
discarded or dispatched when camera.stop() is called.

camera.stop() will discard all Request events (RequestCompleted and
BufferCompleted) for that camera. camera.stop(dispatch_events=True) will
instead dispatch all events, also for other cameras.

The dispatch version dispatches all events instead of just the Request
events for one camera so that the event ordering stays the same.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
 src/py/libcamera/py_camera_manager.cpp | 30 ++++++++++++++++++++++++++
 src/py/libcamera/py_camera_manager.h   |  1 +
 src/py/libcamera/py_main.cpp           | 12 +++++++++--
 3 files changed, 41 insertions(+), 2 deletions(-)

Patch
diff mbox series

diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp
index 599a9f7e..dfd43a09 100644
--- a/src/py/libcamera/py_camera_manager.cpp
+++ b/src/py/libcamera/py_camera_manager.cpp
@@ -218,6 +218,36 @@  void PyCameraManager::discardEvents()
 	}
 }
 
+void PyCameraManager::discardRequests(std::shared_ptr<Camera> camera)
+{
+	MutexLocker guard(cameraEventsMutex_);
+
+	size_t oldSize = cameraEvents_.size();
+
+	for (const auto &ev : cameraEvents_) {
+		if (ev.type_ != CameraEvent::Type::RequestCompleted)
+			continue;
+
+		if (ev.camera_ != camera)
+			continue;
+
+		/* Decrease the ref increased in Camera.queue_request() */
+		py::object o = py::cast(ev.request_);
+		o.dec_ref();
+	}
+
+	cameraEvents_.erase(std::remove_if(cameraEvents_.begin(), cameraEvents_.end(),
+		[&camera](const CameraEvent &ev) {
+			return ev.camera_ == camera &&
+				(ev.type_ == CameraEvent::Type::RequestCompleted ||
+				 ev.type_ == CameraEvent::Type::BufferCompleted);
+		}),
+		cameraEvents_.end());
+
+	LOG(Python, Debug) << "Discarded " << oldSize - cameraEvents_.size()
+			   << " request events";
+}
+
 std::function<void(std::shared_ptr<Camera>)> PyCameraManager::getCameraAdded() const
 {
 	return cameraAddedHandler_;
diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h
index aa51a6bc..9c8d6ca8 100644
--- a/src/py/libcamera/py_camera_manager.h
+++ b/src/py/libcamera/py_camera_manager.h
@@ -38,6 +38,7 @@  public:
 
 	void dispatchEvents();
 	void discardEvents();
+	void discardRequests(std::shared_ptr<Camera> camera);
 
 	std::function<void(std::shared_ptr<Camera>)> getCameraAdded() const;
 	void setCameraAdded(std::function<void(std::shared_ptr<Camera>)> func);
diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
index a07f06c4..c4755bea 100644
--- a/src/py/libcamera/py_main.cpp
+++ b/src/py/libcamera/py_main.cpp
@@ -168,16 +168,24 @@  PYBIND11_MODULE(_libcamera, m)
 			}
 		}, py::arg("controls") = std::unordered_map<const ControlId *, py::object>())
 
-		.def("stop", [](Camera &self) {
+		.def("stop", [](Camera &self, bool dispatchEvents) {
 			int ret = self.stop();
 
 			self.requestCompleted.disconnect();
 
+			auto cm = gCameraManager.lock();
+			ASSERT(cm);
+
+			if (dispatchEvents)
+				cm->dispatchEvents();
+			else
+				cm->discardRequests(self.shared_from_this());
+
 			/* \todo Should we just ignore the error? */
 			if (ret)
 				throw std::system_error(-ret, std::generic_category(),
 				                        "Failed to start camera");
-		})
+		}, py::arg("dispatch_events") = false)
 
 		.def_property("request_completed",
 		[](Camera &self) {