@@ -58,6 +58,7 @@ py::list PyCameraManager::cameras()
return l;
}
+/* DEPRECATED */
std::vector<py::object> PyCameraManager::getReadyRequests()
{
int ret = readFd();
@@ -70,21 +71,234 @@ std::vector<py::object> PyCameraManager::getReadyRequests()
std::vector<py::object> py_reqs;
- for (Request *request : getCompletedRequests()) {
- py::object o = py::cast(request);
- /* Decrease the ref increased in Camera.queue_request() */
- o.dec_ref();
- py_reqs.push_back(o);
+ for (const auto &ev : getEvents()) {
+ switch (ev.type_) {
+ case CameraEvent::Type::RequestCompleted: {
+ py::object o = py::cast(ev.request_);
+ /* Decrease the ref increased in Camera.queue_request() */
+ o.dec_ref();
+ py_reqs.push_back(o);
+ }
+ default:
+ /* ignore */
+ break;
+ }
}
return py_reqs;
}
/* Note: Called from another thread */
-void PyCameraManager::handleRequestCompleted(Request *req)
+void PyCameraManager::handleBufferCompleted(std::shared_ptr<Camera> cam, Request *req, FrameBuffer *fb)
{
- pushRequest(req);
- writeFd();
+ CameraEvent ev(CameraEvent::Type::BufferCompleted, cam);
+ ev.request_ = req;
+ ev.fb_ = fb;
+
+ pushEvent(ev);
+}
+
+/* Note: Called from another thread */
+void PyCameraManager::handleRequestCompleted(std::shared_ptr<Camera> cam, Request *req)
+{
+ CameraEvent ev(CameraEvent::Type::RequestCompleted, cam);
+ ev.request_ = req;
+
+ pushEvent(ev);
+}
+
+/* Note: Called from another thread */
+void PyCameraManager::handleDisconnected(std::shared_ptr<Camera> cam)
+{
+ CameraEvent ev(CameraEvent::Type::Disconnect, cam);
+
+ pushEvent(ev);
+}
+
+/* Note: Called from another thread */
+void PyCameraManager::handleCameraAdded(std::shared_ptr<Camera> cam)
+{
+ CameraEvent ev(CameraEvent::Type::CameraAdded, cam);
+
+ pushEvent(ev);
+}
+
+/* Note: Called from another thread */
+void PyCameraManager::handleCameraRemoved(std::shared_ptr<Camera> cam)
+{
+ CameraEvent ev(CameraEvent::Type::CameraRemoved, cam);
+
+ pushEvent(ev);
+}
+
+void PyCameraManager::dispatchEvents()
+{
+ int ret = readFd();
+
+ if (ret == EAGAIN) {
+ LOG(Python, Debug) << "No events to dispatch";
+ return;
+ }
+
+ if (ret != 0)
+ throw std::system_error(ret, std::generic_category());
+
+ std::vector<CameraEvent> events = getEvents();
+
+ LOG(Python, Debug) << "Dispatch " << events.size() << " events";
+
+ for (const auto &event : events) {
+ std::shared_ptr<Camera> camera = event.camera_;
+
+ switch (event.type_) {
+ case CameraEvent::Type::CameraAdded: {
+ if (cameraAddedHandler_)
+ cameraAddedHandler_(camera);
+
+ break;
+ }
+ case CameraEvent::Type::CameraRemoved: {
+ if (cameraRemovedHandler_)
+ cameraRemovedHandler_(camera);
+
+ break;
+ }
+ case CameraEvent::Type::BufferCompleted: {
+ auto cb = getBufferCompleted(camera.get());
+ if (cb)
+ cb(camera, event.request_, event.fb_);
+
+ break;
+ }
+ case CameraEvent::Type::RequestCompleted: {
+ auto cb = getRequestCompleted(camera.get());
+ if (cb)
+ cb(camera, event.request_);
+
+ /* Decrease the ref increased in Camera.queue_request() */
+ py::object o = py::cast(event.request_);
+ o.dec_ref();
+
+ break;
+ }
+ case CameraEvent::Type::Disconnect: {
+ auto cb = getDisconnected(camera.get());
+ if (cb)
+ cb(camera);
+
+ break;
+ }
+ default:
+ ASSERT(false);
+ }
+ }
+}
+
+void PyCameraManager::discardEvents()
+{
+ int ret = readFd();
+
+ if (ret == EAGAIN)
+ return;
+
+ if (ret != 0)
+ throw std::system_error(ret, std::generic_category());
+
+ std::vector<CameraEvent> v = getEvents();
+
+ LOG(Python, Debug) << "Discard " << v.size() << " events";
+
+ for (const auto &ev : v) {
+ if (ev.type_ != CameraEvent::Type::RequestCompleted)
+ continue;
+
+ /* Decrease the ref increased in Camera.queue_request() */
+ py::object o = py::cast(ev.request_);
+ o.dec_ref();
+ }
+}
+
+std::function<void(std::shared_ptr<Camera>)> PyCameraManager::getCameraAdded() const
+{
+ return cameraAddedHandler_;
+}
+
+void PyCameraManager::setCameraAdded(std::function<void(std::shared_ptr<Camera>)> func)
+{
+ if (cameraAddedHandler_)
+ cameraManager_->cameraAdded.disconnect();
+
+ cameraAddedHandler_ = func;
+
+ if (func)
+ cameraManager_->cameraAdded.connect(this, &PyCameraManager::handleCameraAdded);
+}
+
+std::function<void(std::shared_ptr<Camera>)> PyCameraManager::getCameraRemoved() const
+{
+ return cameraRemovedHandler_;
+}
+
+void PyCameraManager::setCameraRemoved(std::function<void(std::shared_ptr<Camera>)> func)
+{
+ if (cameraRemovedHandler_)
+ cameraManager_->cameraRemoved.disconnect();
+
+ cameraRemovedHandler_ = func;
+
+ if (func)
+ cameraManager_->cameraRemoved.connect(this, &PyCameraManager::handleCameraRemoved);
+}
+
+std::function<void(std::shared_ptr<Camera>, Request *)> PyCameraManager::getRequestCompleted(Camera *cam)
+{
+ if (auto it = cameraRequestCompletedHandlers_.find(cam);
+ it != cameraRequestCompletedHandlers_.end())
+ return it->second;
+
+ return nullptr;
+}
+
+void PyCameraManager::setRequestCompleted(Camera *cam, std::function<void(std::shared_ptr<Camera>, Request *)> func)
+{
+ if (func)
+ cameraRequestCompletedHandlers_[cam] = func;
+ else
+ cameraRequestCompletedHandlers_.erase(cam);
+}
+
+std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> PyCameraManager::getBufferCompleted(Camera *cam)
+{
+ if (auto it = cameraBufferCompletedHandlers_.find(cam);
+ it != cameraBufferCompletedHandlers_.end())
+ return it->second;
+
+ return nullptr;
+}
+
+void PyCameraManager::setBufferCompleted(Camera *cam, std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> func)
+{
+ if (func)
+ cameraBufferCompletedHandlers_[cam] = func;
+ else
+ cameraBufferCompletedHandlers_.erase(cam);
+}
+
+std::function<void(std::shared_ptr<Camera>)> PyCameraManager::getDisconnected(Camera *cam)
+{
+ if (auto it = cameraDisconnectHandlers_.find(cam);
+ it != cameraDisconnectHandlers_.end())
+ return it->second;
+
+ return nullptr;
+}
+
+void PyCameraManager::setDisconnected(Camera *cam, std::function<void(std::shared_ptr<Camera>)> func)
+{
+ if (func)
+ cameraDisconnectHandlers_[cam] = func;
+ else
+ cameraDisconnectHandlers_.erase(cam);
}
void PyCameraManager::writeFd()
@@ -110,16 +324,22 @@ int PyCameraManager::readFd()
return 0;
}
-void PyCameraManager::pushRequest(Request *req)
+void PyCameraManager::pushEvent(const CameraEvent &ev)
{
- MutexLocker guard(completedRequestsMutex_);
- completedRequests_.push_back(req);
+ MutexLocker guard(cameraEventsMutex_);
+ cameraEvents_.push_back(ev);
+
+ writeFd();
+
+ LOG(Python, Debug) << "Queued events: " << cameraEvents_.size();
}
-std::vector<Request *> PyCameraManager::getCompletedRequests()
+std::vector<PyCameraManager::CameraEvent> PyCameraManager::getEvents()
{
- std::vector<Request *> v;
- MutexLocker guard(completedRequestsMutex_);
- swap(v, completedRequests_);
+ std::vector<CameraEvent> v;
+
+ MutexLocker guard(cameraEventsMutex_);
+ swap(v, cameraEvents_);
+
return v;
}
@@ -30,16 +30,70 @@ public:
void handleRequestCompleted(Request *req);
+ void handleBufferCompleted(std::shared_ptr<Camera> cam, Request *req, FrameBuffer *fb);
+ void handleRequestCompleted(std::shared_ptr<Camera> cam, Request *req);
+ void handleDisconnected(std::shared_ptr<Camera> cam);
+ void handleCameraAdded(std::shared_ptr<Camera> cam);
+ void handleCameraRemoved(std::shared_ptr<Camera> cam);
+
+ void dispatchEvents();
+ void discardEvents();
+
+ std::function<void(std::shared_ptr<Camera>)> getCameraAdded() const;
+ void setCameraAdded(std::function<void(std::shared_ptr<Camera>)> func);
+
+ std::function<void(std::shared_ptr<Camera>)> getCameraRemoved() const;
+ void setCameraRemoved(std::function<void(std::shared_ptr<Camera>)> func);
+
+ std::function<void(std::shared_ptr<Camera>, Request *)> getRequestCompleted(Camera *cam);
+ void setRequestCompleted(Camera *cam, std::function<void(std::shared_ptr<Camera>, Request *)> func);
+
+ std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> getBufferCompleted(Camera *cam);
+ void setBufferCompleted(Camera *cam, std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> func);
+
+ std::function<void(std::shared_ptr<Camera>)> getDisconnected(Camera *cam);
+ void setDisconnected(Camera *cam, std::function<void(std::shared_ptr<Camera>)> func);
+
private:
+ struct CameraEvent {
+ enum class Type {
+ Undefined = 0,
+ CameraAdded,
+ CameraRemoved,
+ Disconnect,
+ RequestCompleted,
+ BufferCompleted,
+ };
+
+ CameraEvent(Type type, std::shared_ptr<Camera> camera)
+ : type_(type), camera_(camera)
+ {
+ }
+
+ Type type_;
+
+ std::shared_ptr<Camera> camera_;
+
+ Request *request_ = nullptr;
+ FrameBuffer *fb_ = nullptr;
+ };
+
std::unique_ptr<CameraManager> cameraManager_;
UniqueFD eventFd_;
- libcamera::Mutex completedRequestsMutex_;
- std::vector<Request *> completedRequests_
- LIBCAMERA_TSA_GUARDED_BY(completedRequestsMutex_);
+ libcamera::Mutex cameraEventsMutex_;
+ std::vector<CameraEvent> cameraEvents_
+ LIBCAMERA_TSA_GUARDED_BY(cameraEvents_);
+
+ std::function<void(std::shared_ptr<Camera>)> cameraAddedHandler_;
+ std::function<void(std::shared_ptr<Camera>)> cameraRemovedHandler_;
+
+ std::map<Camera *, std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)>> cameraBufferCompletedHandlers_;
+ std::map<Camera *, std::function<void(std::shared_ptr<Camera>, Request *)>> cameraRequestCompletedHandlers_;
+ std::map<Camera *, std::function<void(std::shared_ptr<Camera>)>> cameraDisconnectHandlers_;
void writeFd();
int readFd();
- void pushRequest(Request *req);
- std::vector<Request *> getCompletedRequests();
+ void pushEvent(const CameraEvent &ev);
+ std::vector<CameraEvent> getEvents();
};
@@ -107,7 +107,20 @@ PYBIND11_MODULE(_libcamera, m)
.def_property_readonly("cameras", &PyCameraManager::cameras)
.def_property_readonly("event_fd", &PyCameraManager::eventFd)
- .def("get_ready_requests", &PyCameraManager::getReadyRequests);
+
+ /* DEPRECATED */
+ .def("get_ready_requests", &PyCameraManager::getReadyRequests)
+
+ .def("dispatch_events", &PyCameraManager::dispatchEvents)
+ .def("discard_events", &PyCameraManager::discardEvents)
+
+ .def_property("camera_added",
+ &PyCameraManager::getCameraAdded,
+ &PyCameraManager::setCameraAdded)
+
+ .def_property("camera_removed",
+ &PyCameraManager::getCameraRemoved,
+ &PyCameraManager::setCameraRemoved);
pyCamera
.def_property_readonly("id", &Camera::id)
@@ -131,7 +144,14 @@ PYBIND11_MODULE(_libcamera, m)
auto cm = gCameraManager.lock();
ASSERT(cm);
- self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted);
+ /*
+ * Note: We always subscribe requestComplete, as the bindings
+ * use requestComplete event to decrement the Request refcount-
+ */
+
+ self.requestCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req) {
+ cm->handleRequestCompleted(camera, req);
+ });
ControlList controlList(self.controls());
@@ -159,6 +179,71 @@ PYBIND11_MODULE(_libcamera, m)
"Failed to start camera");
})
+ .def_property("request_completed",
+ [](Camera &self) {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ return cm->getRequestCompleted(&self);
+ },
+ [](Camera &self, std::function<void(std::shared_ptr<Camera>, Request *)> f) {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ cm->setRequestCompleted(&self, f);
+
+ /*
+ * Note: We do not subscribe requestComplete here, as we
+ * do that in the start() method.
+ */
+ })
+
+ .def_property("buffer_completed",
+ [](Camera &self) -> std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ return cm->getBufferCompleted(&self);
+ },
+ [](Camera &self, std::function<void(std::shared_ptr<Camera>, Request *, FrameBuffer *)> f) {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ cm->setBufferCompleted(&self, f);
+
+ self.bufferCompleted.disconnect();
+
+ if (!f)
+ return;
+
+ self.bufferCompleted.connect(&self, [cm, camera=self.shared_from_this()](Request *req, FrameBuffer *fb) {
+ cm->handleBufferCompleted(camera, req, fb);
+ });
+ })
+
+ .def_property("disconnected",
+ [](Camera &self) -> std::function<void(std::shared_ptr<Camera>)> {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ return cm->getDisconnected(&self);
+ },
+ [](Camera &self, std::function<void(std::shared_ptr<Camera>)> f) {
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ cm->setDisconnected(&self, f);
+
+ self.disconnected.disconnect();
+
+ if (!f)
+ return;
+
+ self.disconnected.connect(&self, [cm, camera=self.shared_from_this()]() {
+ cm->handleDisconnected(camera);
+ });
+ })
+
.def("__str__", [](Camera &self) {
return "<libcamera.Camera '" + self.id() + "'>";
})
At the moment the Python bindings only handle the requestCompleted events. But we have others, bufferCompleted and disconnect from the Camera, and cameraAdded and cameraRemoved from the CameraManager. This makes all those events available to the users. The get_ready_requests() method is now deprecated, but available to keep backward compatibility. The new event handling works as follows: The user sets callbacks to the CameraManager or Cameras (e.g. Camera.buffer_completed). When the event_fd informs of an event, the user must call CameraManager.dispatch_events() which gets the queued events and calls the relevant callbacks for each queued event. Additionally there is CameraManager.discard_events() if the user does not want to process the events but wants to clear the event queue (e.g. after stopping the cameras or when exiting the app). I'm not very happy with this patch. It works fine, but there's a lot of repetition of almost-the-same code. Perhaps some template magics might reduce the repetition, but I also fear that it can easily make the code more difficult to read. TODO: Documentation. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> --- src/py/libcamera/py_camera_manager.cpp | 250 +++++++++++++++++++++++-- src/py/libcamera/py_camera_manager.h | 64 ++++++- src/py/libcamera/py_main.cpp | 89 ++++++++- 3 files changed, 381 insertions(+), 22 deletions(-)