/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019, Google Inc. * * capture.cpp - Cam capture */ #include #include #include #include #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 &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 &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 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 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 &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); }