[RFC,7/7] apps: cam: Add command line options for stream specific outputs
diff mbox series

Message ID 20250314202943.112109-8-mzamazal@redhat.com
State New
Headers show
Series
  • Support different outputs for cam streams
Related show

Commit Message

Milan Zamazal March 14, 2025, 8:29 p.m. UTC
Now, when the support for multiple outputs is ready, let's add command
line options to actually use it.

To remain backward compatible and to keep the things simple for the
user, the original output related command line options work as before.
For example, simply running

  cam -c1 -C -D

or

  cam ... -s role=viewfinder,... -s role=raw,... -Fimage#

still works.

But the user can also specify a different output for each of the given
streams now, for example:

  cam ... -s role=viewfinder,... --stream-display \
          -s role=raw,... --stream-file=raw#

or

  cam ... -s role=viewfinder,... --stream-file=processed#.ppm \
          -s role=raw,... --stream-file=raw#

To not complicate the implementation more than necessary, it's not
allowed to combine the old and the new output options.

It is also checked that --stream-display is not used more than once (the
KMS output may be used only for a single stream) and that each stream
has at most one output assigned.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/apps/cam/camera_session.cpp | 67 ++++++++++++++++++++++++++++++++-
 src/apps/cam/main.cpp           | 23 ++++++++---
 src/apps/cam/main.h             |  3 ++
 3 files changed, 85 insertions(+), 8 deletions(-)

Patch
diff mbox series

diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
index 5124029b..5ef0c2fe 100644
--- a/src/apps/cam/camera_session.cpp
+++ b/src/apps/cam/camera_session.cpp
@@ -7,13 +7,16 @@ 
 
 #include "camera_session.h"
 
+#include <algorithm>
 #include <iomanip>
 #include <iostream>
 #include <limits.h>
+#include <memory.h>
 #include <sstream>
 
 #include <libcamera/control_ids.h>
 #include <libcamera/property_ids.h>
+#include <libcamera/stream.h>
 
 #include "../common/event_loop.h"
 #include "../common/stream_options.h"
@@ -292,10 +295,70 @@  int CameraSession::start()
 		defaultSink = std::move(sink);
 	}
 
+#ifdef HAVE_KMS
+	bool kmsSinkAssigned = false;
+#endif
+	auto &streamOptions = options_[OptStream];
 	for (unsigned int i = 0; i < config_->size(); i++) {
 		const StreamConfiguration &cfg = config_->at(i);
-		if (defaultSink)
-			defaultSink->addStream(cfg.stream());
+		if (streamOptions.empty()) {
+			if (defaultSink)
+				defaultSink->addStream(cfg.stream());
+		} else {
+			const OptionsParser::Options &suboptions = streamOptions.toArray()[i].children();
+			if (defaultSink) {
+				if (suboptions.isSet(OptStreamDisplay) ||
+				    suboptions.isSet(OptStreamSDL) ||
+				    suboptions.isSet(OptStreamFile)) {
+					std::cerr << "Combining default and stream specific outputs is unsupported" << std::endl;
+					return -EINVAL;
+				}
+
+				defaultSink->addStream(cfg.stream());
+				continue;
+			}
+
+			std::unique_ptr<FrameSink> sink;
+#ifdef HAVE_KMS
+			if (suboptions.isSet(OptStreamDisplay)) {
+				if (kmsSinkAssigned) {
+					std::cerr << "Display doesn't support multiple streams" << std::endl;
+					return -EINVAL;
+				}
+				kmsSinkAssigned = true;
+
+				sink = std::make_unique<KMSSink>(suboptions[OptStreamDisplay].toString());
+			}
+#endif
+#ifdef HAVE_SDL
+			if (suboptions.isSet(OptStreamSDL)) {
+				if (sink) {
+					std::cerr << "Single stream cannot have multiple outputs" << std::endl;
+					return -EINVAL;
+				}
+				sink = std::make_unique<SDLSink>();
+			}
+#endif
+			if (suboptions.isSet(OptStreamFile)) {
+				if (sink) {
+					std::cerr << "Single stream cannot have multiple outputs" << std::endl;
+					return -EINVAL;
+				}
+				std::unique_ptr<FileSink> fileSink =
+					std::make_unique<FileSink>(camera_.get(), streamNames_);
+				if (!suboptions[OptStreamFile].toString().empty()) {
+					ret = fileSink->setFilePattern(suboptions[OptStreamFile]);
+					if (ret)
+						return ret;
+				}
+				sink = std::move(fileSink);
+			}
+
+			if (sink) {
+				sink->addStream(cfg.stream());
+				sinks_.push_back(std::move(sink));
+			}
+		}
 	}
 
 	if (defaultSink)
diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp
index 9d84ef8e..10b7a33a 100644
--- a/src/apps/cam/main.cpp
+++ b/src/apps/cam/main.cpp
@@ -138,14 +138,21 @@  int CamApp::parseOptions(int argc, char *argv[])
 			 "Desired image orientation (rot0, rot180, mirror, flip)",
 			 "orientation", ArgumentRequired, "orientation", false,
 			 OptCamera);
+	parser.addOption(OptStream, &streamKeyValue,
+			 "Set configuration of a camera stream", "stream", true,
+			 OptCamera);
 #ifdef HAVE_KMS
 	parser.addOption(OptDisplay, OptionString,
-			 "Display viewfinder through DRM/KMS on specified connector",
+			 "Display viewfinder by default through DRM/KMS on specified connector",
 			 "display", ArgumentOptional, "connector", false,
 			 OptCamera);
+	parser.addOption(OptStreamDisplay, OptionString,
+			 "Display viewfinder stream through DRM/KMS on specified connector",
+			 "stream-display", ArgumentOptional, "connector", false,
+			 OptStream);
 #endif
 	parser.addOption(OptFile, OptionString,
-			 "Write captured frames to disk\n"
+			 "Write captured frames by default to disk\n"
 			 "If the file name ends with a '/', it sets the directory in which\n"
 			 "to write files, using the default file name. Otherwise it sets the\n"
 			 "full file path and name. The first '#' character in the file name\n"
@@ -159,13 +166,17 @@  int CamApp::parseOptions(int argc, char *argv[])
 			 "The default file name is 'frame-#.bin'.",
 			 "file", ArgumentOptional, "filename", false,
 			 OptCamera);
+	parser.addOption(OptStreamFile, OptionString,
+			 "Write frames captured from a stream to disk\n"
+			 "The file name is of the same format as in --file.",
+			 "stream-file", ArgumentOptional, "filename", false,
+			 OptStream);
 #ifdef HAVE_SDL
-	parser.addOption(OptSDL, OptionNone, "Display viewfinder through SDL",
+	parser.addOption(OptSDL, OptionNone, "Display viewfinder by default through SDL",
 			 "sdl", ArgumentNone, "", false, OptCamera);
+	parser.addOption(OptStreamSDL, OptionNone, "Display stream viewfinder through SDL",
+			 "stream-sdl", ArgumentNone, "", false, OptStream);
 #endif
-	parser.addOption(OptStream, &streamKeyValue,
-			 "Set configuration of a camera stream", "stream", true,
-			 OptCamera);
 	parser.addOption(OptStrictFormats, OptionNone,
 			 "Do not allow requested stream format(s) to be adjusted",
 			 "strict-formats", ArgumentNone, nullptr, false,
diff --git a/src/apps/cam/main.h b/src/apps/cam/main.h
index 64e6a20e..2f630fbe 100644
--- a/src/apps/cam/main.h
+++ b/src/apps/cam/main.h
@@ -20,6 +20,9 @@  enum {
 	OptOrientation = 'o',
 	OptSDL = 'S',
 	OptStream = 's',
+	OptStreamDisplay = 'd',
+	OptStreamFile = 'f',
+	OptStreamSDL = 'w',
 	OptListControls = 256,
 	OptStrictFormats = 257,
 	OptMetadata = 258,