diff --git a/src/cam/meson.build b/src/cam/meson.build
index 2419d648bc17e02b..162d6333f94e4851 100644
--- a/src/cam/meson.build
+++ b/src/cam/meson.build
@@ -4,6 +4,7 @@ cam_sources = files([
     'event_loop.cpp',
     'main.cpp',
     'options.cpp',
+    'stream_options.cpp',
 ])
 
 cam  = executable('cam', cam_sources,
diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp
new file mode 100644
index 0000000000000000..22bf61f2c13d10c1
--- /dev/null
+++ b/src/cam/stream_options.cpp
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.
+ *
+ * stream_options.cpp - Helper to parse options for stremas
+ */
+#include "stream_options.h"
+
+#include <iostream>
+
+using namespace libcamera;
+
+int StreamOptionsParser::Options::applyCameraParameters(CameraConfiguration *config) const
+{
+	if (!config) {
+		std::cerr << "No configuration provided" << std::endl;
+		return -EINVAL;
+	}
+
+	if (config->size() != streamParameters_.size()) {
+		std::cerr
+			<< "Number of streams in configuration "
+			<< config->size()
+			<< " does not match number of streams parsed "
+			<< streamParameters_.size()
+			<< std::endl;
+		return -EINVAL;
+	}
+
+	unsigned int i = 0;
+	for (auto const &param : streamParameters_) {
+		StreamConfiguration &cfg = config->at(i++);
+
+		if (param.size.width && param.size.height)
+			cfg.size = param.size;
+
+		if (param.pixelFormat.isValid())
+			cfg.pixelFormat = param.pixelFormat;
+	}
+
+	return 0;
+}
+
+int StreamOptionsParser::addStreamOptions(int opt, const char *name)
+{
+	if (opt_)
+		return -EINVAL;
+
+	opt_ = opt;
+
+	streamKeyValue_.addOption("role", OptionString,
+				  "Role for the stream (viewfinder, video, still, stillraw)",
+				  ArgumentRequired);
+	streamKeyValue_.addOption("width", OptionInteger, "Width in pixels",
+				  ArgumentRequired);
+	streamKeyValue_.addOption("height", OptionInteger, "Height in pixels",
+				  ArgumentRequired);
+	streamKeyValue_.addOption("pixelformat", OptionInteger, "Pixel format",
+				  ArgumentRequired);
+
+	return addOption(opt_, &streamKeyValue_,
+			 "Set configuration of a camera stream", name, true);
+}
+
+StreamOptionsParser::Options StreamOptionsParser::parse(int argc, char *argv[])
+{
+	OptionsParser::Options options = OptionsParser::parse(argc, argv);
+
+	if (!opt_ || !options.isSet(opt_))
+		return Options(options, {StreamRole::Viewfinder});
+
+	StreamRoles roles;
+	std::vector<Options::Parameters> parameters;
+
+	for (auto const &value : options[opt_].toArray()) {
+		KeyValueParser::Options opts = value.toKeyValues();
+		Options::Parameters param;
+
+		if (opts.isSet("width") && opts.isSet("height")) {
+			param.size.width = opts["width"];
+			param.size.height = opts["height"];
+		}
+
+		/* TODO: Translate 4CC string to ID. */
+		if (opts.isSet("pixelformat"))
+			param.pixelFormat = PixelFormat(opts["pixelformat"]);
+
+		roles.push_back(parseRole(opts));
+		parameters.push_back(param);
+	}
+
+	return Options(options, roles, parameters);
+}
+
+StreamRole StreamOptionsParser::parseRole(KeyValueParser::Options opts)
+{
+	if (!opts.isSet("role"))
+		return StreamRole::Viewfinder;
+
+	std::string role = opts["role"].toString();
+
+	if (role == "viewfinder")
+		return StreamRole::Viewfinder;
+	if (role == "video")
+		return StreamRole::VideoRecording;
+	if (role == "still")
+		return StreamRole::StillCapture;
+	if (role == "stillraw")
+		return StreamRole::StillCaptureRaw;
+
+	/* No stream role found, warn and default to viewfinder. */
+	std::cerr << "Unknown stream role " << role
+		  << ", adjusted to viewfinder" << std::endl;
+
+	return StreamRole::Viewfinder;
+}
diff --git a/src/cam/stream_options.h b/src/cam/stream_options.h
new file mode 100644
index 0000000000000000..a0f8a456b44fe275
--- /dev/null
+++ b/src/cam/stream_options.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Raspberry Pi (Trading) Ltd.
+ *
+ * stream_options.h - Helper to parse options for stremas
+ */
+#ifndef __CAM_STREAM_OPTIONS_H__
+#define __CAM_STREAM_OPTIONS_H__
+
+#include <libcamera/camera.h>
+#include <libcamera/stream.h>
+
+#include "options.h"
+
+using namespace libcamera;
+
+class StreamOptionsParser : public OptionsParser
+{
+public:
+	class Options : public OptionsParser::Options
+	{
+	public:
+		struct Parameters {
+			PixelFormat pixelFormat;
+			Size size;
+		};
+
+		Options()
+			: OptionsParser::Options()
+		{
+		}
+
+		Options(OptionsParser::Options options, StreamRoles roles = {},
+			std::vector<Parameters> parameters = {})
+			: OptionsParser::Options(options), roles_(roles),
+			  streamParameters_(parameters)
+		{
+		}
+
+		const StreamRoles &roles() const { return roles_; }
+		int applyCameraParameters(CameraConfiguration *config) const;
+
+	private:
+		StreamRoles roles_;
+		std::vector<Parameters> streamParameters_;
+	};
+
+	StreamOptionsParser()
+		: opt_(0)
+	{
+	}
+
+	int addStreamOptions(int opt, const char *name = nullptr);
+	Options parse(int argc, char *argv[]);
+
+private:
+	StreamRole parseRole(KeyValueParser::Options opts);
+
+	int opt_;
+	KeyValueParser streamKeyValue_;
+};
+
+#endif /* __CAM_STREAM_OPTIONS_H__ */
