[libcamera-devel,11/17] libcamera: stream: Add StreamFormats

Message ID 20190527001543.13593-12-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: Add support for format information and validation
Related show

Commit Message

Niklas Söderlund May 27, 2019, 12:15 a.m. UTC
Add a StreamFormats which describes all the formats a stream can
support. The object does not collect any formation itself but can
simplify users interaction with formats as it's able to translate a
stream format range into discrete list and a discrete list to a range.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 include/libcamera/stream.h |  16 +++
 src/libcamera/stream.cpp   | 216 +++++++++++++++++++++++++++++++++++++
 2 files changed, 232 insertions(+)

Comments

Kieran Bingham May 29, 2019, 10:22 p.m. UTC | #1
Hi Niklas,

On 27/05/2019 01:15, Niklas Söderlund wrote:
> Add a StreamFormats which describes all the formats a stream can
> support. The object does not collect any formation itself but can
> simplify users interaction with formats as it's able to translate a
> stream format range into discrete list and a discrete list to a range.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  include/libcamera/stream.h |  16 +++
>  src/libcamera/stream.cpp   | 216 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 232 insertions(+)
> 
> diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
> index e38c0e7e827d5888..48daf5ac23f55d85 100644
> --- a/include/libcamera/stream.h
> +++ b/include/libcamera/stream.h
> @@ -7,6 +7,7 @@
>  #ifndef __LIBCAMERA_STREAM_H__
>  #define __LIBCAMERA_STREAM_H__
>  
> +#include <map>
>  #include <string>
>  #include <vector>
>  
> @@ -18,6 +19,21 @@ namespace libcamera {
>  class Camera;
>  class Stream;
>  
> +class StreamFormats
> +{
> +public:
> +	StreamFormats();
> +	StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats);
> +
> +	std::vector<unsigned int> pixelformats() const;
> +	std::vector<Size> sizes(unsigned int pixelformat) const;
> +
> +	SizeRange range(unsigned int pixelformat) const;
> +
> +private:
> +	std::map<unsigned int, std::vector<SizeRange>> formats_;
> +};
> +
>  struct StreamConfiguration {
>  	StreamConfiguration()
>  		: stream_(nullptr)
> diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
> index eecd37160150d55c..a2931902fda2baa5 100644
> --- a/src/libcamera/stream.cpp
> +++ b/src/libcamera/stream.cpp
> @@ -7,6 +7,7 @@
>  
>  #include <libcamera/stream.h>
>  
> +#include <algorithm>
>  #include <iomanip>
>  #include <sstream>
>  
> @@ -36,6 +37,221 @@ namespace libcamera {
>  
>  LOG_DEFINE_CATEGORY(Stream)
>  
> +/**
> + * \class StreamFormats
> + * \brief Hold information about supported stream formats
> + *
> + * The StreamFormats class holds information about pixel formats and frame

/about pixel formats/about the pixel formats/

> + * sizes a stream supports. The class groups size information by the pixel
> + * format which can produce it. There are two ways to examine the size
> + * information, as a range or as a list of discrete sizes.
> + *
> + * When sizes are viewed as a range it describes the minimum and maximum width
> + * and height values. There is a possibility to supplement the range description
> + * with a horizontal och vertical stepping size. The stepping size describes the

/och/and/ ?


> + * step size in pixel from the minimum with/height.

/in pixel/in pixels/

> + *
> + * When sizes is viewed as a list of discrete sizes it describes exact dimensions

When sizes are viewed...  ... they describe exact ...

> + * which can be selected and used.
> + *
> + * Pipeline handlers can create StreamFormats describing each pixel format using
> + * either a range or a list of  discrete sizes. The StreamFormats class attempts

/of  discrete/of discrete/ <remove extra space>

> + * to translates between the two different ways to view them. The translations

/translates/translate/

> + * are performed as:
> + *
> + *  - If a pixel format is described as a list of discrete sizes a range is

... of discrete sizes then a range ....

> + *    created by taking the minim and maximum width/height in the list.

/minim/minimum/

> + *    The stepping information is not recreated and set to 1.

... and is set to 1.


> + *
> + *  - If a pixel format is described as a range a list of discrete sizes which

... as a range then a list of ...

> + *    fits inside that range are selected from a list of common sizes. The
> + *    stepping information is taken into consideration when generating the
> + *    sizes.
> + *
> + * Applications examining sizes as a range with stepping values of 1 should be
> + * aware that the range could be generated form a list of discrete sizes and

/form/from/


> + * there could be big gaps in the range to what the stream can support.

... there could be a large quantity of possible Size combinations which
may not be supported by the Stream.


Could we set the stepping size to '-1' or '0' or something here to
identify that the range is really only specifying the maximum, and
minimum width/height ?

> + *
> + * All sizes retrieved from StreamFormats should be treated as advisory and no
> + * size should be considered to be supported until its been verified using
> + * CameraConfiguration::validate().
> + */
> +
> +/**
> + * \brief Construct a empty StreamFormats object
> + */
> +StreamFormats::StreamFormats()
> +{
> +}
> +
> +/**
> + * \brief Construct a StreamFormats object
> + * \param[in] formats A map of pixel formats to a sizes description
> + */
> +StreamFormats::StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats)
> +	: formats_(formats)
> +{
> +}
> +
> +/**
> + * \brief Retrieve the list of pixel formats supported
> + * \returns List of pixel formats
> + */
> +std::vector<unsigned int> StreamFormats::pixelformats() const
> +{
> +	std::vector<unsigned int> formats;
> +
> +	for (auto const &it : formats_)
> +		formats.push_back(it.first);
> +
> +	return formats;
> +}
> +
> +/**
> + * \brief Retrieve the list of frame sizes
> + * \param[in] pixelformat Pixel format to retrieve sizes for
> + * \returns List description of frame sizes
> + */
> +std::vector<Size> StreamFormats::sizes(unsigned int pixelformat) const
> +{
> +	/*
> +	 * Sizes to try and extract from ranges.
> +	 * \todo Verify list of resolutions are good, current list compiled
> +	 * from v4l2 documentation and source code as well as lists of
> +	 * common frame sizes.
> +	 */
> +	static const std::vector<Size> rangeDiscreteSizes = {
> +		Size(160, 120),
> +		Size(240, 160),
> +		Size(320, 240),
> +		Size(400, 240),
> +		Size(480, 320),
> +		Size(640, 360),
> +		Size(640, 480),
> +		Size(720, 480),
> +		Size(720, 576),
> +		Size(768, 480),
> +		Size(800, 600),
> +		Size(854, 480),
> +		Size(960, 540),
> +		Size(960, 640),
> +		Size(1024, 576),
> +		Size(1024, 600),
> +		Size(1024, 768),
> +		Size(1152, 864),
> +		Size(1280, 1024),
> +		Size(1280, 1080),
> +		Size(1280, 720),
> +		Size(1280, 800),
> +		Size(1360, 768),
> +		Size(1366, 768),
> +		Size(1400, 1050),
> +		Size(1440, 900),
> +		Size(1536, 864),
> +		Size(1600, 1200),
> +		Size(1600, 900),
> +		Size(1680, 1050),
> +		Size(1920, 1080),
> +		Size(1920, 1200),
> +		Size(2048, 1080),
> +		Size(2048, 1152),
> +		Size(2048, 1536),
> +		Size(2160, 1080),
> +		Size(2560, 1080),
> +		Size(2560, 1440),
> +		Size(2560, 1600),
> +		Size(2560, 2048),
> +		Size(2960, 1440),
> +		Size(3200, 1800),
> +		Size(3200, 2048),
> +		Size(3200, 2400),
> +		Size(3440, 1440),
> +		Size(3840, 1080),
> +		Size(3840, 1600),
> +		Size(3840, 2160),
> +		Size(3840, 2400),
> +		Size(4096, 2160),
> +		Size(5120, 2160),
> +		Size(5120, 2880),
> +		Size(7680, 4320),

I still wonder if we could generate this data table in a better form.

For a start, perhaps it might be worth grouping common Sizes to a single
line to form a table based on their ratios? That would show what ratios
are included maybe?

(perhaps we could generate each ratio from a single width or height
instead?)

/* 4:3                     16:9				16:10 */

Size(1600, 1200),	Size(1600, 900),	Size(1680, 1050),
			Size(1920, 1080),	Size(1920, 1200),

/* 2:1			 2:1 ish again?			4:3 */
Size(2048, 1080),	Size(2048, 1152),	Size(2048, 1536),


If you did look at doing this, I'd move the table outside of the
function so that it's indent is reduced.

> +	};
> +	std::vector<Size> sizes;
> +
> +	/* Make sure pixel format exists. */
> +	auto const &it = formats_.find(pixelformat);
> +	if (it == formats_.end())
> +		return {};
> +
> +	/* Try create list of discrete sizes. */
> +	const std::vector<SizeRange> &ranges = it->second;
> +	bool discrete = true;
> +	for (const SizeRange &range : ranges) {
> +		if (range.min != range.max) {
> +			discrete = false;
> +			break;
> +		}
> +		sizes.emplace_back(range.min.width, range.min.height);
> +	}
> +
> +	/* If discrete not possible generate from range. */
> +	if (!discrete) {
> +		if (ranges.size() != 1) {
> +			LOG(Stream, Error) << "Range format is ambiguous";
> +			return {};
> +		}
> +
> +		const SizeRange &limit = ranges.front();
> +		sizes.clear();
> +
> +		for (const Size &size : rangeDiscreteSizes) {
> +			if (size.width < limit.min.width ||
> +			    size.width > limit.max.width ||
> +			    size.height < limit.min.height ||
> +			    size.height > limit.max.height ||
> +			    (size.width - limit.min.width) % limit.vStep ||
> +			    (size.height - limit.min.height) % limit.hStep)
> +				continue;
> +
> +			sizes.push_back(size);
> +		}
> +	}
> +
> +	std::sort(sizes.begin(), sizes.end());
> +
> +	return sizes;
> +}
> +
> +/**
> + * \brief Retrieve a frame size range
> + * \param[in] pixelformat Pixel format to retrieve range for
> + * \returns Range description of frame size
> + */
> +SizeRange StreamFormats::range(unsigned int pixelformat) const
> +{
> +	auto const it = formats_.find(pixelformat);
> +	if (it == formats_.end())
> +		return {};
> +
> +	const SizeRange &first = it->second.front();
> +	if (it->second.size() == 1)
> +		return first;

There's a lot of mixing of 'first' and 'second' here which is a bit
awkward for comprehension....

> +
> +	LOG(Stream, Debug) << "Building range from discret";

/discret/discrete/

> +
> +	SizeRange range(first.min.width, first.min.height,
> +			first.max.width, first.max.height);
> +
> +	for (const SizeRange &limit : it->second) {
> +		if (limit.min < range.min)
> +			range.min = limit.min;
> +
> +		if (limit.max > range.max)
> +			range.max = limit.max;
> +	}
> +
> +	return range;
> +}
> +
>  /**
>   * \struct StreamConfiguration
>   * \brief Configuration parameters for a stream
>
Laurent Pinchart June 10, 2019, 6:50 a.m. UTC | #2
Hi Niklas,

On Wed, May 29, 2019 at 11:22:51PM +0100, Kieran Bingham wrote:
> On 27/05/2019 01:15, Niklas Söderlund wrote:
> > Add a StreamFormats which describes all the formats a stream can

s/StreamFormats/StreamFormats class/

> > support. The object does not collect any formation itself but can
> > simplify users interaction with formats as it's able to translate a

s/users interaction/user interactions/

> > stream format range into discrete list and a discrete list to a range.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  include/libcamera/stream.h |  16 +++
> >  src/libcamera/stream.cpp   | 216 +++++++++++++++++++++++++++++++++++++
> >  2 files changed, 232 insertions(+)
> > 
> > diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
> > index e38c0e7e827d5888..48daf5ac23f55d85 100644
> > --- a/include/libcamera/stream.h
> > +++ b/include/libcamera/stream.h
> > @@ -7,6 +7,7 @@
> >  #ifndef __LIBCAMERA_STREAM_H__
> >  #define __LIBCAMERA_STREAM_H__
> >  
> > +#include <map>
> >  #include <string>
> >  #include <vector>
> >  
> > @@ -18,6 +19,21 @@ namespace libcamera {
> >  class Camera;
> >  class Stream;
> >  
> > +class StreamFormats
> > +{
> > +public:
> > +	StreamFormats();
> > +	StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats);
> > +
> > +	std::vector<unsigned int> pixelformats() const;

For efficiency reasons, do you think it would make sense to cache the
pixel formats vector the first time it is computed and return a const
reference to the cached copy ? Or do you expect applications to
typically retrieve the pixel formats once only ?

> > +	std::vector<Size> sizes(unsigned int pixelformat) const;

Same question for the sizes and ranges.

> > +
> > +	SizeRange range(unsigned int pixelformat) const;
> > +
> > +private:
> > +	std::map<unsigned int, std::vector<SizeRange>> formats_;
> > +};
> > +
> >  struct StreamConfiguration {
> >  	StreamConfiguration()
> >  		: stream_(nullptr)
> > diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
> > index eecd37160150d55c..a2931902fda2baa5 100644
> > --- a/src/libcamera/stream.cpp
> > +++ b/src/libcamera/stream.cpp
> > @@ -7,6 +7,7 @@
> >  
> >  #include <libcamera/stream.h>
> >  
> > +#include <algorithm>
> >  #include <iomanip>
> >  #include <sstream>
> >  
> > @@ -36,6 +37,221 @@ namespace libcamera {
> >  
> >  LOG_DEFINE_CATEGORY(Stream)
> >  
> > +/**
> > + * \class StreamFormats
> > + * \brief Hold information about supported stream formats
> > + *
> > + * The StreamFormats class holds information about pixel formats and frame
> 
> /about pixel formats/about the pixel formats/
> 
> > + * sizes a stream supports. The class groups size information by the pixel
> > + * format which can produce it. There are two ways to examine the size
> > + * information, as a range or as a list of discrete sizes.
> > + *

I would start a new paragraph before the lest sentence, and remove this
blank line, as the last sentence and the text beflow are related.

> > + * When sizes are viewed as a range it describes the minimum and maximum width
> > + * and height values. There is a possibility to supplement the range description
> > + * with a horizontal och vertical stepping size. The stepping size describes the
> 
> /och/and/ ?
> 
> 
> > + * step size in pixel from the minimum with/height.
> 
> /in pixel/in pixels/
> 
> > + *
> > + * When sizes is viewed as a list of discrete sizes it describes exact dimensions
> 
> When sizes are viewed...  ... they describe exact ...
> 
> > + * which can be selected and used.
> > + *
> > + * Pipeline handlers can create StreamFormats describing each pixel format using
> > + * either a range or a list of  discrete sizes. The StreamFormats class attempts
> 
> /of  discrete/of discrete/ <remove extra space>
> 
> > + * to translates between the two different ways to view them. The translations
> 
> /translates/translate/
> 
> > + * are performed as:
> > + *
> > + *  - If a pixel format is described as a list of discrete sizes a range is
> 
> ... of discrete sizes then a range ....
> 
> > + *    created by taking the minim and maximum width/height in the list.
> 
> /minim/minimum/
> 
> > + *    The stepping information is not recreated and set to 1.
> 
> ... and is set to 1.
> 
> 
> > + *
> > + *  - If a pixel format is described as a range a list of discrete sizes which
> 
> ... as a range then a list of ...
> 
> > + *    fits inside that range are selected from a list of common sizes. The
> > + *    stepping information is taken into consideration when generating the
> > + *    sizes.
> > + *
> > + * Applications examining sizes as a range with stepping values of 1 should be
> > + * aware that the range could be generated form a list of discrete sizes and
> 
> /form/from/
> 
> > + * there could be big gaps in the range to what the stream can support.
> 
> ... there could be a large quantity of possible Size combinations which
> may not be supported by the Stream.
> 
> 
> Could we set the stepping size to '-1' or '0' or something here to
> identify that the range is really only specifying the maximum, and
> minimum width/height ?

I think it would be indeed be useful for an application to determine if
the range is continuous, or just exposes the minimum and maximum with
the stream only supported discrete resolutions. Setting the step to 0
would be one way of doing so. It should then be clearly documented. We
should also evaluate the impact on applications, and find a better API
if it gets too awkward or error-prone to use.

> > + *
> > + * All sizes retrieved from StreamFormats should be treated as advisory and no
> > + * size should be considered to be supported until its been verified using
> > + * CameraConfiguration::validate().

s/should/shall/g

We should also take care of not returning sizes that are not supported.
I think the current implementation is good enough for now, if a pipeline
handler has requirements more complex than a continuous range with a
step, it should likely provide a list of discrete sizes explicitly.
However, we may want to revisit this later, possibly to add a helper for
pipeline handlers that can handle more complex constraints. When we will
do so it may make sense to move the logic that constructs the sizes
based on the standard discrete sizes to a separate function, outside of
the StreamFormats class.

> > + */
> > +
> > +/**
> > + * \brief Construct a empty StreamFormats object
> > + */
> > +StreamFormats::StreamFormats()
> > +{
> > +}
> > +
> > +/**
> > + * \brief Construct a StreamFormats object
> > + * \param[in] formats A map of pixel formats to a sizes description
> > + */
> > +StreamFormats::StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats)
> > +	: formats_(formats)
> > +{
> > +}
> > +
> > +/**
> > + * \brief Retrieve the list of pixel formats supported

s/pixel formats supported/supported pixel formats/

> > + * \returns List of pixel formats

The list of supported pixel formats

> > + */
> > +std::vector<unsigned int> StreamFormats::pixelformats() const
> > +{
> > +	std::vector<unsigned int> formats;
> > +
> > +	for (auto const &it : formats_)
> > +		formats.push_back(it.first);
> > +
> > +	return formats;
> > +}
> > +
> > +/**
> > + * \brief Retrieve the list of frame sizes
> > + * \param[in] pixelformat Pixel format to retrieve sizes for
> > + * \returns List description of frame sizes
> > + */

It would be useful to extend the documentation to explain that sizes are
computed (and how) if the device supports a continuous range.

> > +std::vector<Size> StreamFormats::sizes(unsigned int pixelformat) const
> > +{
> > +	/*
> > +	 * Sizes to try and extract from ranges.
> > +	 * \todo Verify list of resolutions are good, current list compiled
> > +	 * from v4l2 documentation and source code as well as lists of
> > +	 * common frame sizes.
> > +	 */
> > +	static const std::vector<Size> rangeDiscreteSizes = {
> > +		Size(160, 120),
> > +		Size(240, 160),
> > +		Size(320, 240),
> > +		Size(400, 240),
> > +		Size(480, 320),
> > +		Size(640, 360),
> > +		Size(640, 480),
> > +		Size(720, 480),
> > +		Size(720, 576),
> > +		Size(768, 480),
> > +		Size(800, 600),
> > +		Size(854, 480),
> > +		Size(960, 540),
> > +		Size(960, 640),
> > +		Size(1024, 576),
> > +		Size(1024, 600),
> > +		Size(1024, 768),
> > +		Size(1152, 864),
> > +		Size(1280, 1024),
> > +		Size(1280, 1080),
> > +		Size(1280, 720),
> > +		Size(1280, 800),
> > +		Size(1360, 768),
> > +		Size(1366, 768),
> > +		Size(1400, 1050),
> > +		Size(1440, 900),
> > +		Size(1536, 864),
> > +		Size(1600, 1200),
> > +		Size(1600, 900),
> > +		Size(1680, 1050),
> > +		Size(1920, 1080),
> > +		Size(1920, 1200),
> > +		Size(2048, 1080),
> > +		Size(2048, 1152),
> > +		Size(2048, 1536),
> > +		Size(2160, 1080),
> > +		Size(2560, 1080),
> > +		Size(2560, 1440),
> > +		Size(2560, 1600),
> > +		Size(2560, 2048),
> > +		Size(2960, 1440),
> > +		Size(3200, 1800),
> > +		Size(3200, 2048),
> > +		Size(3200, 2400),
> > +		Size(3440, 1440),
> > +		Size(3840, 1080),
> > +		Size(3840, 1600),
> > +		Size(3840, 2160),
> > +		Size(3840, 2400),
> > +		Size(4096, 2160),
> > +		Size(5120, 2160),
> > +		Size(5120, 2880),
> > +		Size(7680, 4320),
> 
> I still wonder if we could generate this data table in a better form.
> 
> For a start, perhaps it might be worth grouping common Sizes to a single
> line to form a table based on their ratios? That would show what ratios
> are included maybe?
> 
> (perhaps we could generate each ratio from a single width or height
> instead?)
> 
> /* 4:3                     16:9				16:10 */
> 
> Size(1600, 1200),	Size(1600, 900),	Size(1680, 1050),
> 			Size(1920, 1080),	Size(1920, 1200),
> 
> /* 2:1			 2:1 ish again?			4:3 */
> Size(2048, 1080),	Size(2048, 1152),	Size(2048, 1536),
> 
> 
> If you did look at doing this, I'd move the table outside of the
> function so that it's indent is reduced.
> 
> > +	};
> > +	std::vector<Size> sizes;
> > +
> > +	/* Make sure pixel format exists. */
> > +	auto const &it = formats_.find(pixelformat);
> > +	if (it == formats_.end())
> > +		return {};
> > +
> > +	/* Try create list of discrete sizes. */

s/create/creating a/

> > +	const std::vector<SizeRange> &ranges = it->second;
> > +	bool discrete = true;
> > +	for (const SizeRange &range : ranges) {
> > +		if (range.min != range.max) {
> > +			discrete = false;
> > +			break;
> > +		}
> > +		sizes.emplace_back(range.min.width, range.min.height);

		sizes.emplace_back(range.min);

> > +	}
> > +
> > +	/* If discrete not possible generate from range. */
> > +	if (!discrete) {
> > +		if (ranges.size() != 1) {
> > +			LOG(Stream, Error) << "Range format is ambiguous";
> > +			return {};
> > +		}
> > +
> > +		const SizeRange &limit = ranges.front();
> > +		sizes.clear();
> > +
> > +		for (const Size &size : rangeDiscreteSizes) {
> > +			if (size.width < limit.min.width ||
> > +			    size.width > limit.max.width ||
> > +			    size.height < limit.min.height ||
> > +			    size.height > limit.max.height ||

Would it make sense to add a bool SizeRange::contains(const Size &size)
method ?

> > +			    (size.width - limit.min.width) % limit.vStep ||
> > +			    (size.height - limit.min.height) % limit.hStep)
> > +				continue;
> > +
> > +			sizes.push_back(size);
> > +		}
> > +	}
> > +
> > +	std::sort(sizes.begin(), sizes.end());
> > +
> > +	return sizes;
> > +}
> > +
> > +/**
> > + * \brief Retrieve a frame size range
> > + * \param[in] pixelformat Pixel format to retrieve range for
> > + * \returns Range description of frame size

It would be useful to extend the documentation to explain what the range
represents and how it can be used (in particular that not all sizes
within the range are necessarily supported by the stream).

> > + */
> > +SizeRange StreamFormats::range(unsigned int pixelformat) const
> > +{
> > +	auto const it = formats_.find(pixelformat);
> > +	if (it == formats_.end())
> > +		return {};
> > +
> > +	const SizeRange &first = it->second.front();
> > +	if (it->second.size() == 1)
> > +		return first;
> 
> There's a lot of mixing of 'first' and 'second' here which is a bit
> awkward for comprehension....

You could write it as

	const std::vector<SizeRange> &ranges = it->second;
	if (ranges.size() == 1)
		return ranges[0];

> > +
> > +	LOG(Stream, Debug) << "Building range from discret";
> 
> /discret/discrete/
> 
> > +
> > +	SizeRange range(first.min.width, first.min.height,
> > +			first.max.width, first.max.height);

Feel free to add a SizeRange(const Size &min, const Size &max)
constructor if you think it would be useful.

You can also initialise range to (UINT_MAX, UINT_MAX, 0, 0).

> > +
> > +	for (const SizeRange &limit : it->second) {
> > +		if (limit.min < range.min)
> > +			range.min = limit.min;
> > +
> > +		if (limit.max > range.max)
> > +			range.max = limit.max;
> > +	}
> > +
> > +	return range;
> > +}
> > +
> >  /**
> >   * \struct StreamConfiguration
> >   * \brief Configuration parameters for a stream

Patch

diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index e38c0e7e827d5888..48daf5ac23f55d85 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -7,6 +7,7 @@ 
 #ifndef __LIBCAMERA_STREAM_H__
 #define __LIBCAMERA_STREAM_H__
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -18,6 +19,21 @@  namespace libcamera {
 class Camera;
 class Stream;
 
+class StreamFormats
+{
+public:
+	StreamFormats();
+	StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats);
+
+	std::vector<unsigned int> pixelformats() const;
+	std::vector<Size> sizes(unsigned int pixelformat) const;
+
+	SizeRange range(unsigned int pixelformat) const;
+
+private:
+	std::map<unsigned int, std::vector<SizeRange>> formats_;
+};
+
 struct StreamConfiguration {
 	StreamConfiguration()
 		: stream_(nullptr)
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index eecd37160150d55c..a2931902fda2baa5 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -7,6 +7,7 @@ 
 
 #include <libcamera/stream.h>
 
+#include <algorithm>
 #include <iomanip>
 #include <sstream>
 
@@ -36,6 +37,221 @@  namespace libcamera {
 
 LOG_DEFINE_CATEGORY(Stream)
 
+/**
+ * \class StreamFormats
+ * \brief Hold information about supported stream formats
+ *
+ * The StreamFormats class holds information about pixel formats and frame
+ * sizes a stream supports. The class groups size information by the pixel
+ * format which can produce it. There are two ways to examine the size
+ * information, as a range or as a list of discrete sizes.
+ *
+ * When sizes are viewed as a range it describes the minimum and maximum width
+ * and height values. There is a possibility to supplement the range description
+ * with a horizontal och vertical stepping size. The stepping size describes the
+ * step size in pixel from the minimum with/height.
+ *
+ * When sizes is viewed as a list of discrete sizes it describes exact dimensions
+ * which can be selected and used.
+ *
+ * Pipeline handlers can create StreamFormats describing each pixel format using
+ * either a range or a list of  discrete sizes. The StreamFormats class attempts
+ * to translates between the two different ways to view them. The translations
+ * are performed as:
+ *
+ *  - If a pixel format is described as a list of discrete sizes a range is
+ *    created by taking the minim and maximum width/height in the list.
+ *    The stepping information is not recreated and set to 1.
+ *
+ *  - If a pixel format is described as a range a list of discrete sizes which
+ *    fits inside that range are selected from a list of common sizes. The
+ *    stepping information is taken into consideration when generating the
+ *    sizes.
+ *
+ * Applications examining sizes as a range with stepping values of 1 should be
+ * aware that the range could be generated form a list of discrete sizes and
+ * there could be big gaps in the range to what the stream can support.
+ *
+ * All sizes retrieved from StreamFormats should be treated as advisory and no
+ * size should be considered to be supported until its been verified using
+ * CameraConfiguration::validate().
+ */
+
+/**
+ * \brief Construct a empty StreamFormats object
+ */
+StreamFormats::StreamFormats()
+{
+}
+
+/**
+ * \brief Construct a StreamFormats object
+ * \param[in] formats A map of pixel formats to a sizes description
+ */
+StreamFormats::StreamFormats(const std::map<unsigned int, std::vector<SizeRange>> &formats)
+	: formats_(formats)
+{
+}
+
+/**
+ * \brief Retrieve the list of pixel formats supported
+ * \returns List of pixel formats
+ */
+std::vector<unsigned int> StreamFormats::pixelformats() const
+{
+	std::vector<unsigned int> formats;
+
+	for (auto const &it : formats_)
+		formats.push_back(it.first);
+
+	return formats;
+}
+
+/**
+ * \brief Retrieve the list of frame sizes
+ * \param[in] pixelformat Pixel format to retrieve sizes for
+ * \returns List description of frame sizes
+ */
+std::vector<Size> StreamFormats::sizes(unsigned int pixelformat) const
+{
+	/*
+	 * Sizes to try and extract from ranges.
+	 * \todo Verify list of resolutions are good, current list compiled
+	 * from v4l2 documentation and source code as well as lists of
+	 * common frame sizes.
+	 */
+	static const std::vector<Size> rangeDiscreteSizes = {
+		Size(160, 120),
+		Size(240, 160),
+		Size(320, 240),
+		Size(400, 240),
+		Size(480, 320),
+		Size(640, 360),
+		Size(640, 480),
+		Size(720, 480),
+		Size(720, 576),
+		Size(768, 480),
+		Size(800, 600),
+		Size(854, 480),
+		Size(960, 540),
+		Size(960, 640),
+		Size(1024, 576),
+		Size(1024, 600),
+		Size(1024, 768),
+		Size(1152, 864),
+		Size(1280, 1024),
+		Size(1280, 1080),
+		Size(1280, 720),
+		Size(1280, 800),
+		Size(1360, 768),
+		Size(1366, 768),
+		Size(1400, 1050),
+		Size(1440, 900),
+		Size(1536, 864),
+		Size(1600, 1200),
+		Size(1600, 900),
+		Size(1680, 1050),
+		Size(1920, 1080),
+		Size(1920, 1200),
+		Size(2048, 1080),
+		Size(2048, 1152),
+		Size(2048, 1536),
+		Size(2160, 1080),
+		Size(2560, 1080),
+		Size(2560, 1440),
+		Size(2560, 1600),
+		Size(2560, 2048),
+		Size(2960, 1440),
+		Size(3200, 1800),
+		Size(3200, 2048),
+		Size(3200, 2400),
+		Size(3440, 1440),
+		Size(3840, 1080),
+		Size(3840, 1600),
+		Size(3840, 2160),
+		Size(3840, 2400),
+		Size(4096, 2160),
+		Size(5120, 2160),
+		Size(5120, 2880),
+		Size(7680, 4320),
+	};
+	std::vector<Size> sizes;
+
+	/* Make sure pixel format exists. */
+	auto const &it = formats_.find(pixelformat);
+	if (it == formats_.end())
+		return {};
+
+	/* Try create list of discrete sizes. */
+	const std::vector<SizeRange> &ranges = it->second;
+	bool discrete = true;
+	for (const SizeRange &range : ranges) {
+		if (range.min != range.max) {
+			discrete = false;
+			break;
+		}
+		sizes.emplace_back(range.min.width, range.min.height);
+	}
+
+	/* If discrete not possible generate from range. */
+	if (!discrete) {
+		if (ranges.size() != 1) {
+			LOG(Stream, Error) << "Range format is ambiguous";
+			return {};
+		}
+
+		const SizeRange &limit = ranges.front();
+		sizes.clear();
+
+		for (const Size &size : rangeDiscreteSizes) {
+			if (size.width < limit.min.width ||
+			    size.width > limit.max.width ||
+			    size.height < limit.min.height ||
+			    size.height > limit.max.height ||
+			    (size.width - limit.min.width) % limit.vStep ||
+			    (size.height - limit.min.height) % limit.hStep)
+				continue;
+
+			sizes.push_back(size);
+		}
+	}
+
+	std::sort(sizes.begin(), sizes.end());
+
+	return sizes;
+}
+
+/**
+ * \brief Retrieve a frame size range
+ * \param[in] pixelformat Pixel format to retrieve range for
+ * \returns Range description of frame size
+ */
+SizeRange StreamFormats::range(unsigned int pixelformat) const
+{
+	auto const it = formats_.find(pixelformat);
+	if (it == formats_.end())
+		return {};
+
+	const SizeRange &first = it->second.front();
+	if (it->second.size() == 1)
+		return first;
+
+	LOG(Stream, Debug) << "Building range from discret";
+
+	SizeRange range(first.min.width, first.min.height,
+			first.max.width, first.max.height);
+
+	for (const SizeRange &limit : it->second) {
+		if (limit.min < range.min)
+			range.min = limit.min;
+
+		if (limit.max > range.max)
+			range.max = limit.max;
+	}
+
+	return range;
+}
+
 /**
  * \struct StreamConfiguration
  * \brief Configuration parameters for a stream