summaryrefslogtreecommitdiff
path: root/src/cam
diff options
context:
space:
mode:
Diffstat (limited to 'src/cam')
-rw-r--r--src/cam/buffer_writer.h2
-rw-r--r--src/cam/capture.cpp68
-rw-r--r--src/cam/capture.h9
-rw-r--r--src/cam/event_loop.cpp67
-rw-r--r--src/cam/event_loop.h27
-rw-r--r--src/cam/main.cpp62
-rw-r--r--src/cam/main.h1
-rw-r--r--src/cam/meson.build15
-rw-r--r--src/cam/options.cpp17
-rw-r--r--src/cam/options.h2
-rw-r--r--src/cam/stream_options.cpp2
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);