@@ -13,6 +13,7 @@ pybind11_proj = subproject('pybind11')
pybind11_dep = pybind11_proj.get_variable('pybind11_dep')
pycamera_sources = files([
+ 'py_camera_manager.cpp',
'py_enums.cpp',
'py_geometry.cpp',
'py_helpers.cpp',
new file mode 100644
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+ */
+
+#include "py_camera_manager.h"
+
+#include <errno.h>
+#include <memory>
+#include <sys/eventfd.h>
+#include <system_error>
+#include <unistd.h>
+#include <vector>
+
+#include "py_main.h"
+
+namespace py = pybind11;
+
+using namespace libcamera;
+
+PyCameraManager::PyCameraManager()
+{
+ LOG(Python, Debug) << "PyCameraManager()";
+
+ cameraManager_ = std::make_unique<CameraManager>();
+
+ int fd = eventfd(0, 0);
+ if (fd == -1)
+ throw std::system_error(errno, std::generic_category(),
+ "Failed to create eventfd");
+
+ eventFd_ = fd;
+
+ int ret = cameraManager_->start();
+ if (ret) {
+ close(fd);
+ eventFd_ = -1;
+ throw std::system_error(-ret, std::generic_category(),
+ "Failed to start CameraManager");
+ }
+}
+
+PyCameraManager::~PyCameraManager()
+{
+ LOG(Python, Debug) << "~PyCameraManager()";
+
+ if (eventFd_ != -1) {
+ close(eventFd_);
+ eventFd_ = -1;
+ }
+}
+
+py::list PyCameraManager::cameras()
+{
+ /*
+ * Create a list of Cameras, where each camera has a keep-alive to
+ * CameraManager.
+ */
+ py::list l;
+
+ for (auto &camera : cameraManager_->cameras()) {
+ py::object py_cm = py::cast(this);
+ py::object py_cam = py::cast(camera);
+ py::detail::keep_alive_impl(py_cam, py_cm);
+ l.append(py_cam);
+ }
+
+ return l;
+}
+
+std::vector<py::object> PyCameraManager::getReadyRequests()
+{
+ readFd();
+
+ 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);
+ }
+
+ return py_reqs;
+}
+
+/* Note: Called from another thread */
+void PyCameraManager::handleRequestCompleted(Request *req)
+{
+ pushRequest(req);
+ writeFd();
+}
+
+void PyCameraManager::writeFd()
+{
+ uint64_t v = 1;
+
+ size_t s = write(eventFd_, &v, 8);
+ /*
+ * We should never fail, and have no simple means to manage the error,
+ * so let's log a fatal error.
+ */
+ if (s != 8)
+ LOG(Python, Fatal) << "Unable to write to eventfd";
+}
+
+void PyCameraManager::readFd()
+{
+ uint8_t buf[8];
+
+ if (read(eventFd_, buf, 8) != 8)
+ throw std::system_error(errno, std::generic_category());
+}
+
+void PyCameraManager::pushRequest(Request *req)
+{
+ std::lock_guard guard(completedRequestsMutex_);
+ completedRequests_.push_back(req);
+}
+
+std::vector<Request *> PyCameraManager::getCompletedRequests()
+{
+ std::vector<Request *> v;
+ std::lock_guard guard(completedRequestsMutex_);
+ swap(v, completedRequests_);
+ return v;
+}
new file mode 100644
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include <libcamera/libcamera.h>
+
+#include <pybind11/smart_holder.h>
+
+using namespace libcamera;
+
+class PyCameraManager
+{
+public:
+ PyCameraManager();
+ ~PyCameraManager();
+
+ pybind11::list cameras();
+ std::shared_ptr<Camera> get(const std::string &name) { return cameraManager_->get(name); }
+
+ static const std::string &version() { return CameraManager::version(); }
+
+ int eventFd() const { return eventFd_; }
+
+ std::vector<pybind11::object> getReadyRequests();
+
+ void handleRequestCompleted(Request *req);
+
+private:
+ std::unique_ptr<CameraManager> cameraManager_;
+
+ int eventFd_ = -1;
+ std::mutex completedRequestsMutex_;
+ std::vector<Request *> completedRequests_;
+
+ void writeFd();
+ void readFd();
+ void pushRequest(Request *req);
+ std::vector<Request *> getCompletedRequests();
+};
@@ -7,10 +7,10 @@
#include "py_main.h"
-#include <mutex>
+#include <memory>
#include <stdexcept>
-#include <sys/eventfd.h>
-#include <unistd.h>
+#include <string>
+#include <vector>
#include <libcamera/base/log.h>
@@ -21,6 +21,7 @@
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
+#include "py_camera_manager.h"
#include "py_helpers.h"
namespace py = pybind11;
@@ -33,27 +34,11 @@ LOG_DEFINE_CATEGORY(Python)
}
-static std::weak_ptr<CameraManager> gCameraManager;
-static int gEventfd;
-static std::mutex gReqlistMutex;
-static std::vector<Request *> gReqList;
-
-static void handleRequestCompleted(Request *req)
-{
- {
- std::lock_guard guard(gReqlistMutex);
- gReqList.push_back(req);
- }
-
- uint64_t v = 1;
- size_t s = write(gEventfd, &v, 8);
- /*
- * We should never fail, and have no simple means to manage the error,
- * so let's log a fatal error.
- */
- if (s != 8)
- LOG(Python, Fatal) << "Unable to write to eventfd";
-}
+/*
+ * Note: global C++ destructors can be ran on this before the py module is
+ * destructed.
+ */
+static std::weak_ptr<PyCameraManager> gCameraManager;
void init_py_enums(py::module &m);
void init_py_controls_generated(py::module &m);
@@ -76,7 +61,7 @@ PYBIND11_MODULE(_libcamera, m)
* https://pybind11.readthedocs.io/en/latest/advanced/misc.html#avoiding-c-types-in-docstrings
*/
- auto pyCameraManager = py::class_<CameraManager>(m, "CameraManager");
+ auto pyCameraManager = py::class_<PyCameraManager>(m, "CameraManager");
auto pyCamera = py::class_<Camera>(m, "Camera");
auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
@@ -110,78 +95,22 @@ PYBIND11_MODULE(_libcamera, m)
/* Classes */
pyCameraManager
.def_static("singleton", []() {
- std::shared_ptr<CameraManager> cm = gCameraManager.lock();
- if (cm)
- return cm;
-
- int fd = eventfd(0, 0);
- if (fd == -1)
- throw std::system_error(errno, std::generic_category(),
- "Failed to create eventfd");
-
- cm = std::shared_ptr<CameraManager>(new CameraManager, [](auto p) {
- close(gEventfd);
- gEventfd = -1;
- delete p;
- });
-
- gEventfd = fd;
- gCameraManager = cm;
-
- int ret = cm->start();
- if (ret)
- throw std::system_error(-ret, std::generic_category(),
- "Failed to start CameraManager");
-
- return cm;
- })
-
- .def_property_readonly("version", &CameraManager::version)
+ std::shared_ptr<PyCameraManager> cm = gCameraManager.lock();
- .def_property_readonly("event_fd", [](CameraManager &) {
- return gEventfd;
- })
-
- .def("get_ready_requests", [](CameraManager &) {
- uint8_t buf[8];
-
- if (read(gEventfd, buf, 8) != 8)
- throw std::system_error(errno, std::generic_category());
-
- std::vector<Request *> v;
-
- {
- std::lock_guard guard(gReqlistMutex);
- swap(v, gReqList);
+ if (!cm) {
+ cm = std::make_shared<PyCameraManager>();
+ gCameraManager = cm;
}
- std::vector<py::object> ret;
-
- for (Request *req : v) {
- py::object o = py::cast(req);
- /* Decrease the ref increased in Camera.queue_request() */
- o.dec_ref();
- ret.push_back(o);
- }
-
- return ret;
+ return cm;
})
- .def("get", py::overload_cast<const std::string &>(&CameraManager::get), py::keep_alive<0, 1>())
-
- /* Create a list of Cameras, where each camera has a keep-alive to CameraManager */
- .def_property_readonly("cameras", [](CameraManager &self) {
- py::list l;
-
- for (auto &c : self.cameras()) {
- py::object py_cm = py::cast(self);
- py::object py_cam = py::cast(c);
- py::detail::keep_alive_impl(py_cam, py_cm);
- l.append(py_cam);
- }
+ .def_property_readonly("version", &PyCameraManager::version)
+ .def("get", &PyCameraManager::get, py::keep_alive<0, 1>())
+ .def_property_readonly("cameras", &PyCameraManager::cameras)
- return l;
- });
+ .def_property_readonly("event_fd", &PyCameraManager::eventFd)
+ .def("get_ready_requests", &PyCameraManager::getReadyRequests);
pyCamera
.def_property_readonly("id", &Camera::id)
@@ -191,7 +120,10 @@ PYBIND11_MODULE(_libcamera, m)
const std::unordered_map<const ControlId *, py::object> &controls) {
/* \todo What happens if someone calls start() multiple times? */
- self.requestCompleted.connect(handleRequestCompleted);
+ auto cm = gCameraManager.lock();
+ ASSERT(cm);
+
+ self.requestCompleted.connect(cm.get(), &PyCameraManager::handleRequestCompleted);
ControlList controlList(self.controls());
@@ -202,7 +134,7 @@ PYBIND11_MODULE(_libcamera, m)
int ret = self.start(&controlList);
if (ret) {
- self.requestCompleted.disconnect(handleRequestCompleted);
+ self.requestCompleted.disconnect();
return ret;
}
@@ -214,7 +146,7 @@ PYBIND11_MODULE(_libcamera, m)
if (ret)
return ret;
- self.requestCompleted.disconnect(handleRequestCompleted);
+ self.requestCompleted.disconnect();
return 0;
})