From patchwork Fri Apr 24 01:16:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 3509 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 34269603F9 for ; Fri, 24 Apr 2020 03:17:06 +0200 (CEST) X-Halon-ID: 48f292dc-85c9-11ea-aeed-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 48f292dc-85c9-11ea-aeed-005056917f90; Fri, 24 Apr 2020 03:16:54 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Apr 2020 03:16:54 +0200 Message-Id: <20200424011656.2889720-2-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.26.0 In-Reply-To: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> References: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/3] cam: Add helper class to parse stream configuration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 24 Apr 2020 01:17:06 -0000 Create a new helper class StreamOptionsParser to parse command line options describing stream configurations. The goal is to share this new class between cam and qcam. The parser inherits from OptionsParser which it aims to replace and allows for a common command line argument syntax to be inserted anywhere in the argument list with addStreamOptions(), still allowing for any short and long option name. The Options data carrier returned inherits from OptionsParser::Options and extends it with new functions; roles() and applyCameraParameters(). The new functions makes it easy to generate and override the default values of a camera configuration. Signed-off-by: Niklas Söderlund --- src/cam/meson.build | 1 + src/cam/stream_options.cpp | 116 +++++++++++++++++++++++++++++++++++++ src/cam/stream_options.h | 63 ++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 src/cam/stream_options.cpp create mode 100644 src/cam/stream_options.h diff --git a/src/cam/meson.build b/src/cam/meson.build index 2419d648bc17e02b..162d6333f94e4851 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -4,6 +4,7 @@ cam_sources = files([ 'event_loop.cpp', 'main.cpp', 'options.cpp', + 'stream_options.cpp', ]) cam = executable('cam', cam_sources, diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp new file mode 100644 index 0000000000000000..22bf61f2c13d10c1 --- /dev/null +++ b/src/cam/stream_options.cpp @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd. + * + * stream_options.cpp - Helper to parse options for stremas + */ +#include "stream_options.h" + +#include + +using namespace libcamera; + +int StreamOptionsParser::Options::applyCameraParameters(CameraConfiguration *config) const +{ + if (!config) { + std::cerr << "No configuration provided" << std::endl; + return -EINVAL; + } + + if (config->size() != streamParameters_.size()) { + std::cerr + << "Number of streams in configuration " + << config->size() + << " does not match number of streams parsed " + << streamParameters_.size() + << std::endl; + return -EINVAL; + } + + unsigned int i = 0; + for (auto const ¶m : streamParameters_) { + StreamConfiguration &cfg = config->at(i++); + + if (param.size.width && param.size.height) + cfg.size = param.size; + + if (param.pixelFormat.isValid()) + cfg.pixelFormat = param.pixelFormat; + } + + return 0; +} + +int StreamOptionsParser::addStreamOptions(int opt, const char *name) +{ + if (opt_) + return -EINVAL; + + opt_ = opt; + + streamKeyValue_.addOption("role", OptionString, + "Role for the stream (viewfinder, video, still, stillraw)", + ArgumentRequired); + streamKeyValue_.addOption("width", OptionInteger, "Width in pixels", + ArgumentRequired); + streamKeyValue_.addOption("height", OptionInteger, "Height in pixels", + ArgumentRequired); + streamKeyValue_.addOption("pixelformat", OptionInteger, "Pixel format", + ArgumentRequired); + + return addOption(opt_, &streamKeyValue_, + "Set configuration of a camera stream", name, true); +} + +StreamOptionsParser::Options StreamOptionsParser::parse(int argc, char *argv[]) +{ + OptionsParser::Options options = OptionsParser::parse(argc, argv); + + if (!opt_ || !options.isSet(opt_)) + return Options(options, {StreamRole::Viewfinder}); + + StreamRoles roles; + std::vector parameters; + + for (auto const &value : options[opt_].toArray()) { + KeyValueParser::Options opts = value.toKeyValues(); + Options::Parameters param; + + if (opts.isSet("width") && opts.isSet("height")) { + param.size.width = opts["width"]; + param.size.height = opts["height"]; + } + + /* TODO: Translate 4CC string to ID. */ + if (opts.isSet("pixelformat")) + param.pixelFormat = PixelFormat(opts["pixelformat"]); + + roles.push_back(parseRole(opts)); + parameters.push_back(param); + } + + return Options(options, roles, parameters); +} + +StreamRole StreamOptionsParser::parseRole(KeyValueParser::Options opts) +{ + if (!opts.isSet("role")) + return StreamRole::Viewfinder; + + std::string role = opts["role"].toString(); + + if (role == "viewfinder") + return StreamRole::Viewfinder; + if (role == "video") + return StreamRole::VideoRecording; + if (role == "still") + return StreamRole::StillCapture; + if (role == "stillraw") + return StreamRole::StillCaptureRaw; + + /* No stream role found, warn and default to viewfinder. */ + std::cerr << "Unknown stream role " << role + << ", adjusted to viewfinder" << std::endl; + + return StreamRole::Viewfinder; +} diff --git a/src/cam/stream_options.h b/src/cam/stream_options.h new file mode 100644 index 0000000000000000..a0f8a456b44fe275 --- /dev/null +++ b/src/cam/stream_options.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Ltd. + * + * stream_options.h - Helper to parse options for stremas + */ +#ifndef __CAM_STREAM_OPTIONS_H__ +#define __CAM_STREAM_OPTIONS_H__ + +#include +#include + +#include "options.h" + +using namespace libcamera; + +class StreamOptionsParser : public OptionsParser +{ +public: + class Options : public OptionsParser::Options + { + public: + struct Parameters { + PixelFormat pixelFormat; + Size size; + }; + + Options() + : OptionsParser::Options() + { + } + + Options(OptionsParser::Options options, StreamRoles roles = {}, + std::vector parameters = {}) + : OptionsParser::Options(options), roles_(roles), + streamParameters_(parameters) + { + } + + const StreamRoles &roles() const { return roles_; } + int applyCameraParameters(CameraConfiguration *config) const; + + private: + StreamRoles roles_; + std::vector streamParameters_; + }; + + StreamOptionsParser() + : opt_(0) + { + } + + int addStreamOptions(int opt, const char *name = nullptr); + Options parse(int argc, char *argv[]); + +private: + StreamRole parseRole(KeyValueParser::Options opts); + + int opt_; + KeyValueParser streamKeyValue_; +}; + +#endif /* __CAM_STREAM_OPTIONS_H__ */ From patchwork Fri Apr 24 01:16:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 3510 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AE5CB603F9 for ; Fri, 24 Apr 2020 03:17:06 +0200 (CEST) X-Halon-ID: 495e2c41-85c9-11ea-aeed-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 495e2c41-85c9-11ea-aeed-005056917f90; Fri, 24 Apr 2020 03:16:55 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Apr 2020 03:16:55 +0200 Message-Id: <20200424011656.2889720-3-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.26.0 In-Reply-To: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> References: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/3] cam: Make use of StreamOptionsParser X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 24 Apr 2020 01:17:06 -0000 Use the StreamOptionsParser helper to parse stream configuration from the command line. Signed-off-by: Niklas Söderlund --- src/cam/main.cpp | 77 +++++------------------------------------------- 1 file changed, 7 insertions(+), 70 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 718740f49762ec7b..b7cc206e6d5176db 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -17,6 +17,7 @@ #include "event_loop.h" #include "main.h" #include "options.h" +#include "stream_options.h" using namespace libcamera; @@ -42,7 +43,7 @@ private: int run(); static CamApp *app_; - OptionsParser::Options options_; + StreamOptionsParser::Options options_; CameraManager *cm_; std::shared_ptr camera_; std::unique_ptr config_; @@ -153,18 +154,7 @@ void CamApp::quit() int CamApp::parseOptions(int argc, char *argv[]) { - KeyValueParser streamKeyValue; - streamKeyValue.addOption("role", OptionString, - "Role for the stream (viewfinder, video, still, stillraw)", - ArgumentRequired); - streamKeyValue.addOption("width", OptionInteger, "Width in pixels", - ArgumentRequired); - streamKeyValue.addOption("height", OptionInteger, "Height in pixels", - ArgumentRequired); - streamKeyValue.addOption("pixelformat", OptionInteger, "Pixel format", - ArgumentRequired); - - OptionsParser parser; + StreamOptionsParser parser; parser.addOption(OptCamera, OptionString, "Specify which camera to operate on, by name or by index", "camera", ArgumentRequired, "camera"); @@ -175,8 +165,7 @@ int CamApp::parseOptions(int argc, char *argv[]) "The first '#' character in the file name is expanded to the stream name and frame sequence number.\n" "The default file name is 'frame-#.bin'.", "file", ArgumentOptional, "filename"); - parser.addOption(OptStream, &streamKeyValue, - "Set configuration of a camera stream", "stream", true); + parser.addStreamOptions(OptStream, "stream"); parser.addOption(OptHelp, OptionNone, "Display this help message", "help"); parser.addOption(OptInfo, OptionNone, @@ -199,67 +188,15 @@ int CamApp::parseOptions(int argc, char *argv[]) int CamApp::prepareConfig() { - 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(); - - std::string role = opt.isSet("role") - ? opt["role"].toString() - : "viewfinder"; - - if (role == "viewfinder") { - roles.push_back(StreamRole::Viewfinder); - } else if (role == "video") { - roles.push_back(StreamRole::VideoRecording); - } else if (role == "still") { - roles.push_back(StreamRole::StillCapture); - } else if (role == "stillraw") { - roles.push_back(StreamRole::StillCaptureRaw); - } else { - std::cerr << "Unknown stream role " - << role << 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()) { + config_ = camera_->generateConfiguration(options_.roles()); + if (!config_ || config_->size() != options_.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 = PixelFormat(opt["pixelformat"]); - } - } + options_.applyCameraParameters(config_.get()); switch (config_->validate()) { case CameraConfiguration::Valid: From patchwork Fri Apr 24 01:16:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 3511 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 48CD062E5D for ; Fri, 24 Apr 2020 03:17:07 +0200 (CEST) X-Halon-ID: 49b29452-85c9-11ea-aeed-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 49b29452-85c9-11ea-aeed-005056917f90; Fri, 24 Apr 2020 03:16:56 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Apr 2020 03:16:56 +0200 Message-Id: <20200424011656.2889720-4-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.26.0 In-Reply-To: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> References: <20200424011656.2889720-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/3] qcam: Make use of StreamOptionsParser X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 24 Apr 2020 01:17:07 -0000 Use the StreamOptionsParser helper to parse stream configuration from the command line. This extends qcam to accept role hints and pixel format in addition to a size. Currently only one viewfinder stream is supported, add a check to keep this behavior. Going forward this restriction will be lifted to support more then one stream. Signed-off-by: Niklas Söderlund --- src/qcam/main.cpp | 19 ++++++------------- src/qcam/main_window.cpp | 34 ++++++++++++++++------------------ src/qcam/main_window.h | 8 ++++---- src/qcam/meson.build | 1 + 4 files changed, 27 insertions(+), 35 deletions(-) diff --git a/src/qcam/main.cpp b/src/qcam/main.cpp index 862d714f467c04f9..734423572aae253c 100644 --- a/src/qcam/main.cpp +++ b/src/qcam/main.cpp @@ -13,8 +13,8 @@ #include +#include "../cam/stream_options.h" #include "main_window.h" -#include "../cam/options.h" void signalHandler(int signal) { @@ -22,24 +22,17 @@ void signalHandler(int signal) qApp->quit(); } -OptionsParser::Options parseOptions(int argc, char *argv[]) +StreamOptionsParser::Options parseOptions(int argc, char *argv[]) { - KeyValueParser sizeParser; - sizeParser.addOption("width", OptionInteger, "Width in pixels", - ArgumentRequired); - sizeParser.addOption("height", OptionInteger, "Height in pixels", - ArgumentRequired); - - OptionsParser parser; + StreamOptionsParser parser; parser.addOption(OptCamera, OptionString, "Specify which camera to operate on", "camera", ArgumentRequired, "camera"); parser.addOption(OptHelp, OptionNone, "Display this help message", "help"); - parser.addOption(OptSize, &sizeParser, "Set the stream size", - "size", true); + parser.addStreamOptions(OptStream, "stream"); - OptionsParser::Options options = parser.parse(argc, argv); + StreamOptionsParser::Options options = parser.parse(argc, argv); if (options.isSet(OptHelp)) parser.usage(); @@ -51,7 +44,7 @@ int main(int argc, char **argv) QApplication app(argc, argv); int ret; - OptionsParser::Options options = parseOptions(argc, argv); + StreamOptionsParser::Options options = parseOptions(argc, argv); if (!options.valid()) return EXIT_FAILURE; if (options.isSet(OptHelp)) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index cf39ed7aceca05b8..dad1b001de770bdc 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -47,7 +47,7 @@ public: } }; -MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) +MainWindow::MainWindow(CameraManager *cm, const StreamOptionsParser::Options &options) : options_(options), cm_(cm), allocator_(nullptr), isCapturing_(false) { int ret; @@ -277,27 +277,22 @@ int MainWindow::startCapture() { int ret; + /* Verify roles are supported. */ + if (options_.roles().size() != 1) { + qWarning() << "Only one stream supported"; + return -EINVAL; + } + + if (options_.roles()[0] != StreamRole::Viewfinder) { + qWarning() << "Only viewfinder supported"; + return -EINVAL; + } + /* Configure the camera. */ - config_ = camera_->generateConfiguration({ StreamRole::Viewfinder }); + config_ = camera_->generateConfiguration(options_.roles()); StreamConfiguration &cfg = config_->at(0); - if (options_.isSet(OptSize)) { - const std::vector &sizeOptions = - options_[OptSize].toArray(); - - /* Set desired stream size if requested. */ - for (const auto &value : sizeOptions) { - KeyValueParser::Options opt = value.toKeyValues(); - - if (opt.isSet("width")) - cfg.size.width = opt["width"]; - - if (opt.isSet("height")) - cfg.size.height = opt["height"]; - } - } - /* Use a format supported by the viewfinder if available. */ std::vector formats = cfg.formats().pixelformats(); for (const PixelFormat &format : viewfinder_->nativeFormats()) { @@ -311,6 +306,9 @@ int MainWindow::startCapture() } } + /* Allow user to override configuration. */ + options_.applyCameraParameters(config_.get()); + CameraConfiguration::Status validation = config_->validate(); if (validation == CameraConfiguration::Invalid) { qWarning() << "Failed to create valid camera configuration"; diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 5d6251c830707a79..f28eff6591169b9f 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -23,7 +23,7 @@ #include #include -#include "../cam/options.h" +#include "../cam/stream_options.h" #include "viewfinder.h" using namespace libcamera; @@ -33,7 +33,7 @@ class QAction; enum { OptCamera = 'c', OptHelp = 'h', - OptSize = 's', + OptStream = 's', }; class MainWindow : public QMainWindow @@ -41,7 +41,7 @@ class MainWindow : public QMainWindow Q_OBJECT public: - MainWindow(CameraManager *cm, const OptionsParser::Options &options); + MainWindow(CameraManager *cm, const StreamOptionsParser::Options &options); ~MainWindow(); bool event(QEvent *e) override; @@ -81,7 +81,7 @@ private: QTimer titleTimer_; /* Options */ - const OptionsParser::Options &options_; + const StreamOptionsParser::Options &options_; /* Camera manager, camera, configuration and buffers */ CameraManager *cm_; diff --git a/src/qcam/meson.build b/src/qcam/meson.build index c256d06f8ccfc0ae..895264be4a3388f4 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -3,6 +3,7 @@ qcam_sources = files([ 'main.cpp', 'main_window.cpp', '../cam/options.cpp', + '../cam/stream_options.cpp', 'viewfinder.cpp', ])