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,
