diff options
Diffstat (limited to 'src/cam/camera_session.cpp')
-rw-r--r-- | src/cam/camera_session.cpp | 450 |
1 files changed, 0 insertions, 450 deletions
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 <iomanip> -#include <iostream> -#include <limits.h> -#include <sstream> - -#include <libcamera/control_ids.h> -#include <libcamera/property_ids.h> - -#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<CameraConfiguration> 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<CaptureScript>(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<KMSSink>(options_[OptDisplay].toString()); -#endif - -#ifdef HAVE_SDL - if (options_.isSet(OptSDL)) - sink_ = std::make_unique<SDLSink>(); -#endif - - if (options_.isSet(OptFile)) { - if (!options_[OptFile].toString().empty()) - sink_ = std::make_unique<FileSink>(camera_.get(), streamNames_, - options_[OptFile]); - else - sink_ = std::make_unique<FileSink>(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<FrameBufferAllocator>(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> 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<std::unique_ptr<FrameBuffer>> &buffers = - allocator_->buffers(stream); - const std::unique_ptr<FrameBuffer> &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> &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); -} |