diff options
-rw-r--r-- | event_loop.cpp | 99 | ||||
-rw-r--r-- | event_loop.h | 45 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | simple-cam.cpp | 35 |
4 files changed, 168 insertions, 13 deletions
diff --git a/event_loop.cpp b/event_loop.cpp new file mode 100644 index 0000000..7b6ea65 --- /dev/null +++ b/event_loop.cpp @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * event_loop.cpp - Event loop based on cam + */ + +#include "event_loop.h" + +#include <assert.h> +#include <event2/event.h> +#include <event2/thread.h> + +EventLoop *EventLoop::instance_ = nullptr; + +EventLoop::EventLoop() +{ + assert(!instance_); + + evthread_use_pthreads(); + event_ = event_base_new(); + instance_ = this; +} + +EventLoop::~EventLoop() +{ + instance_ = nullptr; + + event_base_free(event_); + libevent_global_shutdown(); +} + +int EventLoop::exec() +{ + exitCode_ = -1; + exit_.store(false, std::memory_order_release); + + while (!exit_.load(std::memory_order_acquire)) { + dispatchCalls(); + event_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY); + } + + return exitCode_; +} + +void EventLoop::exit(int code) +{ + exitCode_ = code; + exit_.store(true, std::memory_order_release); + interrupt(); +} + +void EventLoop::interrupt() +{ + event_base_loopbreak(event_); +} + + +void EventLoop::timeoutTriggered(int fd, short event, void *arg) +{ + EventLoop *self = static_cast<EventLoop *>(arg); + self->exit(); +} + +void EventLoop::timeout(unsigned int sec) +{ + struct event *ev; + struct timeval tv; + + tv.tv_sec = sec; + tv.tv_usec = 0; + ev = evtimer_new(event_, &timeoutTriggered, this); + evtimer_add(ev, &tv); +} + +void EventLoop::callLater(const std::function<void()> &func) +{ + { + std::unique_lock<std::mutex> locker(lock_); + calls_.push_back(func); + } + + interrupt(); +} + +void EventLoop::dispatchCalls() +{ + std::unique_lock<std::mutex> locker(lock_); + + for (auto iter = calls_.begin(); iter != calls_.end(); ) { + std::function<void()> call = std::move(*iter); + + iter = calls_.erase(iter); + + locker.unlock(); + call(); + locker.lock(); + } +} diff --git a/event_loop.h b/event_loop.h new file mode 100644 index 0000000..003c3d8 --- /dev/null +++ b/event_loop.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * event_loop.h - Event loop based on cam's + */ +#ifndef __SIMPLE_CAM_EVENT_LOOP_H__ +#define __SIMPLE_CAM_EVENT_LOOP_H__ + +#include <atomic> +#include <functional> +#include <list> +#include <mutex> + +struct event_base; + +class EventLoop +{ +public: + EventLoop(); + ~EventLoop(); + + void exit(int code = 0); + int exec(); + + void timeout(unsigned int sec); + void callLater(const std::function<void()> &func); + +private: + static EventLoop *instance_; + + static void timeoutTriggered(int fd, short event, void *arg); + + struct event_base *event_; + std::atomic<bool> exit_; + int exitCode_; + + std::list<std::function<void()>> calls_; + std::mutex lock_; + + void interrupt(); + void dispatchCalls(); +}; + +#endif /* __SIMPLE_CAM_EVENT_LOOP_H__ */ diff --git a/meson.build b/meson.build index c312f2c..4d580c2 100644 --- a/meson.build +++ b/meson.build @@ -8,12 +8,14 @@ project('simple-cam', 'c', 'cpp', # simple-cam.cpp is the fully commented application src_files = files([ 'simple-cam.cpp', + 'event_loop.cpp', ]) # Point your PKG_CONFIG_PATH environment variable to the # libcamera install path camera.pc file ($prefix/lib/pkgconfig/camera.pc) libcamera_deps = [ dependency('camera', required : true), + dependency('libevent_pthreads'), ] cpp_arguments = [ '-Wno-unused-parameter', ] diff --git a/simple-cam.cpp b/simple-cam.cpp index bfe30d7..6d1d84f 100644 --- a/simple-cam.cpp +++ b/simple-cam.cpp @@ -11,8 +11,13 @@ #include <libcamera/libcamera.h> +#include "event_loop.h" + +#define TIMEOUT_SEC 3 + using namespace libcamera; std::shared_ptr<Camera> camera; +EventLoop loop; /* * -------------------------------------------------------------------- @@ -21,13 +26,26 @@ std::shared_ptr<Camera> camera; * For each Camera::requestCompleted Signal emitted from the Camera the * connected Slot is invoked. * + * The Slot is invoked in the CameraManager's thread, hence one should avoid + * any heavy processing here. The processing of the request shall be re-directed + * to the application's thread instead, so as not to block the CameraManager's + * thread for large amount of time. + * * The Slot receives the Request as a parameter. */ + +static void processRequest(Request *request); + static void requestComplete(Request *request) { if (request->status() == Request::RequestCancelled) return; + loop.callLater(std::bind(&processRequest, request)); +} + +static void processRequest(Request *request) +{ const Request::BufferMap &buffers = request->buffers(); for (auto bufferPair : buffers) { @@ -320,20 +338,11 @@ int main() * * In order to dispatch events received from the video devices, such * as buffer completions, an event loop has to be run. - * - * Libcamera provides its own default event dispatcher realized by - * polling a set of file descriptors, but applications can integrate - * their own even loop with the Libcamera EventDispatcher. - * - * Here, as an example, run the poll-based EventDispatcher for 3 - * seconds. */ - EventDispatcher *dispatcher = cm->eventDispatcher(); - Timer timer; - timer.start(3000); - while (timer.isRunning()) - dispatcher->processEvents(); - + loop.timeout(TIMEOUT_SEC); + int ret = loop.exec(); + std::cout << "Capture ran for " << TIMEOUT_SEC << " seconds and " + << "stopped with exit status: " << ret << std::endl; /* * -------------------------------------------------------------------- * Clean Up |