@@ -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)
@@ -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,
@@ -20,6 +20,9 @@ enum {
OptOrientation = 'o',
OptSDL = 'S',
OptStream = 's',
+ OptStreamDisplay = 'd',
+ OptStreamFile = 'f',
+ OptStreamSDL = 'w',
OptListControls = 256,
OptStrictFormats = 257,
OptMetadata = 258,
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(-)