[v2,2/2] apps: cam: Add support for loading configuration from capture script
diff mbox series

Message ID 20241004120517.3572281-3-paul.elder@ideasonboard.com
State New
Headers show
Series
  • libcamera: Add support for dumping capture script
Related show

Commit Message

Paul Elder Oct. 4, 2024, 12:05 p.m. UTC
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@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(-)

Comments

Laurent Pinchart Oct. 7, 2024, 9:39 p.m. UTC | #1
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@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@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);
>

Patch
diff mbox series

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 */
+		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)
+				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<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);