diff options
-rw-r--r-- | src/py/libcamera/meson.build | 1 | ||||
-rw-r--r-- | src/py/libcamera/py_camera_manager.cpp | 127 | ||||
-rw-r--r-- | src/py/libcamera/py_camera_manager.h | 44 | ||||
-rw-r--r-- | src/py/libcamera/py_main.cpp | 120 |
4 files changed, 198 insertions, 94 deletions
diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index 99c2a8c0..af19ffdd 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -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', diff --git a/src/py/libcamera/py_camera_manager.cpp b/src/py/libcamera/py_camera_manager.cpp new file mode 100644 index 00000000..228fb212 --- /dev/null +++ b/src/py/libcamera/py_camera_manager.cpp @@ -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; +} diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h new file mode 100644 index 00000000..9c15f814 --- /dev/null +++ b/src/py/libcamera/py_camera_manager.h @@ -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(); +}; diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index e652837f..7e613a3f 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -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; }) |