diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 0386671c902e55e8..8455049151d5c5a2 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -24,6 +24,34 @@ class Stream;
 class StreamConfiguration;
 class StreamUsage;
 
+class CameraConfiguration
+{
+public:
+	using iterator = std::vector<Stream *>::iterator;
+	using const_iterator = std::vector<Stream *>::const_iterator;
+
+	CameraConfiguration();
+
+	iterator begin();
+	iterator end();
+	const_iterator begin() const;
+	const_iterator end() const;
+
+	bool valid() const;
+	bool empty() const;
+	std::size_t size() const;
+
+	Stream *front();
+
+	Stream *operator[](unsigned int index) const;
+	StreamConfiguration &operator[](Stream *stream);
+	const StreamConfiguration &operator[](Stream *stream) const;
+
+private:
+	std::vector<Stream *> order_;
+	std::map<Stream *, StreamConfiguration> config_;
+};
+
 class Camera final
 {
 public:
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 63fde0ffc3d02d6c..98145edea1ac9c91 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -39,6 +39,179 @@ namespace libcamera {
 
 LOG_DECLARE_CATEGORY(Camera)
 
+/**
+ * \class CameraConfiguration
+ * \brief Hold configuration for streams of the camera
+ *
+ * The CameraConfiguration is filled with information by the application either
+ * manually or with the streamConfiguration() helper. The helper takes a list
+ * of usages describing how the application intends to use streams of the
+ * camera, the application in return are provided with a default camera
+ * configuration it can tune.
+ *
+ * Applications iterate over the CameraConfiguration to discover which streams
+ * the camera has associated to the usages, and can inspect the configuration
+ * of individual streams using the operator[].
+ */
+
+/**
+ * \typedef CameraConfiguration::iterator
+ * \brief Iterator for the streams in the configuration
+ */
+
+/**
+ * \typedef CameraConfiguration::const_iterator
+ * \brief Const iterator for the streams in the configuration
+ */
+
+/**
+ * \brief Create an empty camera configuration
+ */
+CameraConfiguration::CameraConfiguration()
+	: order_({}), config_({})
+{
+}
+
+/**
+ * \brief Retrieve an iterator to the first stream in the sequence
+ *
+ * \return An iterator to the first stream
+ */
+std::vector<Stream *>::iterator CameraConfiguration::begin()
+{
+	return order_.begin();
+}
+
+/**
+ * \brief Retrieve an iterator pointing to the past-the-end stream in the
+ * sequence
+ *
+ * \return An iterator to the element following the last stream
+ */
+std::vector<Stream *>::iterator CameraConfiguration::end()
+{
+	return order_.end();
+}
+
+/**
+ * \brief Retrieve an iterator to the first element of the streams
+ *
+ * \return An iterator to the first stream
+ */
+std::vector<Stream *>::const_iterator CameraConfiguration::begin() const
+{
+	return order_.begin();
+}
+
+/**
+ * \brief Retrieve an iterator to the end of the streams
+ *
+ * \return An iterator to the element following the last stream
+ */
+std::vector<Stream *>::const_iterator CameraConfiguration::end() const
+{
+	return order_.end();
+}
+
+/**
+ * \brief Check if the camera configuration is valid
+ *
+ * \return True if the configuration is valid
+ */
+bool CameraConfiguration::valid() const
+{
+	if (empty())
+		return false;
+
+	for (auto const &it : config_) {
+		const StreamConfiguration &conf = it.second;
+
+		if (conf.width == 0 || conf.height == 0 ||
+		    conf.pixelFormat == 0 || conf.bufferCount == 0)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * \brief Check if the camera configuration is empty
+ *
+ * \return True if the configuration is empty
+ */
+bool CameraConfiguration::empty() const
+{
+	return order_.empty();
+}
+
+/**
+ * \brief Retrieve the number of stream configurations
+ *
+ * \return Number of stream configurations
+ */
+std::size_t CameraConfiguration::size() const
+{
+	return order_.size();
+}
+
+/**
+ * \brief Access the first stream in the configuration
+ *
+ * \return The first stream in the configuration
+ */
+Stream *CameraConfiguration::front()
+{
+	return order_.front();
+}
+/**
+ * \brief Retrieve a stream pointer from index
+ * \param[in] index Numerical index
+ *
+ * The \a index represents the zero bases insertion order of stream and stream
+ * configuration into the camera configuration.
+ *
+ * \return The stream pointer at index, or a nullptr if the index is out of
+ * bounds
+ */
+Stream *CameraConfiguration::operator[](unsigned int index) const
+{
+	if (index >= order_.size())
+		return nullptr;
+
+	return order_.at(index);
+}
+
+/**
+ * \brief Retrieve a reference to a stream configuration
+ * \param[in] stream Stream to retrieve configuration for
+ *
+ * If the camera configuration does not yet contain a configuration for
+ * the requested stream, create and return an empty stream configuration.
+ *
+ * \return The configuration for the stream
+ */
+StreamConfiguration &CameraConfiguration::operator[](Stream *stream)
+{
+	if (config_.find(stream) == config_.end())
+		order_.push_back(stream);
+
+	return config_[stream];
+}
+
+/**
+ * \brief Retrieve a const reference to a stream configuration
+ * \param[in] stream Stream to retrieve configuration for
+ *
+ * No new stream configuration is created if called with \a stream that is not
+ * already part of the camera configuration, doing so is an illegal operation.
+ *
+ * \return The configuration for the stream
+ */
+const StreamConfiguration &CameraConfiguration::operator[](Stream *stream) const
+{
+	return config_.at(stream);
+}
+
 /**
  * \class Camera
  * \brief Camera device
