From patchwork Fri Oct 4 12:05:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 21520 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 27409C3257 for ; Fri, 4 Oct 2024 12:05:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DB83163512; Fri, 4 Oct 2024 14:05:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hzxQUl+z"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AE0DB62C92 for ; Fri, 4 Oct 2024 14:05:35 +0200 (CEST) Received: from neptunite.flets-east.jp (unknown [IPv6:2404:7a81:160:2100:e3ca:2180:ae9b:1941]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0AABB59C; Fri, 4 Oct 2024 14:04:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1728043442; bh=93ebda6kJkLq8Nx5UezhNbPfWynaFqu/KPp6y3YAOTA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hzxQUl+zglvFBHvUPA1KQYcLjgfxst1SIdHTBq2O3jDjzEIlsSh2fuKE9otp2Puay BqML9cVR3YHkY2vtp+wkkTtOXry6UJcGx1MVuV7QNHwXmFaXaoYw3Oo1pC4svge+49 BKrwnwkAn59a91ZT/gVv+mIvd655MAj4NKaqgqrU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH v2 2/2] apps: cam: Add support for loading configuration from capture script Date: Fri, 4 Oct 2024 21:05:17 +0900 Message-Id: <20241004120517.3572281-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241004120517.3572281-1-paul.elder@ideasonboard.com> References: <20241004120517.3572281-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Laurent Pinchart --- 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(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 orientations{ @@ -119,16 +135,6 @@ CameraSession::CameraSession(CameraManager *cm, } #endif - if (options_.isSet(OptCaptureScript)) { - std::string scriptName = options_[OptCaptureScript].toString(); - script_ = std::make_unique(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 #include #include #include @@ -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 */ + 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 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) + return ret; + } else { + std::cerr << "Unknown type" << std::endl; + return -EINVAL; + } + } + + 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 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);