diff options
Diffstat (limited to 'src/cam')
-rw-r--r-- | src/cam/buffer_writer.h | 2 | ||||
-rw-r--r-- | src/cam/capture.cpp | 68 | ||||
-rw-r--r-- | src/cam/capture.h | 9 | ||||
-rw-r--r-- | src/cam/event_loop.cpp | 67 | ||||
-rw-r--r-- | src/cam/event_loop.h | 27 | ||||
-rw-r--r-- | src/cam/main.cpp | 62 | ||||
-rw-r--r-- | src/cam/main.h | 1 | ||||
-rw-r--r-- | src/cam/meson.build | 15 | ||||
-rw-r--r-- | src/cam/options.cpp | 17 | ||||
-rw-r--r-- | src/cam/options.h | 2 | ||||
-rw-r--r-- | src/cam/stream_options.cpp | 2 |
11 files changed, 204 insertions, 68 deletions
diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h index 47e26103..604ce870 100644 --- a/src/cam/buffer_writer.h +++ b/src/cam/buffer_writer.h @@ -10,7 +10,7 @@ #include <map> #include <string> -#include <libcamera/buffer.h> +#include <libcamera/framebuffer.h> class BufferWriter { diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index 5510c009..3c3e3a53 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -10,6 +10,8 @@ #include <limits.h> #include <sstream> +#include <libcamera/control_ids.h> + #include "capture.h" #include "main.h" @@ -17,8 +19,9 @@ using namespace libcamera; Capture::Capture(std::shared_ptr<Camera> camera, CameraConfiguration *config, EventLoop *loop) - : camera_(camera), config_(config), writer_(nullptr), loop_(loop), - captureCount_(0), captureLimit_(0) + : camera_(camera), config_(config), writer_(nullptr), last_(0), loop_(loop), + queueCount_(0), captureCount_(0), captureLimit_(0), + printMetadata_(false) { } @@ -26,8 +29,10 @@ int Capture::run(const OptionsParser::Options &options) { int ret; + queueCount_ = 0; captureCount_ = 0; captureLimit_ = options[OptCapture].toInteger(); + printMetadata_ = options.isSet(OptMetadata); if (!camera_) { std::cout << "Can't capture without a camera" << std::endl; @@ -65,6 +70,8 @@ int Capture::run(const OptionsParser::Options &options) writer_ = nullptr; } + requests_.clear(); + delete allocator; return ret; @@ -92,9 +99,8 @@ int Capture::capture(FrameBufferAllocator *allocator) * example pushing a button. For now run all streams all the time. */ - std::vector<Request *> requests; for (unsigned int i = 0; i < nbuffers; i++) { - Request *request = camera_->createRequest(); + std::unique_ptr<Request> request = camera_->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; return -ENOMEM; @@ -117,7 +123,7 @@ int Capture::capture(FrameBufferAllocator *allocator) writer_->mapBuffer(buffer.get()); } - requests.push_back(request); + requests_.push_back(std::move(request)); } ret = camera_->start(); @@ -126,8 +132,8 @@ int Capture::capture(FrameBufferAllocator *allocator) return ret; } - for (Request *request : requests) { - ret = camera_->queueRequest(request); + for (std::unique_ptr<Request> &request : requests_) { + ret = queueRequest(request.get()); if (ret < 0) { std::cerr << "Can't queue request" << std::endl; camera_->stop(); @@ -151,11 +157,30 @@ int Capture::capture(FrameBufferAllocator *allocator) return ret; } +int Capture::queueRequest(Request *request) +{ + if (captureLimit_ && queueCount_ >= captureLimit_) + return 0; + + queueCount_++; + + return camera_->queueRequest(request); +} + void Capture::requestComplete(Request *request) { if (request->status() == Request::RequestCancelled) return; + /* + * Defer processing of the completed request to the event loop, to avoid + * blocking the camera manager thread. + */ + loop_->callLater([=]() { processRequest(request); }); +} + +void Capture::processRequest(Request *request) +{ const Request::BufferMap &buffers = request->buffers(); /* @@ -196,28 +221,21 @@ void Capture::requestComplete(Request *request) std::cout << info.str() << std::endl; + if (printMetadata_) { + const ControlList &requestMetadata = request->metadata(); + for (const auto &ctrl : requestMetadata) { + const ControlId *id = controls::controls.at(ctrl.first); + std::cout << "\t" << id->name() << " = " + << ctrl.second.toString() << std::endl; + } + } + captureCount_++; if (captureLimit_ && captureCount_ >= captureLimit_) { loop_->exit(0); return; } - /* - * Create a new request and populate it with one buffer for each - * stream. - */ - request = camera_->createRequest(); - if (!request) { - std::cerr << "Can't create request" << std::endl; - return; - } - - for (auto it = buffers.begin(); it != buffers.end(); ++it) { - const Stream *stream = it->first; - FrameBuffer *buffer = it->second; - - request->addBuffer(stream, buffer); - } - - camera_->queueRequest(request); + request->reuse(Request::ReuseBuffers); + queueRequest(request); } diff --git a/src/cam/capture.h b/src/cam/capture.h index 0aebdac9..de478c98 100644 --- a/src/cam/capture.h +++ b/src/cam/capture.h @@ -9,9 +9,10 @@ #include <memory> #include <stdint.h> +#include <vector> -#include <libcamera/buffer.h> #include <libcamera/camera.h> +#include <libcamera/framebuffer.h> #include <libcamera/framebuffer_allocator.h> #include <libcamera/request.h> #include <libcamera/stream.h> @@ -31,7 +32,9 @@ public: private: int capture(libcamera::FrameBufferAllocator *allocator); + int queueRequest(libcamera::Request *request); void requestComplete(libcamera::Request *request); + void processRequest(libcamera::Request *request); std::shared_ptr<libcamera::Camera> camera_; libcamera::CameraConfiguration *config_; @@ -41,8 +44,12 @@ private: uint64_t last_; EventLoop *loop_; + unsigned int queueCount_; unsigned int captureCount_; unsigned int captureLimit_; + bool printMetadata_; + + std::vector<std::unique_ptr<libcamera::Request>> requests_; }; #endif /* __CAM_CAPTURE_H__ */ diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp index e8ab8617..6a4c47f2 100644 --- a/src/cam/event_loop.cpp +++ b/src/cam/event_loop.cpp @@ -5,35 +5,78 @@ * event_loop.cpp - cam - Event loop */ -#include <libcamera/event_dispatcher.h> - #include "event_loop.h" -using namespace libcamera; +#include <assert.h> +#include <event2/event.h> +#include <event2/thread.h> + +EventLoop *EventLoop::instance_ = nullptr; -EventLoop::EventLoop(EventDispatcher *dispatcher) - : dispatcher_(dispatcher) +EventLoop::EventLoop() { + assert(!instance_); + + evthread_use_pthreads(); + base_ = event_base_new(); + instance_ = this; } EventLoop::~EventLoop() { + instance_ = nullptr; + + event_base_free(base_); + libevent_global_shutdown(); +} + +EventLoop *EventLoop::instance() +{ + return instance_; } int EventLoop::exec() { exitCode_ = -1; - exit_.store(false, std::memory_order_release); - - while (!exit_.load(std::memory_order_acquire)) - dispatcher_->processEvents(); - + event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY); return exitCode_; } void EventLoop::exit(int code) { exitCode_ = code; - exit_.store(true, std::memory_order_release); - dispatcher_->interrupt(); + event_base_loopbreak(base_); +} + +void EventLoop::callLater(const std::function<void()> &func) +{ + { + std::unique_lock<std::mutex> locker(lock_); + calls_.push_back(func); + } + + event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr); +} + +void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd, + [[maybe_unused]] short flags, void *param) +{ + EventLoop *loop = static_cast<EventLoop *>(param); + loop->dispatchCall(); +} + +void EventLoop::dispatchCall() +{ + std::function<void()> call; + + { + std::unique_lock<std::mutex> locker(lock_); + if (calls_.empty()) + return; + + call = calls_.front(); + calls_.pop_front(); + } + + call(); } diff --git a/src/cam/event_loop.h b/src/cam/event_loop.h index 581c7cba..ba3ba3a4 100644 --- a/src/cam/event_loop.h +++ b/src/cam/event_loop.h @@ -7,28 +7,39 @@ #ifndef __CAM_EVENT_LOOP_H__ #define __CAM_EVENT_LOOP_H__ -#include <atomic> +#include <functional> +#include <list> +#include <mutex> -#include <libcamera/event_notifier.h> +#include <event2/util.h> -namespace libcamera { -class EventDispatcher; -} +struct event_base; class EventLoop { public: - EventLoop(libcamera::EventDispatcher *dispatcher); + EventLoop(); ~EventLoop(); + static EventLoop *instance(); + int exec(); void exit(int code = 0); + void callLater(const std::function<void()> &func); + private: - libcamera::EventDispatcher *dispatcher_; + static EventLoop *instance_; - std::atomic<bool> exit_; + struct event_base *base_; int exitCode_; + + std::list<std::function<void()>> calls_; + std::mutex lock_; + + static void dispatchCallback(evutil_socket_t fd, short flags, + void *param); + void dispatchCall(); }; #endif /* __CAM_EVENT_LOOP_H__ */ diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 244720b4..70e9f62c 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -45,12 +45,14 @@ private: int infoConfiguration(); int run(); + std::string const cameraName(const Camera *camera); + static CamApp *app_; OptionsParser::Options options_; CameraManager *cm_; std::shared_ptr<Camera> camera_; std::unique_ptr<libcamera::CameraConfiguration> config_; - EventLoop *loop_; + EventLoop loop_; bool strictFormats_; }; @@ -58,7 +60,7 @@ private: CamApp *CamApp::app_ = nullptr; CamApp::CamApp() - : cm_(nullptr), camera_(nullptr), config_(nullptr), loop_(nullptr), + : cm_(nullptr), camera_(nullptr), config_(nullptr), strictFormats_(false) { CamApp::app_ = this; @@ -132,16 +134,11 @@ int CamApp::init(int argc, char **argv) std::cout << "Monitoring new hotplug and unplug events" << std::endl; } - loop_ = new EventLoop(cm_->eventDispatcher()); - return 0; } void CamApp::cleanup() { - delete loop_; - loop_ = nullptr; - if (camera_) { camera_->release(); camera_.reset(); @@ -164,8 +161,7 @@ int CamApp::exec() void CamApp::quit() { - if (loop_) - loop_->exit(); + loop_.exit(); } int CamApp::parseOptions(int argc, char *argv[]) @@ -201,6 +197,9 @@ int CamApp::parseOptions(int argc, char *argv[]) parser.addOption(OptStrictFormats, OptionNone, "Do not allow requested stream format(s) to be adjusted", "strict-formats"); + parser.addOption(OptMetadata, OptionNone, + "Print the metadata for completed requests", + "metadata"); options_ = parser.parse(argc, argv); if (!options_.valid()) @@ -340,7 +339,7 @@ int CamApp::run() unsigned int index = 1; for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { - std::cout << index << ": " << cam->id() << std::endl; + std::cout << index << ": " << cameraName(cam.get()) << std::endl; index++; } } @@ -364,13 +363,13 @@ int CamApp::run() } if (options_.isSet(OptCapture)) { - Capture capture(camera_, config_.get(), loop_); + Capture capture(camera_, config_.get(), &loop_); return capture.run(options_); } if (options_.isSet(OptMonitor)) { std::cout << "Press Ctrl-C to interrupt" << std::endl; - ret = loop_->exec(); + ret = loop_.exec(); if (ret) std::cout << "Failed to run monitor loop" << std::endl; } @@ -378,6 +377,45 @@ int CamApp::run() return 0; } +std::string const CamApp::cameraName(const Camera *camera) +{ + const ControlList &props = camera->properties(); + bool addModel = true; + std::string name; + + /* + * Construct the name from the camera location, model and ID. The model + * is only used if the location isn't present or is set to External. + */ + if (props.contains(properties::Location)) { + switch (props.get(properties::Location)) { + case properties::CameraLocationFront: + addModel = false; + name = "Internal front camera "; + break; + case properties::CameraLocationBack: + addModel = false; + name = "Internal back camera "; + break; + case properties::CameraLocationExternal: + name = "External camera "; + break; + } + } + + if (addModel && props.contains(properties::Model)) { + /* + * If the camera location is not availble use the camera model + * to build the camera name. + */ + name = "'" + props.get(properties::Model) + "' "; + } + + name += "(" + camera->id() + ")"; + + return name; +} + void signalHandler([[maybe_unused]] int signal) { std::cout << "Exiting" << std::endl; diff --git a/src/cam/main.h b/src/cam/main.h index ea8dfd33..d22451f5 100644 --- a/src/cam/main.h +++ b/src/cam/main.h @@ -19,6 +19,7 @@ enum { OptStream = 's', OptListControls = 256, OptStrictFormats = 257, + OptMetadata = 258, }; #endif /* __CAM_MAIN_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build index 89e124fb..6234ed0a 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -1,5 +1,14 @@ # SPDX-License-Identifier: CC0-1.0 +libevent = dependency('libevent_pthreads', required : get_option('cam')) + +if not libevent.found() + cam_enabled = false + subdir_done() +endif + +cam_enabled = true + cam_sources = files([ 'buffer_writer.cpp', 'capture.cpp', @@ -10,5 +19,9 @@ cam_sources = files([ ]) cam = executable('cam', cam_sources, - dependencies : [ libatomic, libcamera_dep ], + dependencies : [ + libatomic, + libcamera_public, + libevent, + ], install : true) diff --git a/src/cam/options.cpp b/src/cam/options.cpp index 77b3cc1f..417c3ab4 100644 --- a/src/cam/options.cpp +++ b/src/cam/options.cpp @@ -61,7 +61,12 @@ bool OptionsBase<T>::isSet(const T &opt) const template<typename T> const OptionValue &OptionsBase<T>::operator[](const T &opt) const { - return values_.find(opt)->second; + static const OptionValue empty; + + auto it = values_.find(opt); + if (it != values_.end()) + return it->second; + return empty; } template<typename T> @@ -72,7 +77,7 @@ void OptionsBase<T>::invalidate() template<typename T> bool OptionsBase<T>::parseValue(const T &opt, const Option &option, - const char *optarg) + const char *arg) { OptionValue value; @@ -83,9 +88,9 @@ bool OptionsBase<T>::parseValue(const T &opt, const Option &option, case OptionInteger: unsigned int integer; - if (optarg) { + if (arg) { char *endptr; - integer = strtoul(optarg, &endptr, 0); + integer = strtoul(arg, &endptr, 0); if (*endptr != '\0') return false; } else { @@ -96,12 +101,12 @@ bool OptionsBase<T>::parseValue(const T &opt, const Option &option, break; case OptionString: - value = OptionValue(optarg ? optarg : ""); + value = OptionValue(arg ? arg : ""); break; case OptionKeyValue: KeyValueParser *kvParser = option.keyValueParser; - KeyValueParser::Options keyValues = kvParser->parse(optarg); + KeyValueParser::Options keyValues = kvParser->parse(arg); if (!keyValues.valid()) return false; diff --git a/src/cam/options.h b/src/cam/options.h index 18486619..f02eeca2 100644 --- a/src/cam/options.h +++ b/src/cam/options.h @@ -73,7 +73,7 @@ public: { }; - virtual ~KeyValueParser() {} + virtual ~KeyValueParser() = default; bool addOption(const char *name, OptionType type, const char *help, OptionArgument argument = ArgumentNone); diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp index 27cc3912..c58272c2 100644 --- a/src/cam/stream_options.cpp +++ b/src/cam/stream_options.cpp @@ -13,7 +13,7 @@ using namespace libcamera; StreamKeyValueParser::StreamKeyValueParser() { addOption("role", OptionString, - "Role for the stream (viewfinder, video, still, stillraw)", + "Role for the stream (viewfinder, video, still, raw)", ArgumentRequired); addOption("width", OptionInteger, "Width in pixels", ArgumentRequired); |