From 84ad104499d9efc0253dae1a60ee070ed375ad95 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 20 Oct 2022 00:44:55 +0300 Subject: Move test applications to src/apps/ The cam and qcam test application share code, currently through a crude hack that references the cam source files directly from the qcam meson.build file. To prepare for the introduction of hosting that code in a static library, move all applications to src/apps/. Signed-off-by: Laurent Pinchart Reviewed-by: Paul Elder Reviewed-by: Kieran Bingham --- src/cam/camera_session.cpp | 450 ----------------- src/cam/camera_session.h | 79 --- src/cam/capture-script.yaml | 71 --- src/cam/capture_script.cpp | 535 -------------------- src/cam/capture_script.h | 68 --- src/cam/dng_writer.cpp | 653 ------------------------ src/cam/dng_writer.h | 27 - src/cam/drm.cpp | 717 -------------------------- src/cam/drm.h | 334 ------------- src/cam/event_loop.cpp | 150 ------ src/cam/event_loop.h | 68 --- src/cam/file_sink.cpp | 137 ----- src/cam/file_sink.h | 43 -- src/cam/frame_sink.cpp | 67 --- src/cam/frame_sink.h | 32 -- src/cam/image.cpp | 109 ---- src/cam/image.h | 50 -- src/cam/kms_sink.cpp | 538 -------------------- src/cam/kms_sink.h | 83 --- src/cam/main.cpp | 362 -------------- src/cam/main.h | 26 - src/cam/meson.build | 74 --- src/cam/options.cpp | 1141 ------------------------------------------ src/cam/options.h | 157 ------ src/cam/sdl_sink.cpp | 214 -------- src/cam/sdl_sink.h | 48 -- src/cam/sdl_texture.cpp | 36 -- src/cam/sdl_texture.h | 30 -- src/cam/sdl_texture_mjpg.cpp | 83 --- src/cam/sdl_texture_mjpg.h | 23 - src/cam/sdl_texture_yuv.cpp | 33 -- src/cam/sdl_texture_yuv.h | 26 - src/cam/stream_options.cpp | 134 ----- src/cam/stream_options.h | 28 -- 34 files changed, 6626 deletions(-) delete mode 100644 src/cam/camera_session.cpp delete mode 100644 src/cam/camera_session.h delete mode 100644 src/cam/capture-script.yaml delete mode 100644 src/cam/capture_script.cpp delete mode 100644 src/cam/capture_script.h delete mode 100644 src/cam/dng_writer.cpp delete mode 100644 src/cam/dng_writer.h delete mode 100644 src/cam/drm.cpp delete mode 100644 src/cam/drm.h delete mode 100644 src/cam/event_loop.cpp delete mode 100644 src/cam/event_loop.h delete mode 100644 src/cam/file_sink.cpp delete mode 100644 src/cam/file_sink.h delete mode 100644 src/cam/frame_sink.cpp delete mode 100644 src/cam/frame_sink.h delete mode 100644 src/cam/image.cpp delete mode 100644 src/cam/image.h delete mode 100644 src/cam/kms_sink.cpp delete mode 100644 src/cam/kms_sink.h delete mode 100644 src/cam/main.cpp delete mode 100644 src/cam/main.h delete mode 100644 src/cam/meson.build delete mode 100644 src/cam/options.cpp delete mode 100644 src/cam/options.h delete mode 100644 src/cam/sdl_sink.cpp delete mode 100644 src/cam/sdl_sink.h delete mode 100644 src/cam/sdl_texture.cpp delete mode 100644 src/cam/sdl_texture.h delete mode 100644 src/cam/sdl_texture_mjpg.cpp delete mode 100644 src/cam/sdl_texture_mjpg.h delete mode 100644 src/cam/sdl_texture_yuv.cpp delete mode 100644 src/cam/sdl_texture_yuv.h delete mode 100644 src/cam/stream_options.cpp delete mode 100644 src/cam/stream_options.h (limited to 'src/cam') diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp deleted file mode 100644 index 6b409c98..00000000 --- a/src/cam/camera_session.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * camera_session.cpp - Camera capture session - */ - -#include -#include -#include -#include - -#include -#include - -#include "camera_session.h" -#include "capture_script.h" -#include "event_loop.h" -#include "file_sink.h" -#ifdef HAVE_KMS -#include "kms_sink.h" -#endif -#include "main.h" -#ifdef HAVE_SDL -#include "sdl_sink.h" -#endif -#include "stream_options.h" - -using namespace libcamera; - -CameraSession::CameraSession(CameraManager *cm, - const std::string &cameraId, - unsigned int cameraIndex, - const OptionsParser::Options &options) - : options_(options), cameraIndex_(cameraIndex), last_(0), - queueCount_(0), captureCount_(0), captureLimit_(0), - printMetadata_(false) -{ - char *endptr; - unsigned long index = strtoul(cameraId.c_str(), &endptr, 10); - if (*endptr == '\0' && index > 0 && index <= cm->cameras().size()) - camera_ = cm->cameras()[index - 1]; - else - camera_ = cm->get(cameraId); - - if (!camera_) { - std::cerr << "Camera " << cameraId << " not found" << std::endl; - return; - } - - if (camera_->acquire()) { - std::cerr << "Failed to acquire camera " << cameraId - << std::endl; - return; - } - - StreamRoles roles = StreamKeyValueParser::roles(options_[OptStream]); - - std::unique_ptr config = - camera_->generateConfiguration(roles); - if (!config || config->size() != roles.size()) { - std::cerr << "Failed to get default stream configuration" - << std::endl; - return; - } - - /* Apply configuration if explicitly requested. */ - if (StreamKeyValueParser::updateConfiguration(config.get(), - options_[OptStream])) { - std::cerr << "Failed to update configuration" << std::endl; - return; - } - - bool strictFormats = options_.isSet(OptStrictFormats); - -#ifdef HAVE_KMS - if (options_.isSet(OptDisplay)) { - if (options_.isSet(OptFile)) { - std::cerr << "--display and --file options are mutually exclusive" - << std::endl; - return; - } - - if (roles.size() != 1) { - std::cerr << "Display doesn't support multiple streams" - << std::endl; - return; - } - - if (roles[0] != StreamRole::Viewfinder) { - std::cerr << "Display requires a viewfinder stream" - << std::endl; - return; - } - } -#endif - - if (options_.isSet(OptCaptureScript)) { - std::string scriptName = options_[OptCaptureScript].toString(); - script_ = std::make_unique(camera_, scriptName); - if (!script_->valid()) { - std::cerr << "Invalid capture script '" << scriptName - << "'" << std::endl; - return; - } - } - - switch (config->validate()) { - case CameraConfiguration::Valid: - break; - - case CameraConfiguration::Adjusted: - if (strictFormats) { - std::cout << "Adjusting camera configuration disallowed by --strict-formats argument" - << std::endl; - return; - } - std::cout << "Camera configuration adjusted" << std::endl; - break; - - case CameraConfiguration::Invalid: - std::cout << "Camera configuration invalid" << std::endl; - return; - } - - config_ = std::move(config); -} - -CameraSession::~CameraSession() -{ - if (camera_) - camera_->release(); -} - -void CameraSession::listControls() const -{ - for (const auto &[id, info] : camera_->controls()) { - std::cout << "Control: " << id->name() << ": " - << info.toString() << std::endl; - } -} - -void CameraSession::listProperties() const -{ - for (const auto &[key, value] : camera_->properties()) { - const ControlId *id = properties::properties.at(key); - - std::cout << "Property: " << id->name() << " = " - << value.toString() << std::endl; - } -} - -void CameraSession::infoConfiguration() const -{ - unsigned int index = 0; - for (const StreamConfiguration &cfg : *config_) { - std::cout << index << ": " << cfg.toString() << std::endl; - - const StreamFormats &formats = cfg.formats(); - for (PixelFormat pixelformat : formats.pixelformats()) { - std::cout << " * Pixelformat: " - << pixelformat << " " - << formats.range(pixelformat).toString() - << std::endl; - - for (const Size &size : formats.sizes(pixelformat)) - std::cout << " - " << size << std::endl; - } - - index++; - } -} - -int CameraSession::start() -{ - int ret; - - queueCount_ = 0; - captureCount_ = 0; - captureLimit_ = options_[OptCapture].toInteger(); - printMetadata_ = options_.isSet(OptMetadata); - - ret = camera_->configure(config_.get()); - if (ret < 0) { - std::cout << "Failed to configure camera" << std::endl; - return ret; - } - - streamNames_.clear(); - for (unsigned int index = 0; index < config_->size(); ++index) { - StreamConfiguration &cfg = config_->at(index); - streamNames_[cfg.stream()] = "cam" + std::to_string(cameraIndex_) - + "-stream" + std::to_string(index); - } - - camera_->requestCompleted.connect(this, &CameraSession::requestComplete); - -#ifdef HAVE_KMS - if (options_.isSet(OptDisplay)) - sink_ = std::make_unique(options_[OptDisplay].toString()); -#endif - -#ifdef HAVE_SDL - if (options_.isSet(OptSDL)) - sink_ = std::make_unique(); -#endif - - if (options_.isSet(OptFile)) { - if (!options_[OptFile].toString().empty()) - sink_ = std::make_unique(camera_.get(), streamNames_, - options_[OptFile]); - else - sink_ = std::make_unique(camera_.get(), streamNames_); - } - - if (sink_) { - ret = sink_->configure(*config_); - if (ret < 0) { - std::cout << "Failed to configure frame sink" - << std::endl; - return ret; - } - - sink_->requestProcessed.connect(this, &CameraSession::sinkRelease); - } - - allocator_ = std::make_unique(camera_); - - return startCapture(); -} - -void CameraSession::stop() -{ - int ret = camera_->stop(); - if (ret) - std::cout << "Failed to stop capture" << std::endl; - - if (sink_) { - ret = sink_->stop(); - if (ret) - std::cout << "Failed to stop frame sink" << std::endl; - } - - sink_.reset(); - - requests_.clear(); - - allocator_.reset(); -} - -int CameraSession::startCapture() -{ - int ret; - - /* Identify the stream with the least number of buffers. */ - unsigned int nbuffers = UINT_MAX; - for (StreamConfiguration &cfg : *config_) { - ret = allocator_->allocate(cfg.stream()); - if (ret < 0) { - std::cerr << "Can't allocate buffers" << std::endl; - return -ENOMEM; - } - - unsigned int allocated = allocator_->buffers(cfg.stream()).size(); - nbuffers = std::min(nbuffers, allocated); - } - - /* - * TODO: make cam tool smarter to support still capture by for - * example pushing a button. For now run all streams all the time. - */ - - for (unsigned int i = 0; i < nbuffers; i++) { - std::unique_ptr request = camera_->createRequest(); - if (!request) { - std::cerr << "Can't create request" << std::endl; - return -ENOMEM; - } - - for (StreamConfiguration &cfg : *config_) { - Stream *stream = cfg.stream(); - const std::vector> &buffers = - allocator_->buffers(stream); - const std::unique_ptr &buffer = buffers[i]; - - ret = request->addBuffer(stream, buffer.get()); - if (ret < 0) { - std::cerr << "Can't set buffer for request" - << std::endl; - return ret; - } - - if (sink_) - sink_->mapBuffer(buffer.get()); - } - - requests_.push_back(std::move(request)); - } - - if (sink_) { - ret = sink_->start(); - if (ret) { - std::cout << "Failed to start frame sink" << std::endl; - return ret; - } - } - - ret = camera_->start(); - if (ret) { - std::cout << "Failed to start capture" << std::endl; - if (sink_) - sink_->stop(); - return ret; - } - - for (std::unique_ptr &request : requests_) { - ret = queueRequest(request.get()); - if (ret < 0) { - std::cerr << "Can't queue request" << std::endl; - camera_->stop(); - if (sink_) - sink_->stop(); - return ret; - } - } - - if (captureLimit_) - std::cout << "cam" << cameraIndex_ - << ": Capture " << captureLimit_ << " frames" - << std::endl; - else - std::cout << "cam" << cameraIndex_ - << ": Capture until user interrupts by SIGINT" - << std::endl; - - return 0; -} - -int CameraSession::queueRequest(Request *request) -{ - if (captureLimit_ && queueCount_ >= captureLimit_) - return 0; - - if (script_) - request->controls() = script_->frameControls(queueCount_); - - queueCount_++; - - return camera_->queueRequest(request); -} - -void CameraSession::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. - */ - EventLoop::instance()->callLater([=]() { processRequest(request); }); -} - -void CameraSession::processRequest(Request *request) -{ - /* - * If we've reached the capture limit, we're done. This doesn't - * duplicate the check below that emits the captureDone signal, as this - * function will be called for each request still in flight after the - * capture limit is reached and we don't want to emit the signal every - * single time. - */ - if (captureLimit_ && captureCount_ >= captureLimit_) - return; - - const Request::BufferMap &buffers = request->buffers(); - - /* - * Compute the frame rate. The timestamp is arbitrarily retrieved from - * the first buffer, as all buffers should have matching timestamps. - */ - uint64_t ts = buffers.begin()->second->metadata().timestamp; - double fps = ts - last_; - fps = last_ != 0 && fps ? 1000000000.0 / fps : 0.0; - last_ = ts; - - bool requeue = true; - - std::stringstream info; - info << ts / 1000000000 << "." - << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 - << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; - - for (const auto &[stream, buffer] : buffers) { - const FrameMetadata &metadata = buffer->metadata(); - - info << " " << streamNames_[stream] - << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence - << " bytesused: "; - - unsigned int nplane = 0; - for (const FrameMetadata::Plane &plane : metadata.planes()) { - info << plane.bytesused; - if (++nplane < metadata.planes().size()) - info << "/"; - } - } - - if (sink_) { - if (!sink_->processRequest(request)) - requeue = false; - } - - std::cout << info.str() << std::endl; - - if (printMetadata_) { - const ControlList &requestMetadata = request->metadata(); - for (const auto &[key, value] : requestMetadata) { - const ControlId *id = controls::controls.at(key); - std::cout << "\t" << id->name() << " = " - << value.toString() << std::endl; - } - } - - /* - * Notify the user that capture is complete if the limit has just been - * reached. - */ - captureCount_++; - if (captureLimit_ && captureCount_ >= captureLimit_) { - captureDone.emit(); - return; - } - - /* - * If the frame sink holds on the request, we'll requeue it later in the - * complete handler. - */ - if (!requeue) - return; - - request->reuse(Request::ReuseBuffers); - queueRequest(request); -} - -void CameraSession::sinkRelease(Request *request) -{ - request->reuse(Request::ReuseBuffers); - queueRequest(request); -} diff --git a/src/cam/camera_session.h b/src/cam/camera_session.h deleted file mode 100644 index d562caae..00000000 --- a/src/cam/camera_session.h +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * camera_session.h - Camera capture session - */ - -#pragma once - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "options.h" - -class CaptureScript; -class FrameSink; - -class CameraSession -{ -public: - CameraSession(libcamera::CameraManager *cm, - const std::string &cameraId, unsigned int cameraIndex, - const OptionsParser::Options &options); - ~CameraSession(); - - bool isValid() const { return config_ != nullptr; } - const OptionsParser::Options &options() { return options_; } - - libcamera::Camera *camera() { return camera_.get(); } - libcamera::CameraConfiguration *config() { return config_.get(); } - - void listControls() const; - void listProperties() const; - void infoConfiguration() const; - - int start(); - void stop(); - - libcamera::Signal<> captureDone; - -private: - int startCapture(); - - int queueRequest(libcamera::Request *request); - void requestComplete(libcamera::Request *request); - void processRequest(libcamera::Request *request); - void sinkRelease(libcamera::Request *request); - - const OptionsParser::Options &options_; - std::shared_ptr camera_; - std::unique_ptr config_; - - std::unique_ptr script_; - - std::map streamNames_; - std::unique_ptr sink_; - unsigned int cameraIndex_; - - uint64_t last_; - - unsigned int queueCount_; - unsigned int captureCount_; - unsigned int captureLimit_; - bool printMetadata_; - - std::unique_ptr allocator_; - std::vector> requests_; -}; diff --git a/src/cam/capture-script.yaml b/src/cam/capture-script.yaml deleted file mode 100644 index 7118865e..00000000 --- a/src/cam/capture-script.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -# Capture script example -# -# A capture script allows to associate a list of controls and their values -# to frame numbers. -# -# The script allows defining a list of frames associated with controls -# and an optional list of properties that can control the script behaviour. - -# properties: -# # Repeat the controls every 'idx' frames. -# - loop: idx -# -# # List of frame number with associated a list of controls to be applied -# frames: -# - frame-number: -# Control1: value1 -# Control2: value2 - -# \todo Formally define the capture script structure with a schema - -# Notes: -# - Controls have to be specified by name, as defined in the -# libcamera::controls:: enumeration -# - Controls not supported by the camera currently operated are ignored -# - Frame numbers shall be monotonically incrementing, gaps are allowed -# - If a loop limit is specified, frame numbers in the 'frames' list shall be -# less than the loop control - -# Example: Turn brightness up and down every 460 frames - -properties: - - loop: 460 - -frames: - - 0: - Brightness: 0.0 - - - 40: - Brightness: 0.2 - - - 80: - Brightness: 0.4 - - - 120: - Brightness: 0.8 - - - 160: - Brightness: 0.4 - - - 200: - Brightness: 0.2 - - - 240: - Brightness: 0.0 - - - 280: - Brightness: -0.2 - - - 300: - Brightness: -0.4 - - - 340: - Brightness: -0.8 - - - 380: - Brightness: -0.4 - - - 420: - Brightness: -0.2 diff --git a/src/cam/capture_script.cpp b/src/cam/capture_script.cpp deleted file mode 100644 index 5a27361c..00000000 --- a/src/cam/capture_script.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2022, Ideas on Board Oy - * - * capture_script.cpp - Capture session configuration script - */ - -#include "capture_script.h" - -#include -#include -#include - -using namespace libcamera; - -CaptureScript::CaptureScript(std::shared_ptr camera, - const std::string &fileName) - : camera_(camera), loop_(0), valid_(false) -{ - FILE *fh = fopen(fileName.c_str(), "r"); - if (!fh) { - int ret = -errno; - std::cerr << "Failed to open capture script " << fileName - << ": " << strerror(-ret) << std::endl; - return; - } - - /* - * Map the camera's controls to their name so that they can be - * easily identified when parsing the script file. - */ - for (const auto &[control, info] : camera_->controls()) - controls_[control->name()] = control; - - int ret = parseScript(fh); - fclose(fh); - if (ret) - return; - - valid_ = true; -} - -/* Retrieve the control list associated with a frame number. */ -const ControlList &CaptureScript::frameControls(unsigned int frame) -{ - static ControlList controls{}; - unsigned int idx = frame; - - /* If we loop, repeat the controls every 'loop_' frames. */ - if (loop_) - idx = frame % loop_; - - auto it = frameControls_.find(idx); - if (it == frameControls_.end()) - return controls; - - return it->second; -} - -CaptureScript::EventPtr CaptureScript::nextEvent(yaml_event_type_t expectedType) -{ - EventPtr event(new yaml_event_t); - - if (!yaml_parser_parse(&parser_, event.get())) - return nullptr; - - if (expectedType != YAML_NO_EVENT && !checkEvent(event, expectedType)) - return nullptr; - - return event; -} - -bool CaptureScript::checkEvent(const EventPtr &event, yaml_event_type_t expectedType) const -{ - if (event->type != expectedType) { - std::cerr << "Capture script error on line " << event->start_mark.line - << " column " << event->start_mark.column << ": " - << "Expected " << eventTypeName(expectedType) - << " event, got " << eventTypeName(event->type) - << std::endl; - return false; - } - - return true; -} - -std::string CaptureScript::eventScalarValue(const EventPtr &event) -{ - return std::string(reinterpret_cast(event->data.scalar.value), - event->data.scalar.length); -} - -std::string CaptureScript::eventTypeName(yaml_event_type_t type) -{ - static const std::map typeNames = { - { YAML_STREAM_START_EVENT, "stream-start" }, - { YAML_STREAM_END_EVENT, "stream-end" }, - { YAML_DOCUMENT_START_EVENT, "document-start" }, - { YAML_DOCUMENT_END_EVENT, "document-end" }, - { YAML_ALIAS_EVENT, "alias" }, - { YAML_SCALAR_EVENT, "scalar" }, - { YAML_SEQUENCE_START_EVENT, "sequence-start" }, - { YAML_SEQUENCE_END_EVENT, "sequence-end" }, - { YAML_MAPPING_START_EVENT, "mapping-start" }, - { YAML_MAPPING_END_EVENT, "mapping-end" }, - }; - - auto it = typeNames.find(type); - if (it == typeNames.end()) - return "[type " + std::to_string(type) + "]"; - - return it->second; -} - -int CaptureScript::parseScript(FILE *script) -{ - int ret = yaml_parser_initialize(&parser_); - if (!ret) { - std::cerr << "Failed to initialize yaml parser" << std::endl; - return ret; - } - - /* Delete the parser upon function exit. */ - struct ParserDeleter { - ParserDeleter(yaml_parser_t *parser) : parser_(parser) { } - ~ParserDeleter() { yaml_parser_delete(parser_); } - yaml_parser_t *parser_; - } deleter(&parser_); - - yaml_parser_set_input_file(&parser_, script); - - EventPtr event = nextEvent(YAML_STREAM_START_EVENT); - if (!event) - return -EINVAL; - - event = nextEvent(YAML_DOCUMENT_START_EVENT); - if (!event) - return -EINVAL; - - event = nextEvent(YAML_MAPPING_START_EVENT); - if (!event) - return -EINVAL; - - while (1) { - event = nextEvent(); - if (!event) - return -EINVAL; - - if (event->type == YAML_MAPPING_END_EVENT) - return 0; - - if (!checkEvent(event, YAML_SCALAR_EVENT)) - return -EINVAL; - - std::string section = eventScalarValue(event); - - if (section == "properties") { - ret = parseProperties(); - if (ret) - return ret; - } else if (section == "frames") { - ret = parseFrames(); - if (ret) - return ret; - } else { - std::cerr << "Unsupported section '" << section << "'" - << std::endl; - return -EINVAL; - } - } -} - -int CaptureScript::parseProperty() -{ - EventPtr event = nextEvent(YAML_MAPPING_START_EVENT); - if (!event) - return -EINVAL; - - std::string prop = parseScalar(); - if (prop.empty()) - return -EINVAL; - - if (prop == "loop") { - event = nextEvent(); - if (!event) - return -EINVAL; - - std::string value = eventScalarValue(event); - if (value.empty()) - return -EINVAL; - - loop_ = atoi(value.c_str()); - if (!loop_) { - std::cerr << "Invalid loop limit '" << loop_ << "'" - << std::endl; - return -EINVAL; - } - } else { - std::cerr << "Unsupported property '" << prop << "'" << std::endl; - return -EINVAL; - } - - event = nextEvent(YAML_MAPPING_END_EVENT); - if (!event) - return -EINVAL; - - return 0; -} - -int CaptureScript::parseProperties() -{ - EventPtr event = nextEvent(YAML_SEQUENCE_START_EVENT); - if (!event) - return -EINVAL; - - while (1) { - if (event->type == YAML_SEQUENCE_END_EVENT) - return 0; - - int ret = parseProperty(); - if (ret) - return ret; - - event = nextEvent(); - if (!event) - return -EINVAL; - } - - return 0; -} - -int CaptureScript::parseFrames() -{ - EventPtr event = nextEvent(YAML_SEQUENCE_START_EVENT); - if (!event) - return -EINVAL; - - while (1) { - event = nextEvent(); - if (!event) - return -EINVAL; - - if (event->type == YAML_SEQUENCE_END_EVENT) - return 0; - - int ret = parseFrame(std::move(event)); - if (ret) - return ret; - } -} - -int CaptureScript::parseFrame(EventPtr event) -{ - if (!checkEvent(event, YAML_MAPPING_START_EVENT)) - return -EINVAL; - - std::string key = parseScalar(); - if (key.empty()) - return -EINVAL; - - unsigned int frameId = atoi(key.c_str()); - if (loop_ && frameId >= loop_) { - std::cerr - << "Frame id (" << frameId << ") shall be smaller than" - << "loop limit (" << loop_ << ")" << std::endl; - return -EINVAL; - } - - event = nextEvent(YAML_MAPPING_START_EVENT); - if (!event) - return -EINVAL; - - ControlList controls{}; - - while (1) { - event = nextEvent(); - if (!event) - return -EINVAL; - - if (event->type == YAML_MAPPING_END_EVENT) - break; - - int ret = parseControl(std::move(event), controls); - if (ret) - return ret; - } - - frameControls_[frameId] = std::move(controls); - - event = nextEvent(YAML_MAPPING_END_EVENT); - if (!event) - return -EINVAL; - - return 0; -} - -int CaptureScript::parseControl(EventPtr event, ControlList &controls) -{ - /* We expect a value after a key. */ - std::string name = eventScalarValue(event); - if (name.empty()) - return -EINVAL; - - /* If the camera does not support the control just ignore it. */ - auto it = controls_.find(name); - if (it == controls_.end()) { - std::cerr << "Unsupported control '" << name << "'" << std::endl; - return -EINVAL; - } - - const ControlId *controlId = it->second; - - ControlValue val = unpackControl(controlId); - if (val.isNone()) { - std::cerr << "Error unpacking control '" << name << "'" - << std::endl; - return -EINVAL; - } - - controls.set(controlId->id(), val); - - return 0; -} - -std::string CaptureScript::parseScalar() -{ - EventPtr event = nextEvent(YAML_SCALAR_EVENT); - if (!event) - return ""; - - return eventScalarValue(event); -} - -ControlValue CaptureScript::parseRectangles() -{ - std::vector rectangles; - - std::vector> arrays = parseArrays(); - if (arrays.empty()) - return {}; - - for (const std::vector &values : arrays) { - if (values.size() != 4) { - std::cerr << "Error parsing Rectangle: expected " - << "array with 4 parameters" << std::endl; - return {}; - } - - Rectangle rect = unpackRectangle(values); - rectangles.push_back(rect); - } - - ControlValue controlValue; - controlValue.set(Span(rectangles)); - - return controlValue; -} - -std::vector> CaptureScript::parseArrays() -{ - EventPtr event = nextEvent(YAML_SEQUENCE_START_EVENT); - if (!event) - return {}; - - event = nextEvent(); - if (!event) - return {}; - - std::vector> valueArrays; - - /* Parse single array. */ - if (event->type == YAML_SCALAR_EVENT) { - std::string firstValue = eventScalarValue(event); - if (firstValue.empty()) - return {}; - - std::vector remaining = parseSingleArray(); - - std::vector values = { firstValue }; - values.insert(std::end(values), - std::begin(remaining), std::end(remaining)); - valueArrays.push_back(values); - - return valueArrays; - } - - /* Parse array of arrays. */ - while (1) { - switch (event->type) { - case YAML_SEQUENCE_START_EVENT: { - std::vector values = parseSingleArray(); - valueArrays.push_back(values); - break; - } - case YAML_SEQUENCE_END_EVENT: - return valueArrays; - default: - return {}; - } - - event = nextEvent(); - if (!event) - return {}; - } -} - -std::vector CaptureScript::parseSingleArray() -{ - std::vector values; - - while (1) { - EventPtr event = nextEvent(); - if (!event) - return {}; - - switch (event->type) { - case YAML_SCALAR_EVENT: { - std::string value = eventScalarValue(event); - if (value.empty()) - return {}; - values.push_back(value); - break; - } - case YAML_SEQUENCE_END_EVENT: - return values; - default: - return {}; - } - } -} - -void CaptureScript::unpackFailure(const ControlId *id, const std::string &repr) -{ - static const std::map typeNames = { - { ControlTypeNone, "none" }, - { ControlTypeBool, "bool" }, - { ControlTypeByte, "byte" }, - { ControlTypeInteger32, "int32" }, - { ControlTypeInteger64, "int64" }, - { ControlTypeFloat, "float" }, - { ControlTypeString, "string" }, - { ControlTypeRectangle, "Rectangle" }, - { ControlTypeSize, "Size" }, - }; - - const char *typeName; - auto it = typeNames.find(id->type()); - if (it != typeNames.end()) - typeName = it->second; - else - typeName = "unknown"; - - std::cerr << "Unsupported control '" << repr << "' for " - << typeName << " control " << id->name() << std::endl; -} - -ControlValue CaptureScript::unpackControl(const ControlId *id) -{ - /* Parse complex types. */ - switch (id->type()) { - case ControlTypeRectangle: - return parseRectangles(); - case ControlTypeSize: - /* \todo Parse Sizes. */ - return {}; - default: - break; - } - - /* Parse basic types represented by a single scalar. */ - const std::string repr = parseScalar(); - if (repr.empty()) - return {}; - - ControlValue value{}; - - switch (id->type()) { - case ControlTypeNone: - break; - case ControlTypeBool: { - bool val; - - if (repr == "true") { - val = true; - } else if (repr == "false") { - val = false; - } else { - unpackFailure(id, repr); - return value; - } - - value.set(val); - break; - } - case ControlTypeByte: { - uint8_t val = strtol(repr.c_str(), NULL, 10); - value.set(val); - break; - } - case ControlTypeInteger32: { - int32_t val = strtol(repr.c_str(), NULL, 10); - value.set(val); - break; - } - case ControlTypeInteger64: { - int64_t val = strtoll(repr.c_str(), NULL, 10); - value.set(val); - break; - } - case ControlTypeFloat: { - float val = strtof(repr.c_str(), NULL); - value.set(val); - break; - } - case ControlTypeString: { - value.set(repr); - break; - } - default: - std::cerr << "Unsupported control type" << std::endl; - break; - } - - return value; -} - -libcamera::Rectangle CaptureScript::unpackRectangle(const std::vector &strVec) -{ - int x = strtol(strVec[0].c_str(), NULL, 10); - int y = strtol(strVec[1].c_str(), NULL, 10); - unsigned int width = strtoul(strVec[2].c_str(), NULL, 10); - unsigned int height = strtoul(strVec[3].c_str(), NULL, 10); - - return Rectangle(x, y, width, height); -} diff --git a/src/cam/capture_script.h b/src/cam/capture_script.h deleted file mode 100644 index 7a0ddebb..00000000 --- a/src/cam/capture_script.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2022, Ideas on Board Oy - * - * capture_script.h - Capture session configuration script - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include - -class CaptureScript -{ -public: - CaptureScript(std::shared_ptr camera, - const std::string &fileName); - - bool valid() const { return valid_; } - - const libcamera::ControlList &frameControls(unsigned int frame); - -private: - struct EventDeleter { - void operator()(yaml_event_t *event) const - { - yaml_event_delete(event); - delete event; - } - }; - using EventPtr = std::unique_ptr; - - std::map controls_; - std::map frameControls_; - std::shared_ptr camera_; - yaml_parser_t parser_; - unsigned int loop_; - bool valid_; - - EventPtr nextEvent(yaml_event_type_t expectedType = YAML_NO_EVENT); - bool checkEvent(const EventPtr &event, yaml_event_type_t expectedType) const; - static std::string eventScalarValue(const EventPtr &event); - static std::string eventTypeName(yaml_event_type_t type); - - int parseScript(FILE *script); - - int parseProperties(); - int parseProperty(); - int parseFrames(); - int parseFrame(EventPtr event); - int parseControl(EventPtr event, libcamera::ControlList &controls); - - std::string parseScalar(); - libcamera::ControlValue parseRectangles(); - std::vector> parseArrays(); - std::vector parseSingleArray(); - - void unpackFailure(const libcamera::ControlId *id, - const std::string &repr); - libcamera::ControlValue unpackControl(const libcamera::ControlId *id); - libcamera::Rectangle unpackRectangle(const std::vector &strVec); -}; diff --git a/src/cam/dng_writer.cpp b/src/cam/dng_writer.cpp deleted file mode 100644 index c945edce..00000000 --- a/src/cam/dng_writer.cpp +++ /dev/null @@ -1,653 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * dng_writer.cpp - DNG writer - */ - -#include "dng_writer.h" - -#include -#include -#include - -#include - -#include -#include -#include - -using namespace libcamera; - -enum CFAPatternColour : uint8_t { - CFAPatternRed = 0, - CFAPatternGreen = 1, - CFAPatternBlue = 2, -}; - -struct FormatInfo { - uint8_t bitsPerSample; - CFAPatternColour pattern[4]; - void (*packScanline)(void *output, const void *input, - unsigned int width); - void (*thumbScanline)(const FormatInfo &info, void *output, - const void *input, unsigned int width, - unsigned int stride); -}; - -struct Matrix3d { - Matrix3d() - { - } - - Matrix3d(float m0, float m1, float m2, - float m3, float m4, float m5, - float m6, float m7, float m8) - { - m[0] = m0, m[1] = m1, m[2] = m2; - m[3] = m3, m[4] = m4, m[5] = m5; - m[6] = m6, m[7] = m7, m[8] = m8; - } - - Matrix3d(const Span &span) - : Matrix3d(span[0], span[1], span[2], - span[3], span[4], span[5], - span[6], span[7], span[8]) - { - } - - static Matrix3d diag(float diag0, float diag1, float diag2) - { - return Matrix3d(diag0, 0, 0, 0, diag1, 0, 0, 0, diag2); - } - - static Matrix3d identity() - { - return Matrix3d(1, 0, 0, 0, 1, 0, 0, 0, 1); - } - - Matrix3d transpose() const - { - return { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] }; - } - - Matrix3d cofactors() const - { - return { m[4] * m[8] - m[5] * m[7], - -(m[3] * m[8] - m[5] * m[6]), - m[3] * m[7] - m[4] * m[6], - -(m[1] * m[8] - m[2] * m[7]), - m[0] * m[8] - m[2] * m[6], - -(m[0] * m[7] - m[1] * m[6]), - m[1] * m[5] - m[2] * m[4], - -(m[0] * m[5] - m[2] * m[3]), - m[0] * m[4] - m[1] * m[3] }; - } - - Matrix3d adjugate() const - { - return cofactors().transpose(); - } - - float determinant() const - { - return m[0] * (m[4] * m[8] - m[5] * m[7]) - - m[1] * (m[3] * m[8] - m[5] * m[6]) + - m[2] * (m[3] * m[7] - m[4] * m[6]); - } - - Matrix3d inverse() const - { - return adjugate() * (1.0 / determinant()); - } - - Matrix3d operator*(const Matrix3d &other) const - { - Matrix3d result; - for (unsigned int i = 0; i < 3; i++) { - for (unsigned int j = 0; j < 3; j++) { - result.m[i * 3 + j] = - m[i * 3 + 0] * other.m[0 + j] + - m[i * 3 + 1] * other.m[3 + j] + - m[i * 3 + 2] * other.m[6 + j]; - } - } - return result; - } - - Matrix3d operator*(float f) const - { - Matrix3d result; - for (unsigned int i = 0; i < 9; i++) - result.m[i] = m[i] * f; - return result; - } - - float m[9]; -}; - -void packScanlineSBGGR8(void *output, const void *input, unsigned int width) -{ - const uint8_t *in = static_cast(input); - uint8_t *out = static_cast(output); - - std::copy(in, in + width, out); -} - -void packScanlineSBGGR10P(void *output, const void *input, unsigned int width) -{ - const uint8_t *in = static_cast(input); - uint8_t *out = static_cast(output); - - /* \todo Can this be made more efficient? */ - for (unsigned int x = 0; x < width; x += 4) { - *out++ = in[0]; - *out++ = (in[4] & 0x03) << 6 | in[1] >> 2; - *out++ = (in[1] & 0x03) << 6 | (in[4] & 0x0c) << 2 | in[2] >> 4; - *out++ = (in[2] & 0x0f) << 4 | (in[4] & 0x30) >> 2 | in[3] >> 6; - *out++ = (in[3] & 0x3f) << 2 | (in[4] & 0xc0) >> 6; - in += 5; - } -} - -void packScanlineSBGGR12P(void *output, const void *input, unsigned int width) -{ - const uint8_t *in = static_cast(input); - uint8_t *out = static_cast(output); - - /* \todo Can this be made more efficient? */ - for (unsigned int i = 0; i < width; i += 2) { - *out++ = in[0]; - *out++ = (in[2] & 0x0f) << 4 | in[1] >> 4; - *out++ = (in[1] & 0x0f) << 4 | in[2] >> 4; - in += 3; - } -} - -void thumbScanlineSBGGRxxP(const FormatInfo &info, void *output, - const void *input, unsigned int width, - unsigned int stride) -{ - const uint8_t *in = static_cast(input); - uint8_t *out = static_cast(output); - - /* Number of bytes corresponding to 16 pixels. */ - unsigned int skip = info.bitsPerSample * 16 / 8; - - for (unsigned int x = 0; x < width; x++) { - uint8_t value = (in[0] + in[1] + in[stride] + in[stride + 1]) >> 2; - *out++ = value; - *out++ = value; - *out++ = value; - in += skip; - } -} - -void packScanlineIPU3(void *output, const void *input, unsigned int width) -{ - const uint8_t *in = static_cast(input); - uint16_t *out = static_cast(output); - - /* - * Upscale the 10-bit format to 16-bit as it's not trivial to pack it - * as 10-bit without gaps. - * - * \todo Improve packing to keep the 10-bit sample size. - */ - unsigned int x = 0; - while (true) { - for (unsigned int i = 0; i < 6; i++) { - *out++ = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6; - if (++x >= width) - return; - - *out++ = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4; - if (++x >= width) - return; - - *out++ = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2; - if (++x >= width) - return; - - *out++ = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0; - if (++x >= width) - return; - - in += 5; - } - - *out++ = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6; - if (++x >= width) - return; - - in += 2; - } -} - -void thumbScanlineIPU3([[maybe_unused]] const FormatInfo &info, void *output, - const void *input, unsigned int width, - unsigned int stride) -{ - uint8_t *out = static_cast(output); - - for (unsigned int x = 0; x < width; x++) { - unsigned int pixel = x * 16; - unsigned int block = pixel / 25; - unsigned int pixelInBlock = pixel - block * 25; - - /* - * If the pixel is the last in the block cheat a little and - * move one pixel backward to avoid reading between two blocks - * and having to deal with the padding bits. - */ - if (pixelInBlock == 24) - pixelInBlock--; - - const uint8_t *in = static_cast(input) - + block * 32 + (pixelInBlock / 4) * 5; - - uint16_t val1, val2, val3, val4; - switch (pixelInBlock % 4) { - case 0: - val1 = (in[1] & 0x03) << 14 | (in[0] & 0xff) << 6; - val2 = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4; - val3 = (in[stride + 1] & 0x03) << 14 | (in[stride + 0] & 0xff) << 6; - val4 = (in[stride + 2] & 0x0f) << 12 | (in[stride + 1] & 0xfc) << 4; - break; - case 1: - val1 = (in[2] & 0x0f) << 12 | (in[1] & 0xfc) << 4; - val2 = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2; - val3 = (in[stride + 2] & 0x0f) << 12 | (in[stride + 1] & 0xfc) << 4; - val4 = (in[stride + 3] & 0x3f) << 10 | (in[stride + 2] & 0xf0) << 2; - break; - case 2: - val1 = (in[3] & 0x3f) << 10 | (in[2] & 0xf0) << 2; - val2 = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0; - val3 = (in[stride + 3] & 0x3f) << 10 | (in[stride + 2] & 0xf0) << 2; - val4 = (in[stride + 4] & 0xff) << 8 | (in[stride + 3] & 0xc0) << 0; - break; - case 3: - val1 = (in[4] & 0xff) << 8 | (in[3] & 0xc0) << 0; - val2 = (in[6] & 0x03) << 14 | (in[5] & 0xff) << 6; - val3 = (in[stride + 4] & 0xff) << 8 | (in[stride + 3] & 0xc0) << 0; - val4 = (in[stride + 6] & 0x03) << 14 | (in[stride + 5] & 0xff) << 6; - break; - } - - uint8_t value = (val1 + val2 + val3 + val4) >> 10; - *out++ = value; - *out++ = value; - *out++ = value; - } -} - -static const std::map formatInfo = { - { formats::SBGGR8, { - .bitsPerSample = 8, - .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed }, - .packScanline = packScanlineSBGGR8, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGBRG8, { - .bitsPerSample = 8, - .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen }, - .packScanline = packScanlineSBGGR8, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGRBG8, { - .bitsPerSample = 8, - .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen }, - .packScanline = packScanlineSBGGR8, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SRGGB8, { - .bitsPerSample = 8, - .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue }, - .packScanline = packScanlineSBGGR8, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SBGGR10_CSI2P, { - .bitsPerSample = 10, - .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed }, - .packScanline = packScanlineSBGGR10P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGBRG10_CSI2P, { - .bitsPerSample = 10, - .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen }, - .packScanline = packScanlineSBGGR10P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGRBG10_CSI2P, { - .bitsPerSample = 10, - .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen }, - .packScanline = packScanlineSBGGR10P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SRGGB10_CSI2P, { - .bitsPerSample = 10, - .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue }, - .packScanline = packScanlineSBGGR10P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SBGGR12_CSI2P, { - .bitsPerSample = 12, - .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed }, - .packScanline = packScanlineSBGGR12P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGBRG12_CSI2P, { - .bitsPerSample = 12, - .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen }, - .packScanline = packScanlineSBGGR12P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SGRBG12_CSI2P, { - .bitsPerSample = 12, - .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen }, - .packScanline = packScanlineSBGGR12P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SRGGB12_CSI2P, { - .bitsPerSample = 12, - .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue }, - .packScanline = packScanlineSBGGR12P, - .thumbScanline = thumbScanlineSBGGRxxP, - } }, - { formats::SBGGR10_IPU3, { - .bitsPerSample = 16, - .pattern = { CFAPatternBlue, CFAPatternGreen, CFAPatternGreen, CFAPatternRed }, - .packScanline = packScanlineIPU3, - .thumbScanline = thumbScanlineIPU3, - } }, - { formats::SGBRG10_IPU3, { - .bitsPerSample = 16, - .pattern = { CFAPatternGreen, CFAPatternBlue, CFAPatternRed, CFAPatternGreen }, - .packScanline = packScanlineIPU3, - .thumbScanline = thumbScanlineIPU3, - } }, - { formats::SGRBG10_IPU3, { - .bitsPerSample = 16, - .pattern = { CFAPatternGreen, CFAPatternRed, CFAPatternBlue, CFAPatternGreen }, - .packScanline = packScanlineIPU3, - .thumbScanline = thumbScanlineIPU3, - } }, - { formats::SRGGB10_IPU3, { - .bitsPerSample = 16, - .pattern = { CFAPatternRed, CFAPatternGreen, CFAPatternGreen, CFAPatternBlue }, - .packScanline = packScanlineIPU3, - .thumbScanline = thumbScanlineIPU3, - } }, -}; - -int DNGWriter::write(const char *filename, const Camera *camera, - const StreamConfiguration &config, - const ControlList &metadata, - [[maybe_unused]] const FrameBuffer *buffer, - const void *data) -{ - const ControlList &cameraProperties = camera->properties(); - - const auto it = formatInfo.find(config.pixelFormat); - if (it == formatInfo.cend()) { - std::cerr << "Unsupported pixel format" << std::endl; - return -EINVAL; - } - const FormatInfo *info = &it->second; - - TIFF *tif = TIFFOpen(filename, "w"); - if (!tif) { - std::cerr << "Failed to open tiff file" << std::endl; - return -EINVAL; - } - - /* - * Scanline buffer, has to be large enough to store both a RAW scanline - * or a thumbnail scanline. The latter will always be much smaller than - * the former as we downscale by 16 in both directions. - */ - uint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8]; - - toff_t rawIFDOffset = 0; - toff_t exifIFDOffset = 0; - - /* - * Start with a thumbnail in IFD 0 for compatibility with TIFF baseline - * readers, as required by the TIFF/EP specification. Tags that apply to - * the whole file are stored here. - */ - const uint8_t version[] = { 1, 2, 0, 0 }; - - TIFFSetField(tif, TIFFTAG_DNGVERSION, version); - TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, version); - TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); - TIFFSetField(tif, TIFFTAG_MAKE, "libcamera"); - - const auto &model = cameraProperties.get(properties::Model); - if (model) { - TIFFSetField(tif, TIFFTAG_MODEL, model->c_str()); - /* \todo set TIFFTAG_UNIQUECAMERAMODEL. */ - } - - TIFFSetField(tif, TIFFTAG_SOFTWARE, "qcam"); - TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - - /* - * Thumbnail-specific tags. The thumbnail is stored as an RGB image - * with 1/16 of the raw image resolution. Greyscale would save space, - * but doesn't seem well supported by RawTherapee. - */ - TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE); - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width / 16); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height / 16); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); - TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); - - /* - * Fill in some reasonable colour information in the DNG. We supply - * the "neutral" colour values which determine the white balance, and the - * "ColorMatrix1" which converts XYZ to (un-white-balanced) camera RGB. - * Note that this is not a "proper" colour calibration for the DNG, - * nonetheless, many tools should be able to render the colours better. - */ - float neutral[3] = { 1, 1, 1 }; - Matrix3d wbGain = Matrix3d::identity(); - /* From http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html */ - const Matrix3d rgb2xyz(0.4124564, 0.3575761, 0.1804375, - 0.2126729, 0.7151522, 0.0721750, - 0.0193339, 0.1191920, 0.9503041); - Matrix3d ccm = Matrix3d::identity(); - /* - * Pick a reasonable number eps to protect against singularities. It - * should be comfortably larger than the point at which we run into - * numerical trouble, yet smaller than any plausible gain that we might - * apply to a colour, either explicitly or as part of the colour matrix. - */ - const double eps = 1e-2; - - const auto &colourGains = metadata.get(controls::ColourGains); - if (colourGains) { - if ((*colourGains)[0] > eps && (*colourGains)[1] > eps) { - wbGain = Matrix3d::diag((*colourGains)[0], 1, (*colourGains)[1]); - neutral[0] = 1.0 / (*colourGains)[0]; /* red */ - neutral[2] = 1.0 / (*colourGains)[1]; /* blue */ - } - } - - const auto &ccmControl = metadata.get(controls::ColourCorrectionMatrix); - if (ccmControl) { - Matrix3d ccmSupplied(*ccmControl); - if (ccmSupplied.determinant() > eps) - ccm = ccmSupplied; - } - - /* - * rgb2xyz is known to be invertible, and we've ensured above that both - * the ccm and wbGain matrices are non-singular, so the product of all - * three is guaranteed to be invertible too. - */ - Matrix3d colorMatrix1 = (rgb2xyz * ccm * wbGain).inverse(); - - TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colorMatrix1.m); - TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral); - - /* - * Reserve space for the SubIFD and ExifIFD tags, pointing to the IFD - * for the raw image and EXIF data respectively. The real offsets will - * be set later. - */ - TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset); - TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset); - - /* Write the thumbnail. */ - const uint8_t *row = static_cast(data); - for (unsigned int y = 0; y < config.size.height / 16; y++) { - info->thumbScanline(*info, &scanline, row, - config.size.width / 16, config.stride); - - if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) { - std::cerr << "Failed to write thumbnail scanline" - << std::endl; - TIFFClose(tif); - return -EINVAL; - } - - row += config.stride * 16; - } - - TIFFWriteDirectory(tif); - - /* Create a new IFD for the RAW image. */ - const uint16_t cfaRepeatPatternDim[] = { 2, 2 }; - const uint8_t cfaPlaneColor[] = { - CFAPatternRed, - CFAPatternGreen, - CFAPatternBlue - }; - - TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0); - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, config.size.width); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, config.size.height); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, info->bitsPerSample); - TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); - TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, cfaRepeatPatternDim); - if (TIFFLIB_VERSION < 20201219) - TIFFSetField(tif, TIFFTAG_CFAPATTERN, info->pattern); - else - TIFFSetField(tif, TIFFTAG_CFAPATTERN, 4, info->pattern); - TIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, cfaPlaneColor); - TIFFSetField(tif, TIFFTAG_CFALAYOUT, 1); - - const uint16_t blackLevelRepeatDim[] = { 2, 2 }; - float blackLevel[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - uint32_t whiteLevel = (1 << info->bitsPerSample) - 1; - - const auto &blackLevels = metadata.get(controls::SensorBlackLevels); - if (blackLevels) { - Span levels = *blackLevels; - - /* - * The black levels control is specified in R, Gr, Gb, B order. - * Map it to the TIFF tag that is specified in CFA pattern - * order. - */ - unsigned int green = (info->pattern[0] == CFAPatternRed || - info->pattern[1] == CFAPatternRed) - ? 0 : 1; - - for (unsigned int i = 0; i < 4; ++i) { - unsigned int level; - - switch (info->pattern[i]) { - case CFAPatternRed: - level = levels[0]; - break; - case CFAPatternGreen: - level = levels[green + 1]; - green = (green + 1) % 2; - break; - case CFAPatternBlue: - default: - level = levels[3]; - break; - } - - /* Map the 16-bit value to the bits per sample range. */ - blackLevel[i] = level >> (16 - info->bitsPerSample); - } - } - - TIFFSetField(tif, TIFFTAG_BLACKLEVELREPEATDIM, &blackLevelRepeatDim); - TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 4, &blackLevel); - TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whiteLevel); - - /* Write RAW content. */ - row = static_cast(data); - for (unsigned int y = 0; y < config.size.height; y++) { - info->packScanline(&scanline, row, config.size.width); - - if (TIFFWriteScanline(tif, &scanline, y, 0) != 1) { - std::cerr << "Failed to write RAW scanline" - << std::endl; - TIFFClose(tif); - return -EINVAL; - } - - row += config.stride; - } - - /* Checkpoint the IFD to retrieve its offset, and write it out. */ - TIFFCheckpointDirectory(tif); - rawIFDOffset = TIFFCurrentDirOffset(tif); - TIFFWriteDirectory(tif); - - /* Create a new IFD for the EXIF data and fill it. */ - TIFFCreateEXIFDirectory(tif); - - /* Store creation time. */ - time_t rawtime; - struct tm *timeinfo; - char strTime[20]; - - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(strTime, 20, "%Y:%m:%d %H:%M:%S", timeinfo); - - /* - * \todo Handle timezone information by setting OffsetTimeOriginal and - * OffsetTimeDigitized once libtiff catches up to the specification and - * has EXIFTAG_ defines to handle them. - */ - TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, strTime); - TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, strTime); - - const auto &analogGain = metadata.get(controls::AnalogueGain); - if (analogGain) { - uint16_t iso = std::min(std::max(*analogGain * 100, 0.0f), 65535.0f); - TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, &iso); - } - - const auto &exposureTime = metadata.get(controls::ExposureTime); - if (exposureTime) - TIFFSetField(tif, EXIFTAG_EXPOSURETIME, *exposureTime / 1e6); - - TIFFWriteCustomDirectory(tif, &exifIFDOffset); - - /* Update the IFD offsets and close the file. */ - TIFFSetDirectory(tif, 0); - TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset); - TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset); - TIFFWriteDirectory(tif); - - TIFFClose(tif); - - return 0; -} diff --git a/src/cam/dng_writer.h b/src/cam/dng_writer.h deleted file mode 100644 index 38f38f62..00000000 --- a/src/cam/dng_writer.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * dng_writer.h - DNG writer - */ - -#pragma once - -#ifdef HAVE_TIFF -#define HAVE_DNG - -#include -#include -#include -#include - -class DNGWriter -{ -public: - static int write(const char *filename, const libcamera::Camera *camera, - const libcamera::StreamConfiguration &config, - const libcamera::ControlList &metadata, - const libcamera::FrameBuffer *buffer, const void *data); -}; - -#endif /* HAVE_TIFF */ diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp deleted file mode 100644 index 2e4d7985..00000000 --- a/src/cam/drm.cpp +++ /dev/null @@ -1,717 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * drm.cpp - DRM/KMS Helpers - */ - -#include "drm.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "event_loop.h" - -namespace DRM { - -Object::Object(Device *dev, uint32_t id, Type type) - : id_(id), dev_(dev), type_(type) -{ - /* Retrieve properties from the objects that support them. */ - if (type != TypeConnector && type != TypeCrtc && - type != TypeEncoder && type != TypePlane) - return; - - /* - * We can't distinguish between failures due to the object having no - * property and failures due to other conditions. Assume we use the API - * correctly and consider the object has no property. - */ - drmModeObjectProperties *properties = drmModeObjectGetProperties(dev->fd(), id, type); - if (!properties) - return; - - properties_.reserve(properties->count_props); - for (uint32_t i = 0; i < properties->count_props; ++i) - properties_.emplace_back(properties->props[i], - properties->prop_values[i]); - - drmModeFreeObjectProperties(properties); -} - -Object::~Object() -{ -} - -const Property *Object::property(const std::string &name) const -{ - for (const PropertyValue &pv : properties_) { - const Property *property = static_cast(dev_->object(pv.id())); - if (property && property->name() == name) - return property; - } - - return nullptr; -} - -const PropertyValue *Object::propertyValue(const std::string &name) const -{ - for (const PropertyValue &pv : properties_) { - const Property *property = static_cast(dev_->object(pv.id())); - if (property && property->name() == name) - return &pv; - } - - return nullptr; -} - -Property::Property(Device *dev, drmModePropertyRes *property) - : Object(dev, property->prop_id, TypeProperty), - name_(property->name), flags_(property->flags), - values_(property->values, property->values + property->count_values), - blobs_(property->blob_ids, property->blob_ids + property->count_blobs) -{ - if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) - type_ = TypeRange; - else if (drm_property_type_is(property, DRM_MODE_PROP_ENUM)) - type_ = TypeEnum; - else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) - type_ = TypeBlob; - else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) - type_ = TypeBitmask; - else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) - type_ = TypeObject; - else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) - type_ = TypeSignedRange; - else - type_ = TypeUnknown; - - for (int i = 0; i < property->count_enums; ++i) - enums_[property->enums[i].value] = property->enums[i].name; -} - -Blob::Blob(Device *dev, const libcamera::Span &data) - : Object(dev, 0, Object::TypeBlob) -{ - drmModeCreatePropertyBlob(dev->fd(), data.data(), data.size(), &id_); -} - -Blob::~Blob() -{ - if (isValid()) - drmModeDestroyPropertyBlob(device()->fd(), id()); -} - -Mode::Mode(const drmModeModeInfo &mode) - : drmModeModeInfo(mode) -{ -} - -std::unique_ptr Mode::toBlob(Device *dev) const -{ - libcamera::Span data{ reinterpret_cast(this), - sizeof(*this) }; - return std::make_unique(dev, data); -} - -Crtc::Crtc(Device *dev, const drmModeCrtc *crtc, unsigned int index) - : Object(dev, crtc->crtc_id, Object::TypeCrtc), index_(index) -{ -} - -Encoder::Encoder(Device *dev, const drmModeEncoder *encoder) - : Object(dev, encoder->encoder_id, Object::TypeEncoder), - type_(encoder->encoder_type) -{ - const std::list &crtcs = dev->crtcs(); - possibleCrtcs_.reserve(crtcs.size()); - - for (const Crtc &crtc : crtcs) { - if (encoder->possible_crtcs & (1 << crtc.index())) - possibleCrtcs_.push_back(&crtc); - } - - possibleCrtcs_.shrink_to_fit(); -} - -namespace { - -const std::map connectorTypeNames{ - { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, - { DRM_MODE_CONNECTOR_VGA, "VGA" }, - { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, - { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, - { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, - { DRM_MODE_CONNECTOR_Composite, "Composite" }, - { DRM_MODE_CONNECTOR_SVIDEO, "S-Video" }, - { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, - { DRM_MODE_CONNECTOR_Component, "Component" }, - { DRM_MODE_CONNECTOR_9PinDIN, "9-Pin-DIN" }, - { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, - { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, - { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, - { DRM_MODE_CONNECTOR_TV, "TV" }, - { DRM_MODE_CONNECTOR_eDP, "eDP" }, - { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, - { DRM_MODE_CONNECTOR_DSI, "DSI" }, - { DRM_MODE_CONNECTOR_DPI, "DPI" }, -}; - -} /* namespace */ - -Connector::Connector(Device *dev, const drmModeConnector *connector) - : Object(dev, connector->connector_id, Object::TypeConnector), - type_(connector->connector_type) -{ - auto typeName = connectorTypeNames.find(connector->connector_type); - if (typeName == connectorTypeNames.end()) { - std::cerr - << "Invalid connector type " - << connector->connector_type << std::endl; - typeName = connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); - } - - name_ = std::string(typeName->second) + "-" - + std::to_string(connector->connector_type_id); - - switch (connector->connection) { - case DRM_MODE_CONNECTED: - status_ = Status::Connected; - break; - - case DRM_MODE_DISCONNECTED: - status_ = Status::Disconnected; - break; - - case DRM_MODE_UNKNOWNCONNECTION: - default: - status_ = Status::Unknown; - break; - } - - const std::list &encoders = dev->encoders(); - - encoders_.reserve(connector->count_encoders); - - for (int i = 0; i < connector->count_encoders; ++i) { - uint32_t encoderId = connector->encoders[i]; - auto encoder = std::find_if(encoders.begin(), encoders.end(), - [=](const Encoder &e) { - return e.id() == encoderId; - }); - if (encoder == encoders.end()) { - std::cerr - << "Encoder " << encoderId << " not found" - << std::endl; - continue; - } - - encoders_.push_back(&*encoder); - } - - encoders_.shrink_to_fit(); - - modes_ = { connector->modes, connector->modes + connector->count_modes }; -} - -Plane::Plane(Device *dev, const drmModePlane *plane) - : Object(dev, plane->plane_id, Object::TypePlane), - possibleCrtcsMask_(plane->possible_crtcs) -{ - formats_ = { plane->formats, plane->formats + plane->count_formats }; - - const std::list &crtcs = dev->crtcs(); - possibleCrtcs_.reserve(crtcs.size()); - - for (const Crtc &crtc : crtcs) { - if (plane->possible_crtcs & (1 << crtc.index())) - possibleCrtcs_.push_back(&crtc); - } - - possibleCrtcs_.shrink_to_fit(); -} - -bool Plane::supportsFormat(const libcamera::PixelFormat &format) const -{ - return std::find(formats_.begin(), formats_.end(), format.fourcc()) - != formats_.end(); -} - -int Plane::setup() -{ - const PropertyValue *pv = propertyValue("type"); - if (!pv) - return -EINVAL; - - switch (pv->value()) { - case DRM_PLANE_TYPE_OVERLAY: - type_ = TypeOverlay; - break; - - case DRM_PLANE_TYPE_PRIMARY: - type_ = TypePrimary; - break; - - case DRM_PLANE_TYPE_CURSOR: - type_ = TypeCursor; - break; - - default: - return -EINVAL; - } - - return 0; -} - -FrameBuffer::FrameBuffer(Device *dev) - : Object(dev, 0, Object::TypeFb) -{ -} - -FrameBuffer::~FrameBuffer() -{ - for (const auto &plane : planes_) { - struct drm_gem_close gem_close = { - .handle = plane.second.handle, - .pad = 0, - }; - int ret; - - do { - ret = ioctl(device()->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - - if (ret == -1) { - ret = -errno; - std::cerr - << "Failed to close GEM object: " - << strerror(-ret) << std::endl; - } - } - - drmModeRmFB(device()->fd(), id()); -} - -AtomicRequest::AtomicRequest(Device *dev) - : dev_(dev), valid_(true) -{ - request_ = drmModeAtomicAlloc(); - if (!request_) - valid_ = false; -} - -AtomicRequest::~AtomicRequest() -{ - if (request_) - drmModeAtomicFree(request_); -} - -int AtomicRequest::addProperty(const Object *object, const std::string &property, - uint64_t value) -{ - if (!valid_) - return -EINVAL; - - const Property *prop = object->property(property); - if (!prop) { - valid_ = false; - return -EINVAL; - } - - return addProperty(object->id(), prop->id(), value); -} - -int AtomicRequest::addProperty(const Object *object, const std::string &property, - std::unique_ptr blob) -{ - if (!valid_) - return -EINVAL; - - const Property *prop = object->property(property); - if (!prop) { - valid_ = false; - return -EINVAL; - } - - int ret = addProperty(object->id(), prop->id(), blob->id()); - if (ret < 0) - return ret; - - blobs_.emplace_back(std::move(blob)); - - return 0; -} - -int AtomicRequest::addProperty(uint32_t object, uint32_t property, uint64_t value) -{ - int ret = drmModeAtomicAddProperty(request_, object, property, value); - if (ret < 0) { - valid_ = false; - return ret; - } - - return 0; -} - -int AtomicRequest::commit(unsigned int flags) -{ - if (!valid_) - return -EINVAL; - - uint32_t drmFlags = 0; - if (flags & FlagAllowModeset) - drmFlags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - if (flags & FlagAsync) - drmFlags |= DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; - if (flags & FlagTestOnly) - drmFlags |= DRM_MODE_ATOMIC_TEST_ONLY; - - return drmModeAtomicCommit(dev_->fd(), request_, drmFlags, this); -} - -Device::Device() - : fd_(-1) -{ -} - -Device::~Device() -{ - if (fd_ != -1) - drmClose(fd_); -} - -int Device::init() -{ - int ret = openCard(); - if (ret < 0) { - std::cerr << "Failed to open any DRM/KMS device: " - << strerror(-ret) << std::endl; - return ret; - } - - /* - * Enable the atomic APIs. This also automatically enables the - * universal planes API. - */ - ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); - if (ret < 0) { - ret = -errno; - std::cerr - << "Failed to enable atomic capability: " - << strerror(-ret) << std::endl; - return ret; - } - - /* List all the resources. */ - ret = getResources(); - if (ret < 0) - return ret; - - EventLoop::instance()->addFdEvent(fd_, EventLoop::Read, - std::bind(&Device::drmEvent, this)); - - return 0; -} - -int Device::openCard() -{ - const std::string dirName = "/dev/dri/"; - bool found = false; - int ret; - - /* - * Open the first DRM/KMS device beginning with /dev/dri/card. The - * libdrm drmOpen*() functions require either a module name or a bus ID, - * which we don't have, so bypass them. The automatic module loading and - * device node creation from drmOpen() is of no practical use as any - * modern system will handle that through udev or an equivalent - * component. - */ - DIR *folder = opendir(dirName.c_str()); - if (!folder) { - ret = -errno; - std::cerr << "Failed to open " << dirName - << " directory: " << strerror(-ret) << std::endl; - return ret; - } - - for (struct dirent *res; (res = readdir(folder));) { - uint64_t cap; - - if (strncmp(res->d_name, "card", 4)) - continue; - - const std::string devName = dirName + res->d_name; - fd_ = open(devName.c_str(), O_RDWR | O_CLOEXEC); - if (fd_ < 0) { - ret = -errno; - std::cerr << "Failed to open DRM/KMS device " << devName << ": " - << strerror(-ret) << std::endl; - continue; - } - - /* - * Skip devices that don't support the modeset API, to avoid - * selecting a DRM device corresponding to a GPU. There is no - * modeset capability, but the kernel returns an error for most - * caps if mode setting isn't support by the driver. The - * DRM_CAP_DUMB_BUFFER capability is one of those, other would - * do as well. The capability value itself isn't relevant. - */ - ret = drmGetCap(fd_, DRM_CAP_DUMB_BUFFER, &cap); - if (ret < 0) { - drmClose(fd_); - fd_ = -1; - continue; - } - - found = true; - break; - } - - closedir(folder); - - return found ? 0 : -ENOENT; -} - -int Device::getResources() -{ - int ret; - - std::unique_ptr resources{ - drmModeGetResources(fd_), - &drmModeFreeResources - }; - if (!resources) { - ret = -errno; - std::cerr - << "Failed to get DRM/KMS resources: " - << strerror(-ret) << std::endl; - return ret; - } - - for (int i = 0; i < resources->count_crtcs; ++i) { - drmModeCrtc *crtc = drmModeGetCrtc(fd_, resources->crtcs[i]); - if (!crtc) { - ret = -errno; - std::cerr - << "Failed to get CRTC: " << strerror(-ret) - << std::endl; - return ret; - } - - crtcs_.emplace_back(this, crtc, i); - drmModeFreeCrtc(crtc); - - Crtc &obj = crtcs_.back(); - objects_[obj.id()] = &obj; - } - - for (int i = 0; i < resources->count_encoders; ++i) { - drmModeEncoder *encoder = - drmModeGetEncoder(fd_, resources->encoders[i]); - if (!encoder) { - ret = -errno; - std::cerr - << "Failed to get encoder: " << strerror(-ret) - << std::endl; - return ret; - } - - encoders_.emplace_back(this, encoder); - drmModeFreeEncoder(encoder); - - Encoder &obj = encoders_.back(); - objects_[obj.id()] = &obj; - } - - for (int i = 0; i < resources->count_connectors; ++i) { - drmModeConnector *connector = - drmModeGetConnector(fd_, resources->connectors[i]); - if (!connector) { - ret = -errno; - std::cerr - << "Failed to get connector: " << strerror(-ret) - << std::endl; - return ret; - } - - connectors_.emplace_back(this, connector); - drmModeFreeConnector(connector); - - Connector &obj = connectors_.back(); - objects_[obj.id()] = &obj; - } - - std::unique_ptr planes{ - drmModeGetPlaneResources(fd_), - &drmModeFreePlaneResources - }; - if (!planes) { - ret = -errno; - std::cerr - << "Failed to get DRM/KMS planes: " - << strerror(-ret) << std::endl; - return ret; - } - - for (uint32_t i = 0; i < planes->count_planes; ++i) { - drmModePlane *plane = - drmModeGetPlane(fd_, planes->planes[i]); - if (!plane) { - ret = -errno; - std::cerr - << "Failed to get plane: " << strerror(-ret) - << std::endl; - return ret; - } - - planes_.emplace_back(this, plane); - drmModeFreePlane(plane); - - Plane &obj = planes_.back(); - objects_[obj.id()] = &obj; - } - - /* Set the possible planes for each CRTC. */ - for (Crtc &crtc : crtcs_) { - for (const Plane &plane : planes_) { - if (plane.possibleCrtcsMask_ & (1 << crtc.index())) - crtc.planes_.push_back(&plane); - } - } - - /* Collect all property IDs and create Property instances. */ - std::set properties; - for (const auto &object : objects_) { - for (const PropertyValue &value : object.second->properties()) - properties.insert(value.id()); - } - - for (uint32_t id : properties) { - drmModePropertyRes *property = drmModeGetProperty(fd_, id); - if (!property) { - ret = -errno; - std::cerr - << "Failed to get property: " << strerror(-ret) - << std::endl; - continue; - } - - properties_.emplace_back(this, property); - drmModeFreeProperty(property); - - Property &obj = properties_.back(); - objects_[obj.id()] = &obj; - } - - /* Finally, perform all delayed setup of mode objects. */ - for (auto &object : objects_) { - ret = object.second->setup(); - if (ret < 0) { - std::cerr - << "Failed to setup object " << object.second->id() - << ": " << strerror(-ret) << std::endl; - return ret; - } - } - - return 0; -} - -const Object *Device::object(uint32_t id) -{ - const auto iter = objects_.find(id); - if (iter == objects_.end()) - return nullptr; - - return iter->second; -} - -std::unique_ptr Device::createFrameBuffer( - const libcamera::FrameBuffer &buffer, - const libcamera::PixelFormat &format, - const libcamera::Size &size, - const std::array &strides) -{ - std::unique_ptr fb{ new FrameBuffer(this) }; - - uint32_t handles[4] = {}; - uint32_t offsets[4] = {}; - int ret; - - const std::vector &planes = buffer.planes(); - - unsigned int i = 0; - for (const libcamera::FrameBuffer::Plane &plane : planes) { - int fd = plane.fd.get(); - uint32_t handle; - - auto iter = fb->planes_.find(fd); - if (iter == fb->planes_.end()) { - ret = drmPrimeFDToHandle(fd_, plane.fd.get(), &handle); - if (ret < 0) { - ret = -errno; - std::cerr - << "Unable to import framebuffer dmabuf: " - << strerror(-ret) << std::endl; - return nullptr; - } - - fb->planes_[fd] = { handle }; - } else { - handle = iter->second.handle; - } - - handles[i] = handle; - offsets[i] = plane.offset; - ++i; - } - - ret = drmModeAddFB2(fd_, size.width, size.height, format.fourcc(), handles, - strides.data(), offsets, &fb->id_, 0); - if (ret < 0) { - ret = -errno; - std::cerr - << "Failed to add framebuffer: " - << strerror(-ret) << std::endl; - return nullptr; - } - - return fb; -} - -void Device::drmEvent() -{ - drmEventContext ctx{}; - ctx.version = DRM_EVENT_CONTEXT_VERSION; - ctx.page_flip_handler = &Device::pageFlipComplete; - - drmHandleEvent(fd_, &ctx); -} - -void Device::pageFlipComplete([[maybe_unused]] int fd, - [[maybe_unused]] unsigned int sequence, - [[maybe_unused]] unsigned int tv_sec, - [[maybe_unused]] unsigned int tv_usec, - void *user_data) -{ - AtomicRequest *request = static_cast(user_data); - request->device()->requestComplete.emit(request); -} - -} /* namespace DRM */ diff --git a/src/cam/drm.h b/src/cam/drm.h deleted file mode 100644 index ebaea04d..00000000 --- a/src/cam/drm.h +++ /dev/null @@ -1,334 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * drm.h - DRM/KMS Helpers - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace libcamera { -class FrameBuffer; -class PixelFormat; -class Size; -} /* namespace libcamera */ - -namespace DRM { - -class Device; -class Plane; -class Property; -class PropertyValue; - -class Object -{ -public: - enum Type { - TypeCrtc = DRM_MODE_OBJECT_CRTC, - TypeConnector = DRM_MODE_OBJECT_CONNECTOR, - TypeEncoder = DRM_MODE_OBJECT_ENCODER, - TypeMode = DRM_MODE_OBJECT_MODE, - TypeProperty = DRM_MODE_OBJECT_PROPERTY, - TypeFb = DRM_MODE_OBJECT_FB, - TypeBlob = DRM_MODE_OBJECT_BLOB, - TypePlane = DRM_MODE_OBJECT_PLANE, - TypeAny = DRM_MODE_OBJECT_ANY, - }; - - Object(Device *dev, uint32_t id, Type type); - virtual ~Object(); - - Device *device() const { return dev_; } - uint32_t id() const { return id_; } - Type type() const { return type_; } - - const Property *property(const std::string &name) const; - const PropertyValue *propertyValue(const std::string &name) const; - const std::vector &properties() const { return properties_; } - -protected: - virtual int setup() - { - return 0; - } - - uint32_t id_; - -private: - friend Device; - - Device *dev_; - Type type_; - std::vector properties_; -}; - -class Property : public Object -{ -public: - enum Type { - TypeUnknown = 0, - TypeRange, - TypeEnum, - TypeBlob, - TypeBitmask, - TypeObject, - TypeSignedRange, - }; - - Property(Device *dev, drmModePropertyRes *property); - - Type type() const { return type_; } - const std::string &name() const { return name_; } - - bool isImmutable() const { return flags_ & DRM_MODE_PROP_IMMUTABLE; } - - const std::vector values() const { return values_; } - const std::map &enums() const { return enums_; } - const std::vector blobs() const { return blobs_; } - -private: - Type type_; - std::string name_; - uint32_t flags_; - std::vector values_; - std::map enums_; - std::vector blobs_; -}; - -class PropertyValue -{ -public: - PropertyValue(uint32_t id, uint64_t value) - : id_(id), value_(value) - { - } - - uint32_t id() const { return id_; } - uint32_t value() const { return value_; } - -private: - uint32_t id_; - uint64_t value_; -}; - -class Blob : public Object -{ -public: - Blob(Device *dev, const libcamera::Span &data); - ~Blob(); - - bool isValid() const { return id() != 0; } -}; - -class Mode : public drmModeModeInfo -{ -public: - Mode(const drmModeModeInfo &mode); - - std::unique_ptr toBlob(Device *dev) const; -}; - -class Crtc : public Object -{ -public: - Crtc(Device *dev, const drmModeCrtc *crtc, unsigned int index); - - unsigned int index() const { return index_; } - const std::vector &planes() const { return planes_; } - -private: - friend Device; - - unsigned int index_; - std::vector planes_; -}; - -class Encoder : public Object -{ -public: - Encoder(Device *dev, const drmModeEncoder *encoder); - - uint32_t type() const { return type_; } - - const std::vector &possibleCrtcs() const { return possibleCrtcs_; } - -private: - uint32_t type_; - std::vector possibleCrtcs_; -}; - -class Connector : public Object -{ -public: - enum Status { - Connected, - Disconnected, - Unknown, - }; - - Connector(Device *dev, const drmModeConnector *connector); - - uint32_t type() const { return type_; } - const std::string &name() const { return name_; } - - Status status() const { return status_; } - - const std::vector &encoders() const { return encoders_; } - const std::vector &modes() const { return modes_; } - -private: - uint32_t type_; - std::string name_; - Status status_; - std::vector encoders_; - std::vector modes_; -}; - -class Plane : public Object -{ -public: - enum Type { - TypeOverlay, - TypePrimary, - TypeCursor, - }; - - Plane(Device *dev, const drmModePlane *plane); - - Type type() const { return type_; } - const std::vector &formats() const { return formats_; } - const std::vector &possibleCrtcs() const { return possibleCrtcs_; } - - bool supportsFormat(const libcamera::PixelFormat &format) const; - -protected: - int setup() override; - -private: - friend class Device; - - Type type_; - std::vector formats_; - std::vector possibleCrtcs_; - uint32_t possibleCrtcsMask_; -}; - -class FrameBuffer : public Object -{ -public: - struct Plane { - uint32_t handle; - }; - - ~FrameBuffer(); - -private: - friend class Device; - - FrameBuffer(Device *dev); - - std::map planes_; -}; - -class AtomicRequest -{ -public: - enum Flags { - FlagAllowModeset = (1 << 0), - FlagAsync = (1 << 1), - FlagTestOnly = (1 << 2), - }; - - AtomicRequest(Device *dev); - ~AtomicRequest(); - - Device *device() const { return dev_; } - bool isValid() const { return valid_; } - - int addProperty(const Object *object, const std::string &property, - uint64_t value); - int addProperty(const Object *object, const std::string &property, - std::unique_ptr blob); - int commit(unsigned int flags = 0); - -private: - AtomicRequest(const AtomicRequest &) = delete; - AtomicRequest(const AtomicRequest &&) = delete; - AtomicRequest &operator=(const AtomicRequest &) = delete; - AtomicRequest &operator=(const AtomicRequest &&) = delete; - - int addProperty(uint32_t object, uint32_t property, uint64_t value); - - Device *dev_; - bool valid_; - drmModeAtomicReq *request_; - std::list> blobs_; -}; - -class Device -{ -public: - Device(); - ~Device(); - - int init(); - - int fd() const { return fd_; } - - const std::list &crtcs() const { return crtcs_; } - const std::list &encoders() const { return encoders_; } - const std::list &connectors() const { return connectors_; } - const std::list &planes() const { return planes_; } - const std::list &properties() const { return properties_; } - - const Object *object(uint32_t id); - - std::unique_ptr createFrameBuffer( - const libcamera::FrameBuffer &buffer, - const libcamera::PixelFormat &format, - const libcamera::Size &size, - const std::array &strides); - - libcamera::Signal requestComplete; - -private: - Device(const Device &) = delete; - Device(const Device &&) = delete; - Device &operator=(const Device &) = delete; - Device &operator=(const Device &&) = delete; - - int openCard(); - int getResources(); - - void drmEvent(); - static void pageFlipComplete(int fd, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data); - - int fd_; - - std::list crtcs_; - std::list encoders_; - std::list connectors_; - std::list planes_; - std::list properties_; - - std::map objects_; -}; - -} /* namespace DRM */ diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp deleted file mode 100644 index cb83845c..00000000 --- a/src/cam/event_loop.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * event_loop.cpp - cam - Event loop - */ - -#include "event_loop.h" - -#include -#include -#include -#include - -EventLoop *EventLoop::instance_ = nullptr; - -EventLoop::EventLoop() -{ - assert(!instance_); - - evthread_use_pthreads(); - base_ = event_base_new(); - instance_ = this; -} - -EventLoop::~EventLoop() -{ - instance_ = nullptr; - - events_.clear(); - event_base_free(base_); - libevent_global_shutdown(); -} - -EventLoop *EventLoop::instance() -{ - return instance_; -} - -int EventLoop::exec() -{ - exitCode_ = -1; - event_base_loop(base_, EVLOOP_NO_EXIT_ON_EMPTY); - return exitCode_; -} - -void EventLoop::exit(int code) -{ - exitCode_ = code; - event_base_loopbreak(base_); -} - -void EventLoop::callLater(const std::function &func) -{ - { - std::unique_lock locker(lock_); - calls_.push_back(func); - } - - event_base_once(base_, -1, EV_TIMEOUT, dispatchCallback, this, nullptr); -} - -void EventLoop::addFdEvent(int fd, EventType type, - const std::function &callback) -{ - std::unique_ptr event = std::make_unique(callback); - short events = (type & Read ? EV_READ : 0) - | (type & Write ? EV_WRITE : 0) - | EV_PERSIST; - - event->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch, - event.get()); - if (!event->event_) { - std::cerr << "Failed to create event for fd " << fd << std::endl; - return; - } - - int ret = event_add(event->event_, nullptr); - if (ret < 0) { - std::cerr << "Failed to add event for fd " << fd << std::endl; - return; - } - - events_.push_back(std::move(event)); -} - -void EventLoop::addTimerEvent(const std::chrono::microseconds period, - const std::function &callback) -{ - std::unique_ptr event = std::make_unique(callback); - event->event_ = event_new(base_, -1, EV_PERSIST, &EventLoop::Event::dispatch, - event.get()); - if (!event->event_) { - std::cerr << "Failed to create timer event" << std::endl; - return; - } - - struct timeval tv; - tv.tv_sec = period.count() / 1000000ULL; - tv.tv_usec = period.count() % 1000000ULL; - - int ret = event_add(event->event_, &tv); - if (ret < 0) { - std::cerr << "Failed to add timer event" << std::endl; - return; - } - - events_.push_back(std::move(event)); -} - -void EventLoop::dispatchCallback([[maybe_unused]] evutil_socket_t fd, - [[maybe_unused]] short flags, void *param) -{ - EventLoop *loop = static_cast(param); - loop->dispatchCall(); -} - -void EventLoop::dispatchCall() -{ - std::function call; - - { - std::unique_lock locker(lock_); - if (calls_.empty()) - return; - - call = calls_.front(); - calls_.pop_front(); - } - - call(); -} - -EventLoop::Event::Event(const std::function &callback) - : callback_(callback), event_(nullptr) -{ -} - -EventLoop::Event::~Event() -{ - event_del(event_); - event_free(event_); -} - -void EventLoop::Event::dispatch([[maybe_unused]] int fd, - [[maybe_unused]] short events, void *arg) -{ - Event *event = static_cast(arg); - event->callback_(); -} diff --git a/src/cam/event_loop.h b/src/cam/event_loop.h deleted file mode 100644 index ef79e8e5..00000000 --- a/src/cam/event_loop.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * event_loop.h - cam - Event loop - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -struct event_base; - -class EventLoop -{ -public: - enum EventType { - Read = 1, - Write = 2, - }; - - EventLoop(); - ~EventLoop(); - - static EventLoop *instance(); - - int exec(); - void exit(int code = 0); - - void callLater(const std::function &func); - - void addFdEvent(int fd, EventType type, - const std::function &handler); - - using duration = std::chrono::steady_clock::duration; - void addTimerEvent(const std::chrono::microseconds period, - const std::function &handler); - -private: - struct Event { - Event(const std::function &callback); - ~Event(); - - static void dispatch(int fd, short events, void *arg); - - std::function callback_; - struct event *event_; - }; - - static EventLoop *instance_; - - struct event_base *base_; - int exitCode_; - - std::list> calls_; - std::list> events_; - std::mutex lock_; - - static void dispatchCallback(evutil_socket_t fd, short flags, - void *param); - void dispatchCall(); -}; diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp deleted file mode 100644 index 9d60c04e..00000000 --- a/src/cam/file_sink.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * file_sink.cpp - File Sink - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "dng_writer.h" -#include "file_sink.h" -#include "image.h" - -using namespace libcamera; - -FileSink::FileSink(const libcamera::Camera *camera, - const std::map &streamNames, - const std::string &pattern) - : camera_(camera), streamNames_(streamNames), pattern_(pattern) -{ -} - -FileSink::~FileSink() -{ -} - -int FileSink::configure(const libcamera::CameraConfiguration &config) -{ - int ret = FrameSink::configure(config); - if (ret < 0) - return ret; - - return 0; -} - -void FileSink::mapBuffer(FrameBuffer *buffer) -{ - std::unique_ptr image = - Image::fromFrameBuffer(buffer, Image::MapMode::ReadOnly); - assert(image != nullptr); - - mappedBuffers_[buffer] = std::move(image); -} - -bool FileSink::processRequest(Request *request) -{ - for (auto [stream, buffer] : request->buffers()) - writeBuffer(stream, buffer, request->metadata()); - - return true; -} - -void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer, - [[maybe_unused]] const ControlList &metadata) -{ - std::string filename; - size_t pos; - int fd, ret = 0; - - if (!pattern_.empty()) - filename = pattern_; - -#ifdef HAVE_TIFF - bool dng = filename.find(".dng", filename.size() - 4) != std::string::npos; -#endif /* HAVE_TIFF */ - - if (filename.empty() || filename.back() == '/') - filename += "frame-#.bin"; - - pos = filename.find_first_of('#'); - if (pos != std::string::npos) { - std::stringstream ss; - ss << streamNames_[stream] << "-" << std::setw(6) - << std::setfill('0') << buffer->metadata().sequence; - filename.replace(pos, 1, ss.str()); - } - - Image *image = mappedBuffers_[buffer].get(); - -#ifdef HAVE_TIFF - if (dng) { - ret = DNGWriter::write(filename.c_str(), camera_, - stream->configuration(), metadata, - buffer, image->data(0).data()); - if (ret < 0) - std::cerr << "failed to write DNG file `" << filename - << "'" << std::endl; - - return; - } -#endif /* HAVE_TIFF */ - - fd = open(filename.c_str(), O_CREAT | O_WRONLY | - (pos == std::string::npos ? O_APPEND : O_TRUNC), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if (fd == -1) { - ret = -errno; - std::cerr << "failed to open file " << filename << ": " - << strerror(-ret) << std::endl; - return; - } - - for (unsigned int i = 0; i < buffer->planes().size(); ++i) { - const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; - - Span data = image->data(i); - unsigned int length = std::min(meta.bytesused, data.size()); - - if (meta.bytesused > data.size()) - std::cerr << "payload size " << meta.bytesused - << " larger than plane size " << data.size() - << std::endl; - - ret = ::write(fd, data.data(), length); - if (ret < 0) { - ret = -errno; - std::cerr << "write error: " << strerror(-ret) - << std::endl; - break; - } else if (ret != (int)length) { - std::cerr << "write error: only " << ret - << " bytes written instead of " - << length << std::endl; - break; - } - } - - close(fd); -} diff --git a/src/cam/file_sink.h b/src/cam/file_sink.h deleted file mode 100644 index 9ce8b619..00000000 --- a/src/cam/file_sink.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * file_sink.h - File Sink - */ - -#pragma once - -#include -#include -#include - -#include - -#include "frame_sink.h" - -class Image; - -class FileSink : public FrameSink -{ -public: - FileSink(const libcamera::Camera *camera, - const std::map &streamNames, - const std::string &pattern = ""); - ~FileSink(); - - int configure(const libcamera::CameraConfiguration &config) override; - - void mapBuffer(libcamera::FrameBuffer *buffer) override; - - bool processRequest(libcamera::Request *request) override; - -private: - void writeBuffer(const libcamera::Stream *stream, - libcamera::FrameBuffer *buffer, - const libcamera::ControlList &metadata); - - const libcamera::Camera *camera_; - std::map streamNames_; - std::string pattern_; - std::map> mappedBuffers_; -}; diff --git a/src/cam/frame_sink.cpp b/src/cam/frame_sink.cpp deleted file mode 100644 index af21d575..00000000 --- a/src/cam/frame_sink.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * frame_sink.cpp - Base Frame Sink Class - */ - -#include "frame_sink.h" - -/** - * \class FrameSink - * \brief Abstract class to model a consumer of frames - * - * The FrameSink class models the consumer that processes frames after a request - * completes. It receives requests through processRequest(), and processes them - * synchronously or asynchronously. This allows frame sinks to hold onto frames - * for an extended period of time, for instance to display them until a new - * frame arrives. - * - * A frame sink processes whole requests, and is solely responsible for deciding - * how to handle different frame buffers in case multiple streams are captured. - */ - -FrameSink::~FrameSink() -{ -} - -int FrameSink::configure([[maybe_unused]] const libcamera::CameraConfiguration &config) -{ - return 0; -} - -void FrameSink::mapBuffer([[maybe_unused]] libcamera::FrameBuffer *buffer) -{ -} - -int FrameSink::start() -{ - return 0; -} - -int FrameSink::stop() -{ - return 0; -} - -/** - * \fn FrameSink::processRequest() - * \param[in] request The request - * - * This function is called to instruct the sink to process a request. The sink - * may process the request synchronously or queue it for asynchronous - * processing. - * - * When the request is processed synchronously, this function shall return true. - * The \a request shall not be accessed by the FrameSink after the function - * returns. - * - * When the request is processed asynchronously, the FrameSink temporarily takes - * ownership of the \a request. The function shall return false, and the - * FrameSink shall emit the requestProcessed signal when the request processing - * completes. If the stop() function is called before the request processing - * completes, it shall release the request synchronously. - * - * \return True if the request has been processed synchronously, false if - * processing has been queued - */ diff --git a/src/cam/frame_sink.h b/src/cam/frame_sink.h deleted file mode 100644 index ca4347cb..00000000 --- a/src/cam/frame_sink.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * frame_sink.h - Base Frame Sink Class - */ - -#pragma once - -#include - -namespace libcamera { -class CameraConfiguration; -class FrameBuffer; -class Request; -} /* namespace libcamera */ - -class FrameSink -{ -public: - virtual ~FrameSink(); - - virtual int configure(const libcamera::CameraConfiguration &config); - - virtual void mapBuffer(libcamera::FrameBuffer *buffer); - - virtual int start(); - virtual int stop(); - - virtual bool processRequest(libcamera::Request *request) = 0; - libcamera::Signal requestProcessed; -}; diff --git a/src/cam/image.cpp b/src/cam/image.cpp deleted file mode 100644 index fe2cc6da..00000000 --- a/src/cam/image.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * image.cpp - Multi-planar image with access to pixel data - */ - -#include "image.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace libcamera; - -std::unique_ptr Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode mode) -{ - std::unique_ptr image{ new Image() }; - - assert(!buffer->planes().empty()); - - int mmapFlags = 0; - - if (mode & MapMode::ReadOnly) - mmapFlags |= PROT_READ; - - if (mode & MapMode::WriteOnly) - mmapFlags |= PROT_WRITE; - - struct MappedBufferInfo { - uint8_t *address = nullptr; - size_t mapLength = 0; - size_t dmabufLength = 0; - }; - std::map mappedBuffers; - - for (const FrameBuffer::Plane &plane : buffer->planes()) { - const int fd = plane.fd.get(); - if (mappedBuffers.find(fd) == mappedBuffers.end()) { - const size_t length = lseek(fd, 0, SEEK_END); - mappedBuffers[fd] = MappedBufferInfo{ nullptr, 0, length }; - } - - const size_t length = mappedBuffers[fd].dmabufLength; - - if (plane.offset > length || - plane.offset + plane.length > length) { - std::cerr << "plane is out of buffer: buffer length=" - << length << ", plane offset=" << plane.offset - << ", plane length=" << plane.length - << std::endl; - return nullptr; - } - size_t &mapLength = mappedBuffers[fd].mapLength; - mapLength = std::max(mapLength, - static_cast(plane.offset + plane.length)); - } - - for (const FrameBuffer::Plane &plane : buffer->planes()) { - const int fd = plane.fd.get(); - auto &info = mappedBuffers[fd]; - if (!info.address) { - void *address = mmap(nullptr, info.mapLength, mmapFlags, - MAP_SHARED, fd, 0); - if (address == MAP_FAILED) { - int error = -errno; - std::cerr << "Failed to mmap plane: " - << strerror(-error) << std::endl; - return nullptr; - } - - info.address = static_cast(address); - image->maps_.emplace_back(info.address, info.mapLength); - } - - image->planes_.emplace_back(info.address + plane.offset, plane.length); - } - - return image; -} - -Image::Image() = default; - -Image::~Image() -{ - for (Span &map : maps_) - munmap(map.data(), map.size()); -} - -unsigned int Image::numPlanes() const -{ - return planes_.size(); -} - -Span Image::data(unsigned int plane) -{ - assert(plane <= planes_.size()); - return planes_[plane]; -} - -Span Image::data(unsigned int plane) const -{ - assert(plane <= planes_.size()); - return planes_[plane]; -} diff --git a/src/cam/image.h b/src/cam/image.h deleted file mode 100644 index 7953b177..00000000 --- a/src/cam/image.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * image.h - Multi-planar image with access to pixel data - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -#include - -class Image -{ -public: - enum class MapMode { - ReadOnly = 1 << 0, - WriteOnly = 1 << 1, - ReadWrite = ReadOnly | WriteOnly, - }; - - static std::unique_ptr fromFrameBuffer(const libcamera::FrameBuffer *buffer, - MapMode mode); - - ~Image(); - - unsigned int numPlanes() const; - - libcamera::Span data(unsigned int plane); - libcamera::Span data(unsigned int plane) const; - -private: - LIBCAMERA_DISABLE_COPY(Image) - - Image(); - - std::vector> maps_; - std::vector> planes_; -}; - -namespace libcamera { -LIBCAMERA_FLAGS_ENABLE_OPERATORS(Image::MapMode) -} diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp deleted file mode 100644 index 754b061e..00000000 --- a/src/cam/kms_sink.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * kms_sink.cpp - KMS Sink - */ - -#include "kms_sink.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "drm.h" - -KMSSink::KMSSink(const std::string &connectorName) - : connector_(nullptr), crtc_(nullptr), plane_(nullptr), mode_(nullptr) -{ - int ret = dev_.init(); - if (ret < 0) - return; - - /* - * Find the requested connector. If no specific connector is requested, - * pick the first connected connector or, if no connector is connected, - * the first connector with unknown status. - */ - for (const DRM::Connector &conn : dev_.connectors()) { - if (!connectorName.empty()) { - if (conn.name() != connectorName) - continue; - - connector_ = &conn; - break; - } - - if (conn.status() == DRM::Connector::Connected) { - connector_ = &conn; - break; - } - - if (!connector_ && conn.status() == DRM::Connector::Unknown) - connector_ = &conn; - } - - if (!connector_) { - if (!connectorName.empty()) - std::cerr - << "Connector " << connectorName << " not found" - << std::endl; - else - std::cerr << "No connected connector found" << std::endl; - return; - } - - dev_.requestComplete.connect(this, &KMSSink::requestComplete); -} - -void KMSSink::mapBuffer(libcamera::FrameBuffer *buffer) -{ - std::array strides = {}; - - /* \todo Should libcamera report per-plane strides ? */ - unsigned int uvStrideMultiplier; - - switch (format_) { - case libcamera::formats::NV24: - case libcamera::formats::NV42: - uvStrideMultiplier = 4; - break; - case libcamera::formats::YUV420: - case libcamera::formats::YVU420: - case libcamera::formats::YUV422: - uvStrideMultiplier = 1; - break; - default: - uvStrideMultiplier = 2; - break; - } - - strides[0] = stride_; - for (unsigned int i = 1; i < buffer->planes().size(); ++i) - strides[i] = stride_ * uvStrideMultiplier / 2; - - std::unique_ptr drmBuffer = - dev_.createFrameBuffer(*buffer, format_, size_, strides); - if (!drmBuffer) - return; - - buffers_.emplace(std::piecewise_construct, - std::forward_as_tuple(buffer), - std::forward_as_tuple(std::move(drmBuffer))); -} - -int KMSSink::configure(const libcamera::CameraConfiguration &config) -{ - if (!connector_) - return -EINVAL; - - crtc_ = nullptr; - plane_ = nullptr; - mode_ = nullptr; - - const libcamera::StreamConfiguration &cfg = config.at(0); - - /* Find the best mode for the stream size. */ - const std::vector &modes = connector_->modes(); - - unsigned int cfgArea = cfg.size.width * cfg.size.height; - unsigned int bestDistance = UINT_MAX; - - for (const DRM::Mode &mode : modes) { - unsigned int modeArea = mode.hdisplay * mode.vdisplay; - unsigned int distance = modeArea > cfgArea ? modeArea - cfgArea - : cfgArea - modeArea; - - if (distance < bestDistance) { - mode_ = &mode; - bestDistance = distance; - - /* - * If the sizes match exactly, there will be no better - * match. - */ - if (distance == 0) - break; - } - } - - if (!mode_) { - std::cerr << "No modes\n"; - return -EINVAL; - } - - int ret = configurePipeline(cfg.pixelFormat); - if (ret < 0) - return ret; - - size_ = cfg.size; - stride_ = cfg.stride; - - /* Configure color space. */ - colorEncoding_ = std::nullopt; - colorRange_ = std::nullopt; - - if (cfg.colorSpace->ycbcrEncoding == libcamera::ColorSpace::YcbcrEncoding::None) - return 0; - - /* - * The encoding and range enums are defined in the kernel but not - * exposed in public headers. - */ - enum drm_color_encoding { - DRM_COLOR_YCBCR_BT601, - DRM_COLOR_YCBCR_BT709, - DRM_COLOR_YCBCR_BT2020, - }; - - enum drm_color_range { - DRM_COLOR_YCBCR_LIMITED_RANGE, - DRM_COLOR_YCBCR_FULL_RANGE, - }; - - const DRM::Property *colorEncoding = plane_->property("COLOR_ENCODING"); - const DRM::Property *colorRange = plane_->property("COLOR_RANGE"); - - if (colorEncoding) { - drm_color_encoding encoding; - - switch (cfg.colorSpace->ycbcrEncoding) { - case libcamera::ColorSpace::YcbcrEncoding::Rec601: - default: - encoding = DRM_COLOR_YCBCR_BT601; - break; - case libcamera::ColorSpace::YcbcrEncoding::Rec709: - encoding = DRM_COLOR_YCBCR_BT709; - break; - case libcamera::ColorSpace::YcbcrEncoding::Rec2020: - encoding = DRM_COLOR_YCBCR_BT2020; - break; - } - - for (const auto &[id, name] : colorEncoding->enums()) { - if (id == encoding) { - colorEncoding_ = encoding; - break; - } - } - } - - if (colorRange) { - drm_color_range range; - - switch (cfg.colorSpace->range) { - case libcamera::ColorSpace::Range::Limited: - default: - range = DRM_COLOR_YCBCR_LIMITED_RANGE; - break; - case libcamera::ColorSpace::Range::Full: - range = DRM_COLOR_YCBCR_FULL_RANGE; - break; - } - - for (const auto &[id, name] : colorRange->enums()) { - if (id == range) { - colorRange_ = range; - break; - } - } - } - - if (!colorEncoding_ || !colorRange_) - std::cerr << "Color space " << cfg.colorSpace->toString() - << " not supported by the display device." - << " Colors may be wrong." << std::endl; - - return 0; -} - -int KMSSink::selectPipeline(const libcamera::PixelFormat &format) -{ - /* - * If the requested format has an alpha channel, also consider the X - * variant. - */ - libcamera::PixelFormat xFormat; - - switch (format) { - case libcamera::formats::ABGR8888: - xFormat = libcamera::formats::XBGR8888; - break; - case libcamera::formats::ARGB8888: - xFormat = libcamera::formats::XRGB8888; - break; - case libcamera::formats::BGRA8888: - xFormat = libcamera::formats::BGRX8888; - break; - case libcamera::formats::RGBA8888: - xFormat = libcamera::formats::RGBX8888; - break; - } - - /* - * Find a CRTC and plane suitable for the request format and the - * connector at the end of the pipeline. Restrict the search to primary - * planes for now. - */ - for (const DRM::Encoder *encoder : connector_->encoders()) { - for (const DRM::Crtc *crtc : encoder->possibleCrtcs()) { - for (const DRM::Plane *plane : crtc->planes()) { - if (plane->type() != DRM::Plane::TypePrimary) - continue; - - if (plane->supportsFormat(format)) { - crtc_ = crtc; - plane_ = plane; - format_ = format; - return 0; - } - - if (plane->supportsFormat(xFormat)) { - crtc_ = crtc; - plane_ = plane; - format_ = xFormat; - return 0; - } - } - } - } - - return -EPIPE; -} - -int KMSSink::configurePipeline(const libcamera::PixelFormat &format) -{ - const int ret = selectPipeline(format); - if (ret) { - std::cerr - << "Unable to find display pipeline for format " - << format << std::endl; - - return ret; - } - - std::cout - << "Using KMS plane " << plane_->id() << ", CRTC " << crtc_->id() - << ", connector " << connector_->name() - << " (" << connector_->id() << "), mode " << mode_->hdisplay - << "x" << mode_->vdisplay << "@" << mode_->vrefresh << std::endl; - - return 0; -} - -int KMSSink::start() -{ - std::unique_ptr request; - - int ret = FrameSink::start(); - if (ret < 0) - return ret; - - /* Disable all CRTCs and planes to start from a known valid state. */ - request = std::make_unique(&dev_); - - for (const DRM::Crtc &crtc : dev_.crtcs()) - request->addProperty(&crtc, "ACTIVE", 0); - - for (const DRM::Plane &plane : dev_.planes()) { - request->addProperty(&plane, "CRTC_ID", 0); - request->addProperty(&plane, "FB_ID", 0); - } - - ret = request->commit(DRM::AtomicRequest::FlagAllowModeset); - if (ret < 0) { - std::cerr - << "Failed to disable CRTCs and planes: " - << strerror(-ret) << std::endl; - return ret; - } - - return 0; -} - -int KMSSink::stop() -{ - /* Display pipeline. */ - DRM::AtomicRequest request(&dev_); - - request.addProperty(connector_, "CRTC_ID", 0); - request.addProperty(crtc_, "ACTIVE", 0); - request.addProperty(crtc_, "MODE_ID", 0); - request.addProperty(plane_, "CRTC_ID", 0); - request.addProperty(plane_, "FB_ID", 0); - - int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset); - if (ret < 0) { - std::cerr - << "Failed to stop display pipeline: " - << strerror(-ret) << std::endl; - return ret; - } - - /* Free all buffers. */ - pending_.reset(); - queued_.reset(); - active_.reset(); - buffers_.clear(); - - return FrameSink::stop(); -} - -bool KMSSink::testModeSet(DRM::FrameBuffer *drmBuffer, - const libcamera::Rectangle &src, - const libcamera::Rectangle &dst) -{ - DRM::AtomicRequest drmRequest{ &dev_ }; - - drmRequest.addProperty(connector_, "CRTC_ID", crtc_->id()); - - drmRequest.addProperty(crtc_, "ACTIVE", 1); - drmRequest.addProperty(crtc_, "MODE_ID", mode_->toBlob(&dev_)); - - drmRequest.addProperty(plane_, "CRTC_ID", crtc_->id()); - drmRequest.addProperty(plane_, "FB_ID", drmBuffer->id()); - drmRequest.addProperty(plane_, "SRC_X", src.x << 16); - drmRequest.addProperty(plane_, "SRC_Y", src.y << 16); - drmRequest.addProperty(plane_, "SRC_W", src.width << 16); - drmRequest.addProperty(plane_, "SRC_H", src.height << 16); - drmRequest.addProperty(plane_, "CRTC_X", dst.x); - drmRequest.addProperty(plane_, "CRTC_Y", dst.y); - drmRequest.addProperty(plane_, "CRTC_W", dst.width); - drmRequest.addProperty(plane_, "CRTC_H", dst.height); - - return !drmRequest.commit(DRM::AtomicRequest::FlagAllowModeset | - DRM::AtomicRequest::FlagTestOnly); -} - -bool KMSSink::setupComposition(DRM::FrameBuffer *drmBuffer) -{ - /* - * Test composition options, from most to least desirable, to select the - * best one. - */ - const libcamera::Rectangle framebuffer{ size_ }; - const libcamera::Rectangle display{ 0, 0, mode_->hdisplay, mode_->vdisplay }; - - /* 1. Scale the frame buffer to full screen, preserving aspect ratio. */ - libcamera::Rectangle src = framebuffer; - libcamera::Rectangle dst = display.size().boundedToAspectRatio(framebuffer.size()) - .centeredTo(display.center()); - - if (testModeSet(drmBuffer, src, dst)) { - std::cout << "KMS: full-screen scaled output, square pixels" - << std::endl; - src_ = src; - dst_ = dst; - return true; - } - - /* - * 2. Scale the frame buffer to full screen, without preserving aspect - * ratio. - */ - src = framebuffer; - dst = display; - - if (testModeSet(drmBuffer, src, dst)) { - std::cout << "KMS: full-screen scaled output, non-square pixels" - << std::endl; - src_ = src; - dst_ = dst; - return true; - } - - /* 3. Center the frame buffer on the display. */ - src = display.size().centeredTo(framebuffer.center()).boundedTo(framebuffer); - dst = framebuffer.size().centeredTo(display.center()).boundedTo(display); - - if (testModeSet(drmBuffer, src, dst)) { - std::cout << "KMS: centered output" << std::endl; - src_ = src; - dst_ = dst; - return true; - } - - /* 4. Align the frame buffer on the top-left of the display. */ - src = framebuffer.boundedTo(display); - dst = display.boundedTo(framebuffer); - - if (testModeSet(drmBuffer, src, dst)) { - std::cout << "KMS: top-left aligned output" << std::endl; - src_ = src; - dst_ = dst; - return true; - } - - return false; -} - -bool KMSSink::processRequest(libcamera::Request *camRequest) -{ - /* - * Perform a very crude rate adaptation by simply dropping the request - * if the display queue is full. - */ - if (pending_) - return true; - - libcamera::FrameBuffer *buffer = camRequest->buffers().begin()->second; - auto iter = buffers_.find(buffer); - if (iter == buffers_.end()) - return true; - - DRM::FrameBuffer *drmBuffer = iter->second.get(); - - unsigned int flags = DRM::AtomicRequest::FlagAsync; - std::unique_ptr drmRequest = - std::make_unique(&dev_); - drmRequest->addProperty(plane_, "FB_ID", drmBuffer->id()); - - if (!active_ && !queued_) { - /* Enable the display pipeline on the first frame. */ - if (!setupComposition(drmBuffer)) { - std::cerr << "Failed to setup composition" << std::endl; - return true; - } - - drmRequest->addProperty(connector_, "CRTC_ID", crtc_->id()); - - drmRequest->addProperty(crtc_, "ACTIVE", 1); - drmRequest->addProperty(crtc_, "MODE_ID", mode_->toBlob(&dev_)); - - drmRequest->addProperty(plane_, "CRTC_ID", crtc_->id()); - drmRequest->addProperty(plane_, "SRC_X", src_.x << 16); - drmRequest->addProperty(plane_, "SRC_Y", src_.y << 16); - drmRequest->addProperty(plane_, "SRC_W", src_.width << 16); - drmRequest->addProperty(plane_, "SRC_H", src_.height << 16); - drmRequest->addProperty(plane_, "CRTC_X", dst_.x); - drmRequest->addProperty(plane_, "CRTC_Y", dst_.y); - drmRequest->addProperty(plane_, "CRTC_W", dst_.width); - drmRequest->addProperty(plane_, "CRTC_H", dst_.height); - - if (colorEncoding_) - drmRequest->addProperty(plane_, "COLOR_ENCODING", *colorEncoding_); - if (colorRange_) - drmRequest->addProperty(plane_, "COLOR_RANGE", *colorRange_); - - flags |= DRM::AtomicRequest::FlagAllowModeset; - } - - pending_ = std::make_unique(std::move(drmRequest), camRequest); - - std::lock_guard lock(lock_); - - if (!queued_) { - int ret = pending_->drmRequest_->commit(flags); - if (ret < 0) { - std::cerr - << "Failed to commit atomic request: " - << strerror(-ret) << std::endl; - /* \todo Implement error handling */ - } - - queued_ = std::move(pending_); - } - - return false; -} - -void KMSSink::requestComplete(DRM::AtomicRequest *request) -{ - std::lock_guard lock(lock_); - - assert(queued_ && queued_->drmRequest_.get() == request); - - /* Complete the active request, if any. */ - if (active_) - requestProcessed.emit(active_->camRequest_); - - /* The queued request becomes active. */ - active_ = std::move(queued_); - - /* Queue the pending request, if any. */ - if (pending_) { - pending_->drmRequest_->commit(DRM::AtomicRequest::FlagAsync); - queued_ = std::move(pending_); - } -} diff --git a/src/cam/kms_sink.h b/src/cam/kms_sink.h deleted file mode 100644 index e2c618a1..00000000 --- a/src/cam/kms_sink.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021, Ideas on Board Oy - * - * kms_sink.h - KMS Sink - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "drm.h" -#include "frame_sink.h" - -class KMSSink : public FrameSink -{ -public: - KMSSink(const std::string &connectorName); - - void mapBuffer(libcamera::FrameBuffer *buffer) override; - - int configure(const libcamera::CameraConfiguration &config) override; - int start() override; - int stop() override; - - bool processRequest(libcamera::Request *request) override; - -private: - class Request - { - public: - Request(std::unique_ptr drmRequest, - libcamera::Request *camRequest) - : drmRequest_(std::move(drmRequest)), camRequest_(camRequest) - { - } - - std::unique_ptr drmRequest_; - libcamera::Request *camRequest_; - }; - - int selectPipeline(const libcamera::PixelFormat &format); - int configurePipeline(const libcamera::PixelFormat &format); - bool testModeSet(DRM::FrameBuffer *drmBuffer, - const libcamera::Rectangle &src, - const libcamera::Rectangle &dst); - bool setupComposition(DRM::FrameBuffer *drmBuffer); - - void requestComplete(DRM::AtomicRequest *request); - - DRM::Device dev_; - - const DRM::Connector *connector_; - const DRM::Crtc *crtc_; - const DRM::Plane *plane_; - const DRM::Mode *mode_; - - libcamera::PixelFormat format_; - libcamera::Size size_; - unsigned int stride_; - std::optional colorEncoding_; - std::optional colorRange_; - - libcamera::Rectangle src_; - libcamera::Rectangle dst_; - - std::map> buffers_; - - std::mutex lock_; - std::unique_ptr pending_; - std::unique_ptr queued_; - std::unique_ptr active_; -}; diff --git a/src/cam/main.cpp b/src/cam/main.cpp deleted file mode 100644 index d70130e2..00000000 --- a/src/cam/main.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * main.cpp - cam - The libcamera swiss army knife - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "camera_session.h" -#include "event_loop.h" -#include "main.h" -#include "options.h" -#include "stream_options.h" - -using namespace libcamera; - -class CamApp -{ -public: - CamApp(); - - static CamApp *instance(); - - int init(int argc, char **argv); - void cleanup(); - - int exec(); - void quit(); - -private: - void cameraAdded(std::shared_ptr cam); - void cameraRemoved(std::shared_ptr cam); - void captureDone(); - int parseOptions(int argc, char *argv[]); - int run(); - - static std::string cameraName(const Camera *camera); - - static CamApp *app_; - OptionsParser::Options options_; - - std::unique_ptr cm_; - - std::atomic_uint loopUsers_; - EventLoop loop_; -}; - -CamApp *CamApp::app_ = nullptr; - -CamApp::CamApp() - : loopUsers_(0) -{ - CamApp::app_ = this; -} - -CamApp *CamApp::instance() -{ - return CamApp::app_; -} - -int CamApp::init(int argc, char **argv) -{ - int ret; - - ret = parseOptions(argc, argv); - if (ret < 0) - return ret; - - cm_ = std::make_unique(); - - ret = cm_->start(); - if (ret) { - std::cout << "Failed to start camera manager: " - << strerror(-ret) << std::endl; - return ret; - } - - return 0; -} - -void CamApp::cleanup() -{ - cm_->stop(); -} - -int CamApp::exec() -{ - int ret; - - ret = run(); - cleanup(); - - return ret; -} - -void CamApp::quit() -{ - loop_.exit(); -} - -int CamApp::parseOptions(int argc, char *argv[]) -{ - StreamKeyValueParser streamKeyValue; - - OptionsParser parser; - parser.addOption(OptCamera, OptionString, - "Specify which camera to operate on, by id or by index", "camera", - ArgumentRequired, "camera", true); - parser.addOption(OptHelp, OptionNone, "Display this help message", - "help"); - parser.addOption(OptInfo, OptionNone, - "Display information about stream(s)", "info"); - parser.addOption(OptList, OptionNone, "List all cameras", "list"); - parser.addOption(OptListControls, OptionNone, "List cameras controls", - "list-controls"); - parser.addOption(OptListProperties, OptionNone, "List cameras properties", - "list-properties"); - parser.addOption(OptMonitor, OptionNone, - "Monitor for hotplug and unplug camera events", - "monitor"); - - /* Sub-options of OptCamera: */ - parser.addOption(OptCapture, OptionInteger, - "Capture until interrupted by user or until frames captured", - "capture", ArgumentOptional, "count", false, - OptCamera); -#ifdef HAVE_KMS - parser.addOption(OptDisplay, OptionString, - "Display viewfinder through DRM/KMS on specified connector", - "display", ArgumentOptional, "connector", false, - OptCamera); -#endif - parser.addOption(OptFile, OptionString, - "Write captured frames to disk\n" - "If the file name ends with a '/', it sets the directory in which\n" - "to write files, using the default file name. Otherwise it sets the\n" - "full file path and name. The first '#' character in the file name\n" - "is expanded to the camera index, stream name and frame sequence number.\n" -#ifdef HAVE_TIFF - "If the file name ends with '.dng', then the frame will be written to\n" - "the output file(s) in DNG format.\n" -#endif - "The default file name is 'frame-#.bin'.", - "file", ArgumentOptional, "filename", false, - OptCamera); -#ifdef HAVE_SDL - parser.addOption(OptSDL, OptionNone, "Display viewfinder through SDL", - "sdl", ArgumentNone, "", false, OptCamera); -#endif - parser.addOption(OptStream, &streamKeyValue, - "Set configuration of a camera stream", "stream", true, - OptCamera); - parser.addOption(OptStrictFormats, OptionNone, - "Do not allow requested stream format(s) to be adjusted", - "strict-formats", ArgumentNone, nullptr, false, - OptCamera); - parser.addOption(OptMetadata, OptionNone, - "Print the metadata for completed requests", - "metadata", ArgumentNone, nullptr, false, - OptCamera); - parser.addOption(OptCaptureScript, OptionString, - "Load a capture session configuration script from a file", - "script", ArgumentRequired, "script", false, - OptCamera); - - options_ = parser.parse(argc, argv); - if (!options_.valid()) - return -EINVAL; - - if (options_.empty() || options_.isSet(OptHelp)) { - parser.usage(); - return options_.empty() ? -EINVAL : -EINTR; - } - - return 0; -} - -void CamApp::cameraAdded(std::shared_ptr cam) -{ - std::cout << "Camera Added: " << cam->id() << std::endl; -} - -void CamApp::cameraRemoved(std::shared_ptr cam) -{ - std::cout << "Camera Removed: " << cam->id() << std::endl; -} - -void CamApp::captureDone() -{ - if (--loopUsers_ == 0) - EventLoop::instance()->exit(0); -} - -int CamApp::run() -{ - int ret; - - /* 1. List all cameras. */ - if (options_.isSet(OptList)) { - std::cout << "Available cameras:" << std::endl; - - unsigned int index = 1; - for (const std::shared_ptr &cam : cm_->cameras()) { - std::cout << index << ": " << cameraName(cam.get()) << std::endl; - index++; - } - } - - /* 2. Create the camera sessions. */ - std::vector> sessions; - - if (options_.isSet(OptCamera)) { - unsigned int index = 0; - - for (const OptionValue &camera : options_[OptCamera].toArray()) { - std::unique_ptr session = - std::make_unique(cm_.get(), - camera.toString(), - index, - camera.children()); - if (!session->isValid()) { - std::cout << "Failed to create camera session" << std::endl; - return -EINVAL; - } - - std::cout << "Using camera " << session->camera()->id() - << " as cam" << index << std::endl; - - session->captureDone.connect(this, &CamApp::captureDone); - - sessions.push_back(std::move(session)); - index++; - } - } - - /* 3. Print camera information. */ - if (options_.isSet(OptListControls) || - options_.isSet(OptListProperties) || - options_.isSet(OptInfo)) { - for (const auto &session : sessions) { - if (options_.isSet(OptListControls)) - session->listControls(); - if (options_.isSet(OptListProperties)) - session->listProperties(); - if (options_.isSet(OptInfo)) - session->infoConfiguration(); - } - } - - /* 4. Start capture. */ - for (const auto &session : sessions) { - if (!session->options().isSet(OptCapture)) - continue; - - ret = session->start(); - if (ret) { - std::cout << "Failed to start camera session" << std::endl; - return ret; - } - - loopUsers_++; - } - - /* 5. Enable hotplug monitoring. */ - if (options_.isSet(OptMonitor)) { - std::cout << "Monitoring new hotplug and unplug events" << std::endl; - std::cout << "Press Ctrl-C to interrupt" << std::endl; - - cm_->cameraAdded.connect(this, &CamApp::cameraAdded); - cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved); - - loopUsers_++; - } - - if (loopUsers_) - loop_.exec(); - - /* 6. Stop capture. */ - for (const auto &session : sessions) { - if (!session->options().isSet(OptCapture)) - continue; - - session->stop(); - } - - return 0; -} - -std::string 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. - */ - const auto &location = props.get(properties::Location); - if (location) { - switch (*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) { - /* - * If the camera location is not availble use the camera model - * to build the camera name. - */ - const auto &model = props.get(properties::Model); - if (model) - name = "'" + *model + "' "; - } - - name += "(" + camera->id() + ")"; - - return name; -} - -void signalHandler([[maybe_unused]] int signal) -{ - std::cout << "Exiting" << std::endl; - CamApp::instance()->quit(); -} - -int main(int argc, char **argv) -{ - CamApp app; - int ret; - - ret = app.init(argc, argv); - if (ret) - return ret == -EINTR ? 0 : EXIT_FAILURE; - - struct sigaction sa = {}; - sa.sa_handler = &signalHandler; - sigaction(SIGINT, &sa, nullptr); - - if (app.exec()) - return EXIT_FAILURE; - - return 0; -} diff --git a/src/cam/main.h b/src/cam/main.h deleted file mode 100644 index 526aecec..00000000 --- a/src/cam/main.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * main.h - Cam application - */ - -#pragma once - -enum { - OptCamera = 'c', - OptCapture = 'C', - OptDisplay = 'D', - OptFile = 'F', - OptHelp = 'h', - OptInfo = 'I', - OptList = 'l', - OptListProperties = 'p', - OptMonitor = 'm', - OptSDL = 'S', - OptStream = 's', - OptListControls = 256, - OptStrictFormats = 257, - OptMetadata = 258, - OptCaptureScript = 259, -}; diff --git a/src/cam/meson.build b/src/cam/meson.build deleted file mode 100644 index 06dbea06..00000000 --- a/src/cam/meson.build +++ /dev/null @@ -1,74 +0,0 @@ -# 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([ - 'camera_session.cpp', - 'capture_script.cpp', - 'event_loop.cpp', - 'file_sink.cpp', - 'frame_sink.cpp', - 'image.cpp', - 'main.cpp', - 'options.cpp', - 'stream_options.cpp', -]) - -cam_cpp_args = [] - -libdrm = dependency('libdrm', required : false) -libjpeg = dependency('libjpeg', required : false) -libsdl2 = dependency('SDL2', required : false) -libtiff = dependency('libtiff-4', required : false) - -if libdrm.found() - cam_cpp_args += [ '-DHAVE_KMS' ] - cam_sources += files([ - 'drm.cpp', - 'kms_sink.cpp' - ]) -endif - -if libsdl2.found() - cam_cpp_args += ['-DHAVE_SDL'] - cam_sources += files([ - 'sdl_sink.cpp', - 'sdl_texture.cpp', - 'sdl_texture_yuv.cpp', - ]) - - if libjpeg.found() - cam_cpp_args += ['-DHAVE_LIBJPEG'] - cam_sources += files([ - 'sdl_texture_mjpg.cpp' - ]) - endif -endif - -if libtiff.found() - cam_cpp_args += ['-DHAVE_TIFF'] - cam_sources += files([ - 'dng_writer.cpp', - ]) -endif - -cam = executable('cam', cam_sources, - dependencies : [ - libatomic, - libcamera_public, - libdrm, - libevent, - libjpeg, - libsdl2, - libtiff, - libyaml, - ], - cpp_args : cam_cpp_args, - install : true) diff --git a/src/cam/options.cpp b/src/cam/options.cpp deleted file mode 100644 index 4f7e8691..00000000 --- a/src/cam/options.cpp +++ /dev/null @@ -1,1141 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * options.cpp - cam - Options parsing - */ - -#include -#include -#include -#include -#include - -#include "options.h" - -/** - * \enum OptionArgument - * \brief Indicate if an option takes an argument - * - * \var OptionArgument::ArgumentNone - * \brief The option doesn't accept any argument - * - * \var OptionArgument::ArgumentRequired - * \brief The option requires an argument - * - * \var OptionArgument::ArgumentOptional - * \brief The option accepts an optional argument - */ - -/** - * \enum OptionType - * \brief The type of argument for an option - * - * \var OptionType::OptionNone - * \brief No argument type, used for options that take no argument - * - * \var OptionType::OptionInteger - * \brief Integer argument type, with an optional base prefix (`0` for base 8, - * `0x` for base 16, none for base 10) - * - * \var OptionType::OptionString - * \brief String argument - * - * \var OptionType::OptionKeyValue - * \brief key=value list argument - */ - -/* ----------------------------------------------------------------------------- - * Option - */ - -/** - * \struct Option - * \brief Store metadata about an option - * - * \var Option::opt - * \brief The option identifier - * - * \var Option::type - * \brief The type of the option argument - * - * \var Option::name - * \brief The option name - * - * \var Option::argument - * \brief Whether the option accepts an optional argument, a mandatory - * argument, or no argument at all - * - * \var Option::argumentName - * \brief The argument name used in the help text - * - * \var Option::help - * \brief The help text (may be a multi-line string) - * - * \var Option::keyValueParser - * \brief For options of type OptionType::OptionKeyValue, the key-value parser - * to parse the argument - * - * \var Option::isArray - * \brief Whether the option can appear once or multiple times - * - * \var Option::parent - * \brief The parent option - * - * \var Option::children - * \brief List of child options, storing all options whose parent is this option - * - * \fn Option::hasShortOption() - * \brief Tell if the option has a short option specifier (e.g. `-f`) - * \return True if the option has a short option specifier, false otherwise - * - * \fn Option::hasLongOption() - * \brief Tell if the option has a long option specifier (e.g. `--foo`) - * \return True if the option has a long option specifier, false otherwise - */ -struct Option { - int opt; - OptionType type; - const char *name; - OptionArgument argument; - const char *argumentName; - const char *help; - KeyValueParser *keyValueParser; - bool isArray; - Option *parent; - std::list