diff options
Diffstat (limited to 'src/cam/capture.cpp')
-rw-r--r-- | src/cam/capture.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp new file mode 100644 index 00000000..a4aa44af --- /dev/null +++ b/src/cam/capture.cpp @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * capture.cpp - Cam capture + */ + +#include <climits> +#include <iomanip> +#include <iostream> +#include <sstream> + +#include "capture.h" +#include "main.h" + +using namespace libcamera; + +Capture::Capture(Camera *camera) + : camera_(camera), writer_(nullptr), last_(0) +{ +} + +int Capture::run(EventLoop *loop, const OptionsParser::Options &options) +{ + int ret; + + if (!camera_) { + std::cout << "Can't capture without a camera" << std::endl; + return -ENODEV; + } + + ret = prepareConfig(options); + if (ret) { + std::cout << "Failed to prepare camera configuration" << std::endl; + return -EINVAL; + } + + ret = camera_->configure(config_.get()); + if (ret < 0) { + std::cout << "Failed to configure camera" << std::endl; + return ret; + } + + ret = camera_->allocateBuffers(); + if (ret) { + std::cerr << "Failed to allocate buffers" << std::endl; + return ret; + } + + camera_->requestCompleted.connect(this, &Capture::requestComplete); + + if (options.isSet(OptFile)) { + if (!options[OptFile].toString().empty()) + writer_ = new BufferWriter(options[OptFile]); + else + writer_ = new BufferWriter(); + } + + ret = capture(loop); + + if (options.isSet(OptFile)) { + delete writer_; + writer_ = nullptr; + } + + camera_->freeBuffers(); + config_.reset(); + + return ret; +} + +int Capture::prepareConfig(const OptionsParser::Options &options) +{ + StreamRoles roles; + + if (options.isSet(OptStream)) { + const std::vector<OptionValue> &streamOptions = + options[OptStream].toArray(); + + /* Use roles and get a default configuration. */ + for (auto const &value : streamOptions) { + KeyValueParser::Options opt = value.toKeyValues(); + + if (!opt.isSet("role")) { + roles.push_back(StreamRole::VideoRecording); + } else if (opt["role"].toString() == "viewfinder") { + roles.push_back(StreamRole::Viewfinder); + } else if (opt["role"].toString() == "video") { + roles.push_back(StreamRole::VideoRecording); + } else if (opt["role"].toString() == "still") { + roles.push_back(StreamRole::StillCapture); + } else { + std::cerr << "Unknown stream role " + << opt["role"].toString() << std::endl; + return -EINVAL; + } + } + } else { + /* If no configuration is provided assume a single video stream. */ + roles.push_back(StreamRole::VideoRecording); + } + + config_ = camera_->generateConfiguration(roles); + if (!config_ || config_->size() != roles.size()) { + std::cerr << "Failed to get default stream configuration" + << std::endl; + return -EINVAL; + } + + /* Apply configuration if explicitly requested. */ + if (options.isSet(OptStream)) { + const std::vector<OptionValue> &streamOptions = + options[OptStream].toArray(); + + unsigned int i = 0; + for (auto const &value : streamOptions) { + KeyValueParser::Options opt = value.toKeyValues(); + StreamConfiguration &cfg = config_->at(i++); + + if (opt.isSet("width")) + cfg.size.width = opt["width"]; + + if (opt.isSet("height")) + cfg.size.height = opt["height"]; + + /* TODO: Translate 4CC string to ID. */ + if (opt.isSet("pixelformat")) + cfg.pixelFormat = opt["pixelformat"]; + } + } + + streamName_.clear(); + for (unsigned int index = 0; index < config_->size(); ++index) { + StreamConfiguration &cfg = config_->at(index); + streamName_[cfg.stream()] = "stream" + std::to_string(index); + } + + return 0; +} + +int Capture::capture(EventLoop *loop) +{ + int ret; + + /* Identify the stream with the least number of buffers. */ + unsigned int nbuffers = UINT_MAX; + for (StreamConfiguration &cfg : *config_) { + Stream *stream = cfg.stream(); + nbuffers = std::min(nbuffers, stream->bufferPool().count()); + } + + /* + * TODO: make cam tool smarter to support still capture by for + * example pushing a button. For now run all streams all the time. + */ + + std::vector<Request *> requests; + for (unsigned int i = 0; i < nbuffers; i++) { + Request *request = camera_->createRequest(); + if (!request) { + std::cerr << "Can't create request" << std::endl; + return -ENOMEM; + } + + std::map<Stream *, Buffer *> map; + for (StreamConfiguration &cfg : *config_) { + Stream *stream = cfg.stream(); + map[stream] = &stream->bufferPool().buffers()[i]; + } + + ret = request->setBuffers(map); + if (ret < 0) { + std::cerr << "Can't set buffers for request" << std::endl; + return ret; + } + + requests.push_back(request); + } + + ret = camera_->start(); + if (ret) { + std::cout << "Failed to start capture" << std::endl; + return ret; + } + + for (Request *request : requests) { + ret = camera_->queueRequest(request); + if (ret < 0) { + std::cerr << "Can't queue request" << std::endl; + return ret; + } + } + + std::cout << "Capture until user interrupts by SIGINT" << std::endl; + ret = loop->exec(); + if (ret) + std::cout << "Failed to run capture loop" << std::endl; + + ret = camera_->stop(); + if (ret) + std::cout << "Failed to stop capture" << std::endl; + + return ret; +} + +void Capture::requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers) +{ + double fps = 0.0; + uint64_t now; + + if (request->status() == Request::RequestCancelled) + return; + + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + now = time.tv_sec * 1000 + time.tv_nsec / 1000000; + fps = now - last_; + fps = last_ && fps ? 1000.0 / fps : 0.0; + last_ = now; + + std::stringstream info; + info << "fps: " << std::fixed << std::setprecision(2) << fps; + + for (auto it = buffers.begin(); it != buffers.end(); ++it) { + Stream *stream = it->first; + Buffer *buffer = it->second; + const std::string &name = streamName_[stream]; + + info << " " << name + << " (" << buffer->index() << ")" + << " seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() + << " bytesused: " << buffer->bytesused(); + + if (writer_) + writer_->write(buffer, name); + } + + std::cout << info.str() << std::endl; + + request = camera_->createRequest(); + if (!request) { + std::cerr << "Can't create request" << std::endl; + return; + } + + request->setBuffers(buffers); + camera_->queueRequest(request); +} |