[PATCH v2 2/2] apps: cam: Add support for loading configuration from capture script
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Mon Oct 7 23:39:12 CEST 2024
Hi Paul,
Thank you for the patch.
On Fri, Oct 04, 2024 at 09:05:17PM +0900, Paul Elder wrote:
> Add support to the cam application for loading the camera configuration
> from a capture script. These can be written manually, or dumped from a
> capture session via the LIBCAMERA_DUMP_CAPTURE_SCRIPT environment
> variable.
>
> If any configuration options are specified by command line parameters,
> those will take precedence.
>
> Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
>
> ---
> Changes in v2:
> - clean up code
> ---
> src/apps/cam/camera_session.cpp | 26 +++--
> src/apps/cam/capture_script.cpp | 163 ++++++++++++++++++++++++++++++++
> src/apps/cam/capture_script.h | 9 ++
> src/apps/cam/main.cpp | 4 +-
> 4 files changed, 191 insertions(+), 11 deletions(-)
>
> diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
> index edc49b875450..695b313c3779 100644
> --- a/src/apps/cam/camera_session.cpp
> +++ b/src/apps/cam/camera_session.cpp
> @@ -70,6 +70,22 @@ CameraSession::CameraSession(CameraManager *cm,
> return;
> }
>
> + /*
> + * Parse the capture script first to populate the configuration, and
> + * let command line arguments take precedence.
> + */
> + 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;
> + }
> +
> + script_->populateConfiguration(*config);
> + }
> +
> if (options_.isSet(OptOrientation)) {
> std::string orientOpt = options_[OptOrientation].toString();
> static const std::map<std::string, libcamera::Orientation> orientations{
> @@ -119,16 +135,6 @@ CameraSession::CameraSession(CameraManager *cm,
> }
> #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;
> diff --git a/src/apps/cam/capture_script.cpp b/src/apps/cam/capture_script.cpp
> index fc1dfa75f2d4..a4298f99c4d8 100644
> --- a/src/apps/cam/capture_script.cpp
> +++ b/src/apps/cam/capture_script.cpp
> @@ -7,6 +7,7 @@
>
> #include "capture_script.h"
>
> +#include <algorithm>
> #include <iostream>
> #include <stdio.h>
> #include <stdlib.h>
> @@ -158,6 +159,10 @@ int CaptureScript::parseScript(FILE *script)
> ret = parseProperties();
> if (ret)
> return ret;
> + } else if (section == "configuration") {
> + ret = parseConfiguration();
> + if (ret)
> + return ret;
> } else if (section == "frames") {
> ret = parseFrames();
> if (ret)
> @@ -229,6 +234,156 @@ int CaptureScript::parseProperties()
> return 0;
> }
>
> +int CaptureScript::parseConfiguration()
> +{
> + int ret;
> +
> + EventPtr 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)
> + break;
> +
> + std::string key = eventScalarValue(event);
> +
> + event = nextEvent();
> + if (!event)
> + return -EINVAL;
> + if (event->type == YAML_MAPPING_END_EVENT)
> + break;
> +
> + /* TODO Load sensor configuration */
\todo
> + if (key == "orientation") {
> + ret = parseOrientation(std::move(event));
> + if (ret)
> + return ret;
> + } else if (key == "streams") {
> + ret = parseStreams(std::move(event));
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +int CaptureScript::parseOrientation(EventPtr event)
> +{
> + static const std::map<std::string, libcamera::Orientation> orientations{
> + { "Rotate0", libcamera::Orientation::Rotate0 },
> + { "Rotate0Mirror", libcamera::Orientation::Rotate0Mirror },
> + { "Rotate180", libcamera::Orientation::Rotate180 },
> + { "Rotate180Mirror", libcamera::Orientation::Rotate180Mirror },
> + { "Rotate90Mirror", libcamera::Orientation::Rotate90Mirror },
> + { "Rotate270", libcamera::Orientation::Rotate270 },
> + { "Rotate270Mirror", libcamera::Orientation::Rotate270Mirror },
> + { "Rotate90", libcamera::Orientation::Rotate90 },
> + };
> +
> + std::string orientation = eventScalarValue(event);
> +
> + auto it = orientations.find(orientation);
> + if (it == orientations.end()) {
> + std::cerr << "Invalid orientation '" << orientation
> + << "' in capture script" << std::endl;
> + return -EINVAL;
> + }
> +
> + orientation_ = it->second;
> +
> + return 0;
> +}
> +
> +int CaptureScript::parseStreams(EventPtr event)
> +{
> + int ret;
> +
> + if (!checkEvent(event, YAML_SEQUENCE_START_EVENT))
> + return -EINVAL;
> +
> + unsigned int index = 0;
> +
> + while (1) {
> + event = nextEvent();
> + if (!event)
> + return -EINVAL;
> +
> + if (event->type == YAML_SEQUENCE_END_EVENT)
> + return 0;
> +
> + if (event->type == YAML_MAPPING_START_EVENT) {
> + if ((ret = parseStream(std::move(event), index++)) < 0)
int ret = parseStream(std::move(event), index++));
if (ret < 0)
> + return ret;
> + } else {
> + std::cerr << "Unknown type" << std::endl;
> + return -EINVAL;
> + }
Please also see my last reply on v1.
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> + }
> +
> + return 0;
> +}
> +
> +int CaptureScript::parseStream(EventPtr event, unsigned int index)
> +{
> + if (!checkEvent(event, YAML_MAPPING_START_EVENT))
> + return -EINVAL;
> +
> + StreamConfiguration config;
> + while (1) {
> + event = nextEvent();
> + if (!event)
> + return -EINVAL;
> +
> + if (event->type == YAML_MAPPING_END_EVENT)
> + break;
> +
> + std::string key = eventScalarValue(event);
> +
> + std::string value = parseScalar();
> + if (value.empty())
> + return -EINVAL;
> +
> + if (key == "pixelFormat") {
> + config.pixelFormat = libcamera::PixelFormat::fromString(value);
> + } else if (key == "size") {
> + unsigned int split = value.find("x");
> + if (split == std::string::npos) {
> + std::cerr << "Invalid size '" << value
> + << "' in stream configuration "
> + << index << std::endl;
> + }
> +
> + std::string width = value.substr(0, split);
> + std::string height = value.substr(split + 1);
> + config.size = Size(std::stoi(width), std::stoi(height));
> + } else if (key == "stride") {
> + config.stride = std::stoi(value);
> + } else if (key == "frameSize") {
> + config.frameSize = std::stoi(value);
> + } else if (key == "bufferCount") {
> + config.bufferCount = std::stoi(value);
> + } else if (key == "colorSpace") {
> + config.colorSpace = libcamera::ColorSpace::fromString(value);
> + } else {
> + std::cerr << "Unknown key-value pair '"
> + << key << "': '" << value
> + << "' in stream configuration "
> + << index << std::endl;
> + return -EINVAL;
> + }
> + }
> +
> + streamConfigs_.push_back(config);
> +
> + return 0;
> +}
> +
> int CaptureScript::parseFrames()
> {
> EventPtr event = nextEvent(YAML_SEQUENCE_START_EVENT);
> @@ -322,6 +477,14 @@ int CaptureScript::parseControl(EventPtr event, ControlList &controls)
> return 0;
> }
>
> +void CaptureScript::populateConfiguration(CameraConfiguration &configuration) const
> +{
> + configuration.orientation = orientation_;
> +
> + for (unsigned int i = 0; i < streamConfigs_.size(); i++)
> + configuration[i] = streamConfigs_[i];
> +}
> +
> std::string CaptureScript::parseScalar()
> {
> EventPtr event = nextEvent(YAML_SCALAR_EVENT);
> diff --git a/src/apps/cam/capture_script.h b/src/apps/cam/capture_script.h
> index 294b920363ba..4fa3447d156f 100644
> --- a/src/apps/cam/capture_script.h
> +++ b/src/apps/cam/capture_script.h
> @@ -26,6 +26,8 @@ public:
>
> const libcamera::ControlList &frameControls(unsigned int frame);
>
> + void populateConfiguration(libcamera::CameraConfiguration &configuration) const;
> +
> private:
> struct EventDeleter {
> void operator()(yaml_event_t *event) const
> @@ -43,6 +45,9 @@ private:
> unsigned int loop_;
> bool valid_;
>
> + libcamera::Orientation orientation_;
> + std::vector<libcamera::StreamConfiguration> streamConfigs_;
> +
> 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);
> @@ -52,6 +57,10 @@ private:
>
> int parseProperties();
> int parseProperty();
> + int parseConfiguration();
> + int parseOrientation(EventPtr event);
> + int parseStreams(EventPtr event);
> + int parseStream(EventPtr event, unsigned int index);
> int parseFrames();
> int parseFrame(EventPtr event);
> int parseControl(EventPtr event, libcamera::ControlList &controls);
> diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
> index 460dbc813060..4291b64e250d 100644
> --- a/src/apps/cam/main.cpp
> +++ b/src/apps/cam/main.cpp
> @@ -175,7 +175,9 @@ int CamApp::parseOptions(int argc, char *argv[])
> "metadata", ArgumentNone, nullptr, false,
> OptCamera);
> parser.addOption(OptCaptureScript, OptionString,
> - "Load a capture session configuration script from a file",
> + "Load a capture session configuration script from a file.\n"
> + "Configuration options specified in the capture script will be\n"
> + "overwritten by --stream and --orientation.",
> "script", ArgumentRequired, "script", false,
> OptCamera);
>
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list