[libcamera-devel,2/5] libcamera: Use the Size class through libcamera

Message ID 20190430183746.28518-3-laurent.pinchart@ideasonboard.com
State Accepted
Commit a2dddf7c26df3307b9d4554c25387a00687a6234
Headers show
Series
  • Miscellaneous cleanups and refactoring
Related show

Commit Message

Laurent Pinchart April 30, 2019, 6:37 p.m. UTC
Several of our structures include width and height fields that model a
size while be have a Size class for that purpose. Use the Size class
through libcamera, and give it a toString() method like other geometry
and format classes.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/geometry.h             |  2 ++
 include/libcamera/stream.h               |  5 ++-
 src/cam/main.cpp                         |  4 +--
 src/libcamera/camera.cpp                 |  2 +-
 src/libcamera/camera_sensor.cpp          |  3 +-
 src/libcamera/geometry.cpp               |  9 ++++++
 src/libcamera/include/v4l2_device.h      |  4 +--
 src/libcamera/include/v4l2_subdevice.h   |  3 +-
 src/libcamera/pipeline/ipu3/ipu3.cpp     | 39 ++++++++++--------------
 src/libcamera/pipeline/rkisp1/rkisp1.cpp | 16 ++++------
 src/libcamera/pipeline/uvcvideo.cpp      |  9 ++----
 src/libcamera/pipeline/vimc.cpp          |  9 ++----
 src/libcamera/stream.cpp                 | 22 +++++--------
 src/libcamera/v4l2_device.cpp            | 36 +++++++++-------------
 src/libcamera/v4l2_subdevice.cpp         | 24 ++++++---------
 src/qcam/main_window.cpp                 |  3 +-
 test/camera/configuration_set.cpp        |  7 ++---
 test/v4l2_device/formats.cpp             |  6 ++--
 test/v4l2_subdevice/test_formats.cpp     | 11 +++----
 19 files changed, 93 insertions(+), 121 deletions(-)

Comments

Niklas Söderlund April 30, 2019, 6:54 p.m. UTC | #1
Hi Laurent,

Thanks for your work.

On 2019-04-30 21:37:43 +0300, Laurent Pinchart wrote:
> Several of our structures include width and height fields that model a
> size while be have a Size class for that purpose. Use the Size class

s/be/we/

> through libcamera, and give it a toString() method like other geometry
> and format classes.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> ---
>  include/libcamera/geometry.h             |  2 ++
>  include/libcamera/stream.h               |  5 ++-
>  src/cam/main.cpp                         |  4 +--
>  src/libcamera/camera.cpp                 |  2 +-
>  src/libcamera/camera_sensor.cpp          |  3 +-
>  src/libcamera/geometry.cpp               |  9 ++++++
>  src/libcamera/include/v4l2_device.h      |  4 +--
>  src/libcamera/include/v4l2_subdevice.h   |  3 +-
>  src/libcamera/pipeline/ipu3/ipu3.cpp     | 39 ++++++++++--------------
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 16 ++++------
>  src/libcamera/pipeline/uvcvideo.cpp      |  9 ++----
>  src/libcamera/pipeline/vimc.cpp          |  9 ++----
>  src/libcamera/stream.cpp                 | 22 +++++--------
>  src/libcamera/v4l2_device.cpp            | 36 +++++++++-------------
>  src/libcamera/v4l2_subdevice.cpp         | 24 ++++++---------
>  src/qcam/main_window.cpp                 |  3 +-
>  test/camera/configuration_set.cpp        |  7 ++---
>  test/v4l2_device/formats.cpp             |  6 ++--
>  test/v4l2_subdevice/test_formats.cpp     | 11 +++----
>  19 files changed, 93 insertions(+), 121 deletions(-)
> 
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 3dbbced5c76f..a87d65d3ed79 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -40,6 +40,8 @@ struct Size {
>  
>  	unsigned int width;
>  	unsigned int height;
> +
> +	const std::string toString() const;
>  };
>  
>  bool operator==(const Size &lhs, const Size &rhs);
> diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
> index 3caaefc9f8e9..0c986310939b 100644
> --- a/include/libcamera/stream.h
> +++ b/include/libcamera/stream.h
> @@ -17,9 +17,8 @@ namespace libcamera {
>  class Camera;
>  
>  struct StreamConfiguration {
> -	unsigned int width;
> -	unsigned int height;
>  	unsigned int pixelFormat;
> +	Size size;
>  
>  	unsigned int bufferCount;
>  
> @@ -40,7 +39,7 @@ public:
>  
>  protected:
>  	explicit StreamUsage(Role role);
> -	StreamUsage(Role role, int width, int height);
> +	StreamUsage(Role role, const Size &size);
>  
>  private:
>  	Role role_;
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index 46aba728393c..f03c32b385a9 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -137,10 +137,10 @@ static int prepareCameraConfig(CameraConfiguration *config)
>  		it++;
>  
>  		if (conf.isSet("width"))
> -			(*config)[stream].width = conf["width"];
> +			(*config)[stream].size.width = conf["width"];
>  
>  		if (conf.isSet("height"))
> -			(*config)[stream].height = conf["height"];
> +			(*config)[stream].size.height = conf["height"];
>  
>  		/* TODO: Translate 4CC string to ID. */
>  		if (conf.isSet("pixelformat"))
> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> index ef9e15be09ec..6b74eb8e3507 100644
> --- a/src/libcamera/camera.cpp
> +++ b/src/libcamera/camera.cpp
> @@ -131,7 +131,7 @@ bool CameraConfiguration::isValid() const
>  	for (auto const &it : config_) {
>  		const StreamConfiguration &conf = it.second;
>  
> -		if (conf.width == 0 || conf.height == 0 ||
> +		if (conf.size.width == 0 || conf.size.height == 0 ||
>  		    conf.pixelFormat == 0 || conf.bufferCount == 0)
>  			return false;
>  	}
> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> index 8f2eab562b94..2b9d8fa593c1 100644
> --- a/src/libcamera/camera_sensor.cpp
> +++ b/src/libcamera/camera_sensor.cpp
> @@ -234,8 +234,7 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
>  		return format;
>  	}
>  
> -	format.width = bestSize->width;
> -	format.height = bestSize->height;
> +	format.size = *bestSize;
>  
>  	return format;
>  }
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index c8aa05f54b73..97c367bcb454 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -107,6 +107,15 @@ bool operator==(const Rectangle &lhs, const Rectangle &rhs)
>   * \brief The Size height
>   */
>  
> +/**
> + * \brief Assemble and return a string describing the size
> + * \return A string describing the size
> + */
> +const std::string Size::toString() const
> +{
> +	return std::to_string(width) + "x" + std::to_string(height);
> +}
> +
>  /**
>   * \brief Compare sizes for equality
>   * \return True if the two sizes are equal, false otherwise
> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> index b4b3cd5cfda3..2e7bd1e7f4cc 100644
> --- a/src/libcamera/include/v4l2_device.h
> +++ b/src/libcamera/include/v4l2_device.h
> @@ -13,6 +13,7 @@
>  
>  #include <linux/videodev2.h>
>  
> +#include <libcamera/geometry.h>
>  #include <libcamera/signal.h>
>  
>  #include "log.h"
> @@ -92,9 +93,8 @@ struct V4L2Capability final : v4l2_capability {
>  class V4L2DeviceFormat
>  {
>  public:
> -	uint32_t width;
> -	uint32_t height;
>  	uint32_t fourcc;
> +	Size size;
>  
>  	struct {
>  		uint32_t size;
> diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
> index c7b0b4cb76ea..3e4e5107aebe 100644
> --- a/src/libcamera/include/v4l2_subdevice.h
> +++ b/src/libcamera/include/v4l2_subdevice.h
> @@ -23,8 +23,7 @@ class MediaDevice;
>  
>  struct V4L2SubdeviceFormat {
>  	uint32_t mbus_code;
> -	uint32_t width;
> -	uint32_t height;
> +	Size size;
>  
>  	const std::string toString() const;
>  };
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index fbb37498ca8a..3aa475481cf9 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -262,8 +262,7 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera,
>  			 *
>  			 * \todo Clarify ImgU alignment requirements.
>  			 */
> -			streamConfig.width = 2560;
> -			streamConfig.height = 1920;
> +			streamConfig.size = { 2560, 1920 };
>  
>  			break;
>  
> @@ -295,8 +294,7 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera,
>  						      res.width);
>  			unsigned int height = std::min(usage.size().height,
>  						       res.height);
> -			streamConfig.width = width & ~7;
> -			streamConfig.height = height & ~3;
> +			streamConfig.size = { width & ~7, height & ~3 };
>  
>  			break;
>  		}
> @@ -347,15 +345,15 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera,
>  		 * \todo: Consider the BDS scaling factor requirements: "the
>  		 * downscaling factor must be an integer value multiple of 1/32"
>  		 */
> -		if (cfg.width % 8 || cfg.height % 4) {
> +		if (cfg.size.width % 8 || cfg.size.height % 4) {
>  			LOG(IPU3, Error)
>  				<< "Invalid stream size: bad alignment";
>  			return -EINVAL;
>  		}
>  
>  		const Size &resolution = cio2->sensor_->resolution();
> -		if (cfg.width > resolution.width ||
> -		    cfg.height > resolution.height) {
> +		if (cfg.size.width > resolution.width ||
> +		    cfg.size.height > resolution.height) {
>  			LOG(IPU3, Error)
>  				<< "Invalid stream size: larger than sensor resolution";
>  			return -EINVAL;
> @@ -365,10 +363,10 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera,
>  		 * Collect the maximum width and height: IPU3 can downscale
>  		 * only.
>  		 */
> -		if (cfg.width > sensorSize.width)
> -			sensorSize.width = cfg.width;
> -		if (cfg.height > sensorSize.height)
> -			sensorSize.height = cfg.height;
> +		if (cfg.size.width > sensorSize.width)
> +			sensorSize.width = cfg.size.width;
> +		if (cfg.size.height > sensorSize.height)
> +			sensorSize.height = cfg.size.height;
>  
>  		stream->active_ = true;
>  	}
> @@ -427,8 +425,7 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera,
>  	 * \todo Revise this when we'll actually use the stat node.
>  	 */
>  	StreamConfiguration statConfig = {};
> -	statConfig.width = cio2Format.width;
> -	statConfig.height = cio2Format.height;
> +	statConfig.size = cio2Format.size;
>  
>  	ret = imgu->configureOutput(&imgu->stat_, statConfig);
>  	if (ret)
> @@ -932,8 +929,8 @@ int ImgUDevice::configureInput(const Size &size,
>  	Rectangle rect = {
>  		.x = 0,
>  		.y = 0,
> -		.w = inputFormat->width,
> -		.h = inputFormat->height,
> +		.w = inputFormat->size.width,
> +		.h = inputFormat->size.height,
>  	};
>  	ret = imgu_->setCrop(PAD_INPUT, &rect);
>  	if (ret)
> @@ -947,9 +944,8 @@ int ImgUDevice::configureInput(const Size &size,
>  			 << rect.toString();
>  
>  	V4L2SubdeviceFormat imguFormat = {};
> -	imguFormat.width = size.width;
> -	imguFormat.height = size.height;
>  	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
> +	imguFormat.size = size;
>  
>  	ret = imgu_->setFormat(PAD_INPUT, &imguFormat);
>  	if (ret)
> @@ -973,9 +969,8 @@ int ImgUDevice::configureOutput(ImgUOutput *output,
>  	unsigned int pad = output->pad;
>  
>  	V4L2SubdeviceFormat imguFormat = {};
> -	imguFormat.width = config.width;
> -	imguFormat.height = config.height;
>  	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
> +	imguFormat.size = config.size;
>  
>  	int ret = imgu_->setFormat(pad, &imguFormat);
>  	if (ret)
> @@ -986,9 +981,8 @@ int ImgUDevice::configureOutput(ImgUOutput *output,
>  		return 0;
>  
>  	V4L2DeviceFormat outputFormat = {};
> -	outputFormat.width = config.width;
> -	outputFormat.height = config.height;
>  	outputFormat.fourcc = V4L2_PIX_FMT_NV12;
> +	outputFormat.size = config.size;
>  	outputFormat.planesCount = 2;
>  
>  	ret = dev->setFormat(&outputFormat);
> @@ -1288,9 +1282,8 @@ int CIO2Device::configure(const Size &size,
>  	if (ret)
>  		return ret;
>  
> -	outputFormat->width = sensorFormat.width;
> -	outputFormat->height = sensorFormat.height;
>  	outputFormat->fourcc = mediaBusToFormat(sensorFormat.mbus_code);
> +	outputFormat->size = sensorFormat.size;
>  	outputFormat->planesCount = 1;
>  
>  	ret = output_->setFormat(outputFormat);
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 9a63a68b81dd..7acf85155e2d 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -116,10 +116,8 @@ CameraConfiguration PipelineHandlerRkISP1::streamConfiguration(Camera *camera,
>  	CameraConfiguration configs;
>  	StreamConfiguration config{};
>  
> -	const Size &resolution = data->sensor_->resolution();
> -	config.width = resolution.width;
> -	config.height = resolution.height;
>  	config.pixelFormat = V4L2_PIX_FMT_NV12;
> +	config.size = data->sensor_->resolution();
>  	config.bufferCount = RKISP1_BUFFER_COUNT;
>  
>  	configs[&data->stream_] = config;
> @@ -137,8 +135,8 @@ int PipelineHandlerRkISP1::configureStreams(Camera *camera,
>  
>  	/* Verify the configuration. */
>  	const Size &resolution = sensor->resolution();
> -	if (cfg.width > resolution.width ||
> -	    cfg.height > resolution.height) {
> +	if (cfg.size.width > resolution.width ||
> +	    cfg.size.height > resolution.height) {
>  		LOG(RkISP1, Error)
>  			<< "Invalid stream size: larger than sensor resolution";
>  		return -EINVAL;
> @@ -193,7 +191,7 @@ int PipelineHandlerRkISP1::configureStreams(Camera *camera,
>  				     MEDIA_BUS_FMT_SGBRG8_1X8,
>  				     MEDIA_BUS_FMT_SGRBG8_1X8,
>  				     MEDIA_BUS_FMT_SRGGB8_1X8 },
> -				   Size(cfg.width, cfg.height));
> +				   cfg.size);
>  
>  	LOG(RkISP1, Debug) << "Configuring sensor with " << format.toString();
>  
> @@ -216,17 +214,15 @@ int PipelineHandlerRkISP1::configureStreams(Camera *camera,
>  		return ret;
>  
>  	V4L2DeviceFormat outputFormat = {};
> -	outputFormat.width = cfg.width;
> -	outputFormat.height = cfg.height;
>  	outputFormat.fourcc = cfg.pixelFormat;
> +	outputFormat.size = cfg.size;
>  	outputFormat.planesCount = 2;
>  
>  	ret = video_->setFormat(&outputFormat);
>  	if (ret)
>  		return ret;
>  
> -	if (outputFormat.width != cfg.width ||
> -	    outputFormat.height != cfg.height ||
> +	if (outputFormat.size != cfg.size ||
>  	    outputFormat.fourcc != cfg.pixelFormat) {
>  		LOG(RkISP1, Error)
>  			<< "Unable to configure capture in " << cfg.toString();
> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
> index fd1d6df177d9..358e247baade 100644
> --- a/src/libcamera/pipeline/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo.cpp
> @@ -92,9 +92,8 @@ PipelineHandlerUVC::streamConfiguration(Camera *camera,
>  	CameraConfiguration configs;
>  	StreamConfiguration config{};
>  
> -	config.width = 640;
> -	config.height = 480;
>  	config.pixelFormat = V4L2_PIX_FMT_YUYV;
> +	config.size = { 640, 480 };
>  	config.bufferCount = 4;
>  
>  	configs[&data->stream_] = config;
> @@ -110,16 +109,14 @@ int PipelineHandlerUVC::configureStreams(Camera *camera,
>  	int ret;
>  
>  	V4L2DeviceFormat format = {};
> -	format.width = cfg->width;
> -	format.height = cfg->height;
>  	format.fourcc = cfg->pixelFormat;
> +	format.size = cfg->size;
>  
>  	ret = data->video_->setFormat(&format);
>  	if (ret)
>  		return ret;
>  
> -	if (format.width != cfg->width ||
> -	    format.height != cfg->height ||
> +	if (format.size != cfg->size ||
>  	    format.fourcc != cfg->pixelFormat)
>  		return -EINVAL;
>  
> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
> index f95140661b2a..83fa9cf4033a 100644
> --- a/src/libcamera/pipeline/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc.cpp
> @@ -92,9 +92,8 @@ PipelineHandlerVimc::streamConfiguration(Camera *camera,
>  	CameraConfiguration configs;
>  	StreamConfiguration config{};
>  
> -	config.width = 640;
> -	config.height = 480;
>  	config.pixelFormat = V4L2_PIX_FMT_RGB24;
> +	config.size = { 640, 480 };
>  	config.bufferCount = 4;
>  
>  	configs[&data->stream_] = config;
> @@ -110,16 +109,14 @@ int PipelineHandlerVimc::configureStreams(Camera *camera,
>  	int ret;
>  
>  	V4L2DeviceFormat format = {};
> -	format.width = cfg->width;
> -	format.height = cfg->height;
>  	format.fourcc = cfg->pixelFormat;
> +	format.size = cfg->size;
>  
>  	ret = data->video_->setFormat(&format);
>  	if (ret)
>  		return ret;
>  
> -	if (format.width != cfg->width ||
> -	    format.height != cfg->height ||
> +	if (format.size != cfg->size ||
>  	    format.fourcc != cfg->pixelFormat)
>  		return -EINVAL;
>  
> diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
> index aeb479c57b37..4ff296e3a75b 100644
> --- a/src/libcamera/stream.cpp
> +++ b/src/libcamera/stream.cpp
> @@ -41,13 +41,8 @@ namespace libcamera {
>   */
>  
>  /**
> - * \var StreamConfiguration::width
> - * \brief Stream width in pixels
> - */
> -
> -/**
> - * \var StreamConfiguration::height
> - * \brief Stream height in pixels
> + * \var StreamConfiguration::size
> + * \brief Stream size in pixels
>   */
>  
>  /**
> @@ -73,8 +68,8 @@ std::string StreamConfiguration::toString() const
>  	std::stringstream ss;
>  
>  	ss.fill(0);
> -	ss << width << "x" << height << "-0x" << std::hex
> -	   << std::setw(8) << pixelFormat;
> +	ss << size.toString() << "-0x" << std::hex << std::setw(8)
> +	   << pixelFormat;
>  
>  	return ss.str();
>  }
> @@ -128,11 +123,10 @@ StreamUsage::StreamUsage(Role role)
>  /**
>   * \brief Create a stream usage with a desired size
>   * \param[in] role Stream role
> - * \param[in] width The desired width
> - * \param[in] height The desired height
> + * \param[in] size The desired size
>   */
> -StreamUsage::StreamUsage(Role role, int width, int height)
> -	: role_(role), size_(Size(width, height))
> +StreamUsage::StreamUsage(Role role, const Size &size)
> +	: role_(role), size_(size)
>  {
>  }
>  
> @@ -183,7 +177,7 @@ Stream::VideoRecording::VideoRecording()
>   * \param[in] height The desired viewfinder height
>   */
>  Stream::Viewfinder::Viewfinder(int width, int height)
> -	: StreamUsage(Role::Viewfinder, width, height)
> +	: StreamUsage(Role::Viewfinder, Size(width, height))
>  {
>  }
>  
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index a21944160253..1bbf79673c16 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -195,13 +195,8 @@ LOG_DEFINE_CATEGORY(V4L2)
>   */
>  
>  /**
> - * \var V4L2DeviceFormat::width
> - * \brief The image width in pixels
> - */
> -
> -/**
> - * \var V4L2DeviceFormat::height
> - * \brief The image height in pixels
> + * \var V4L2DeviceFormat::size
> + * \brief The image size in pixels
>   */
>  
>  /**
> @@ -236,8 +231,7 @@ const std::string V4L2DeviceFormat::toString() const
>  	std::stringstream ss;
>  
>  	ss.fill(0);
> -	ss << width << "x" << height << "-0x" << std::hex
> -	   << std::setw(8) << fourcc;
> +	ss << size.toString() << "-0x" << std::hex << std::setw(8) << fourcc;
>  
>  	return ss.str();
>  }
> @@ -458,8 +452,8 @@ int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *format)
>  		return ret;
>  	}
>  
> -	format->width = pix->width;
> -	format->height = pix->height;
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
>  	format->fourcc = pix->pixelformat;
>  	format->planesCount = 1;
>  	format->planes[0].bpl = pix->bytesperline;
> @@ -475,8 +469,8 @@ int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *format)
>  	int ret;
>  
>  	v4l2Format.type = bufferType_;
> -	pix->width = format->width;
> -	pix->height = format->height;
> +	pix->width = format->size.width;
> +	pix->height = format->size.height;
>  	pix->pixelformat = format->fourcc;
>  	pix->bytesperline = format->planes[0].bpl;
>  	pix->field = V4L2_FIELD_NONE;
> @@ -492,8 +486,8 @@ int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *format)
>  	 * Return to caller the format actually applied on the device,
>  	 * which might differ from the requested one.
>  	 */
> -	format->width = pix->width;
> -	format->height = pix->height;
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
>  	format->fourcc = pix->pixelformat;
>  	format->planesCount = 1;
>  	format->planes[0].bpl = pix->bytesperline;
> @@ -516,8 +510,8 @@ int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *format)
>  		return ret;
>  	}
>  
> -	format->width = pix->width;
> -	format->height = pix->height;
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
>  	format->fourcc = pix->pixelformat;
>  	format->planesCount = pix->num_planes;
>  
> @@ -536,8 +530,8 @@ int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
>  	int ret;
>  
>  	v4l2Format.type = bufferType_;
> -	pix->width = format->width;
> -	pix->height = format->height;
> +	pix->width = format->size.width;
> +	pix->height = format->size.height;
>  	pix->pixelformat = format->fourcc;
>  	pix->num_planes = format->planesCount;
>  	pix->field = V4L2_FIELD_NONE;
> @@ -558,8 +552,8 @@ int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
>  	 * Return to caller the format actually applied on the device,
>  	 * which might differ from the requested one.
>  	 */
> -	format->width = pix->width;
> -	format->height = pix->height;
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
>  	format->fourcc = pix->pixelformat;
>  	format->planesCount = pix->num_planes;
>  	for (unsigned int i = 0; i < format->planesCount; ++i) {
> diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
> index 6fc866a6450b..fceee33156e9 100644
> --- a/src/libcamera/v4l2_subdevice.cpp
> +++ b/src/libcamera/v4l2_subdevice.cpp
> @@ -65,13 +65,8 @@ LOG_DEFINE_CATEGORY(V4L2Subdev)
>   */
>  
>  /**
> - * \var V4L2SubdeviceFormat::width
> - * \brief The image width in pixels
> - */
> -
> -/**
> - * \var V4L2SubdeviceFormat::height
> - * \brief The image height in pixels
> + * \var V4L2SubdeviceFormat::size
> + * \brief The image size in pixels
>   */
>  
>  /**
> @@ -83,8 +78,7 @@ const std::string V4L2SubdeviceFormat::toString() const
>  	std::stringstream ss;
>  
>  	ss.fill(0);
> -	ss << width << "x" << height << "-0x" << std::hex
> -	   << std::setw(4) << mbus_code;
> +	ss << size.toString() << "-0x" << std::hex << std::setw(4) << mbus_code;
>  
>  	return ss.str();
>  }
> @@ -265,8 +259,8 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
>  		return ret;
>  	}
>  
> -	format->width = subdevFmt.format.width;
> -	format->height = subdevFmt.format.height;
> +	format->size.width = subdevFmt.format.width;
> +	format->size.height = subdevFmt.format.height;
>  	format->mbus_code = subdevFmt.format.code;
>  
>  	return 0;
> @@ -288,8 +282,8 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
>  	struct v4l2_subdev_format subdevFmt = {};
>  	subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>  	subdevFmt.pad = pad;
> -	subdevFmt.format.width = format->width;
> -	subdevFmt.format.height = format->height;
> +	subdevFmt.format.width = format->size.width;
> +	subdevFmt.format.height = format->size.height;
>  	subdevFmt.format.code = format->mbus_code;
>  
>  	int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);
> @@ -301,8 +295,8 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
>  		return ret;
>  	}
>  
> -	format->width = subdevFmt.format.width;
> -	format->height = subdevFmt.format.height;
> +	format->size.width = subdevFmt.format.width;
> +	format->size.height = subdevFmt.format.height;
>  	format->mbus_code = subdevFmt.format.code;
>  
>  	return 0;
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> index 4bc044037041..6e92f6a1124d 100644
> --- a/src/qcam/main_window.cpp
> +++ b/src/qcam/main_window.cpp
> @@ -106,7 +106,8 @@ int MainWindow::startCapture()
>  	}
>  
>  	const StreamConfiguration &sconf = config_[stream];
> -	ret = viewfinder_->setFormat(sconf.pixelFormat, sconf.width, sconf.height);
> +	ret = viewfinder_->setFormat(sconf.pixelFormat, sconf.size.width,
> +				     sconf.size.height);
>  	if (ret < 0) {
>  		std::cout << "Failed to set viewfinder format" << std::endl;
>  		return ret;
> diff --git a/test/camera/configuration_set.cpp b/test/camera/configuration_set.cpp
> index 5ac2a7a908c8..0c932bc1de18 100644
> --- a/test/camera/configuration_set.cpp
> +++ b/test/camera/configuration_set.cpp
> @@ -64,8 +64,8 @@ protected:
>  		 * the default configuration of the VIMC camera is known to
>  		 * work.
>  		 */
> -		sconf->width *= 2;
> -		sconf->height *= 2;
> +		sconf->size.width *= 2;
> +		sconf->size.height *= 2;
>  		if (camera_->configureStreams(conf)) {
>  			cout << "Failed to set modified configuration" << endl;
>  			return TestFail;
> @@ -74,8 +74,7 @@ protected:
>  		/*
>  		 * Test that setting an invalid configuration fails.
>  		 */
> -		sconf->width = 0;
> -		sconf->height = 0;
> +		sconf->size = { 0, 0 };
>  		if (!camera_->configureStreams(conf)) {
>  			cout << "Invalid configuration incorrectly accepted" << endl;
>  			return TestFail;
> diff --git a/test/v4l2_device/formats.cpp b/test/v4l2_device/formats.cpp
> index 30b8b5c3f3f5..007e7e9487b5 100644
> --- a/test/v4l2_device/formats.cpp
> +++ b/test/v4l2_device/formats.cpp
> @@ -31,8 +31,7 @@ int Format::run()
>  		return TestFail;
>  	}
>  
> -	format.width = UINT_MAX;
> -	format.height = UINT_MAX;
> +	format.size = { UINT_MAX, UINT_MAX };
>  	ret = capture_->setFormat(&format);
>  	if (ret) {
>  		cerr << "Failed to set format: image resolution is invalid: "
> @@ -41,7 +40,8 @@ int Format::run()
>  		return TestFail;
>  	}
>  
> -	if (format.width == UINT_MAX || format.height == UINT_MAX) {
> +	if (format.size.width == UINT_MAX ||
> +	    format.size.height == UINT_MAX) {
>  		cerr << "Failed to update image format = (UINT_MAX x UINT_MAX)"
>  		     << endl;
>  		return TestFail;
> diff --git a/test/v4l2_subdevice/test_formats.cpp b/test/v4l2_subdevice/test_formats.cpp
> index af954459a3d8..e90c2c2426da 100644
> --- a/test/v4l2_subdevice/test_formats.cpp
> +++ b/test/v4l2_subdevice/test_formats.cpp
> @@ -44,8 +44,7 @@ int FormatHandlingTest::run()
>  	/*
>  	 * Set unrealistic image resolutions and make sure it gets updated.
>  	 */
> -	format.width = UINT_MAX;
> -	format.height = UINT_MAX;
> +	format.size = { UINT_MAX, UINT_MAX };
>  	ret = scaler_->setFormat(0, &format);
>  	if (ret) {
>  		cerr << "Failed to set format: image resolution is wrong, but "
> @@ -53,13 +52,13 @@ int FormatHandlingTest::run()
>  		return TestFail;
>  	}
>  
> -	if (format.width == UINT_MAX || format.height == UINT_MAX) {
> +	if (format.size.width == UINT_MAX ||
> +	    format.size.height == UINT_MAX) {
>  		cerr << "Failed to update image format" << endl;
>  		return TestFail;
>  	}
>  
> -	format.width = 0;
> -	format.height = 0;
> +	format.size = { 0, 0 };
>  	ret = scaler_->setFormat(0, &format);
>  	if (ret) {
>  		cerr << "Failed to set format: image resolution is wrong, but "
> @@ -67,7 +66,7 @@ int FormatHandlingTest::run()
>  		return TestFail;
>  	}
>  
> -	if (format.width == 0 || format.height == 0) {
> +	if (format.size.width == 0 || format.size.height == 0) {
>  		cerr << "Failed to update image format" << endl;
>  		return TestFail;
>  	}
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 3dbbced5c76f..a87d65d3ed79 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -40,6 +40,8 @@  struct Size {
 
 	unsigned int width;
 	unsigned int height;
+
+	const std::string toString() const;
 };
 
 bool operator==(const Size &lhs, const Size &rhs);
diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index 3caaefc9f8e9..0c986310939b 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -17,9 +17,8 @@  namespace libcamera {
 class Camera;
 
 struct StreamConfiguration {
-	unsigned int width;
-	unsigned int height;
 	unsigned int pixelFormat;
+	Size size;
 
 	unsigned int bufferCount;
 
@@ -40,7 +39,7 @@  public:
 
 protected:
 	explicit StreamUsage(Role role);
-	StreamUsage(Role role, int width, int height);
+	StreamUsage(Role role, const Size &size);
 
 private:
 	Role role_;
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index 46aba728393c..f03c32b385a9 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -137,10 +137,10 @@  static int prepareCameraConfig(CameraConfiguration *config)
 		it++;
 
 		if (conf.isSet("width"))
-			(*config)[stream].width = conf["width"];
+			(*config)[stream].size.width = conf["width"];
 
 		if (conf.isSet("height"))
-			(*config)[stream].height = conf["height"];
+			(*config)[stream].size.height = conf["height"];
 
 		/* TODO: Translate 4CC string to ID. */
 		if (conf.isSet("pixelformat"))
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index ef9e15be09ec..6b74eb8e3507 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -131,7 +131,7 @@  bool CameraConfiguration::isValid() const
 	for (auto const &it : config_) {
 		const StreamConfiguration &conf = it.second;
 
-		if (conf.width == 0 || conf.height == 0 ||
+		if (conf.size.width == 0 || conf.size.height == 0 ||
 		    conf.pixelFormat == 0 || conf.bufferCount == 0)
 			return false;
 	}
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
index 8f2eab562b94..2b9d8fa593c1 100644
--- a/src/libcamera/camera_sensor.cpp
+++ b/src/libcamera/camera_sensor.cpp
@@ -234,8 +234,7 @@  V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
 		return format;
 	}
 
-	format.width = bestSize->width;
-	format.height = bestSize->height;
+	format.size = *bestSize;
 
 	return format;
 }
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index c8aa05f54b73..97c367bcb454 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -107,6 +107,15 @@  bool operator==(const Rectangle &lhs, const Rectangle &rhs)
  * \brief The Size height
  */
 
+/**
+ * \brief Assemble and return a string describing the size
+ * \return A string describing the size
+ */
+const std::string Size::toString() const
+{
+	return std::to_string(width) + "x" + std::to_string(height);
+}
+
 /**
  * \brief Compare sizes for equality
  * \return True if the two sizes are equal, false otherwise
diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
index b4b3cd5cfda3..2e7bd1e7f4cc 100644
--- a/src/libcamera/include/v4l2_device.h
+++ b/src/libcamera/include/v4l2_device.h
@@ -13,6 +13,7 @@ 
 
 #include <linux/videodev2.h>
 
+#include <libcamera/geometry.h>
 #include <libcamera/signal.h>
 
 #include "log.h"
@@ -92,9 +93,8 @@  struct V4L2Capability final : v4l2_capability {
 class V4L2DeviceFormat
 {
 public:
-	uint32_t width;
-	uint32_t height;
 	uint32_t fourcc;
+	Size size;
 
 	struct {
 		uint32_t size;
diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
index c7b0b4cb76ea..3e4e5107aebe 100644
--- a/src/libcamera/include/v4l2_subdevice.h
+++ b/src/libcamera/include/v4l2_subdevice.h
@@ -23,8 +23,7 @@  class MediaDevice;
 
 struct V4L2SubdeviceFormat {
 	uint32_t mbus_code;
-	uint32_t width;
-	uint32_t height;
+	Size size;
 
 	const std::string toString() const;
 };
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index fbb37498ca8a..3aa475481cf9 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -262,8 +262,7 @@  PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 			 *
 			 * \todo Clarify ImgU alignment requirements.
 			 */
-			streamConfig.width = 2560;
-			streamConfig.height = 1920;
+			streamConfig.size = { 2560, 1920 };
 
 			break;
 
@@ -295,8 +294,7 @@  PipelineHandlerIPU3::streamConfiguration(Camera *camera,
 						      res.width);
 			unsigned int height = std::min(usage.size().height,
 						       res.height);
-			streamConfig.width = width & ~7;
-			streamConfig.height = height & ~3;
+			streamConfig.size = { width & ~7, height & ~3 };
 
 			break;
 		}
@@ -347,15 +345,15 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 		 * \todo: Consider the BDS scaling factor requirements: "the
 		 * downscaling factor must be an integer value multiple of 1/32"
 		 */
-		if (cfg.width % 8 || cfg.height % 4) {
+		if (cfg.size.width % 8 || cfg.size.height % 4) {
 			LOG(IPU3, Error)
 				<< "Invalid stream size: bad alignment";
 			return -EINVAL;
 		}
 
 		const Size &resolution = cio2->sensor_->resolution();
-		if (cfg.width > resolution.width ||
-		    cfg.height > resolution.height) {
+		if (cfg.size.width > resolution.width ||
+		    cfg.size.height > resolution.height) {
 			LOG(IPU3, Error)
 				<< "Invalid stream size: larger than sensor resolution";
 			return -EINVAL;
@@ -365,10 +363,10 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 		 * Collect the maximum width and height: IPU3 can downscale
 		 * only.
 		 */
-		if (cfg.width > sensorSize.width)
-			sensorSize.width = cfg.width;
-		if (cfg.height > sensorSize.height)
-			sensorSize.height = cfg.height;
+		if (cfg.size.width > sensorSize.width)
+			sensorSize.width = cfg.size.width;
+		if (cfg.size.height > sensorSize.height)
+			sensorSize.height = cfg.size.height;
 
 		stream->active_ = true;
 	}
@@ -427,8 +425,7 @@  int PipelineHandlerIPU3::configureStreams(Camera *camera,
 	 * \todo Revise this when we'll actually use the stat node.
 	 */
 	StreamConfiguration statConfig = {};
-	statConfig.width = cio2Format.width;
-	statConfig.height = cio2Format.height;
+	statConfig.size = cio2Format.size;
 
 	ret = imgu->configureOutput(&imgu->stat_, statConfig);
 	if (ret)
@@ -932,8 +929,8 @@  int ImgUDevice::configureInput(const Size &size,
 	Rectangle rect = {
 		.x = 0,
 		.y = 0,
-		.w = inputFormat->width,
-		.h = inputFormat->height,
+		.w = inputFormat->size.width,
+		.h = inputFormat->size.height,
 	};
 	ret = imgu_->setCrop(PAD_INPUT, &rect);
 	if (ret)
@@ -947,9 +944,8 @@  int ImgUDevice::configureInput(const Size &size,
 			 << rect.toString();
 
 	V4L2SubdeviceFormat imguFormat = {};
-	imguFormat.width = size.width;
-	imguFormat.height = size.height;
 	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+	imguFormat.size = size;
 
 	ret = imgu_->setFormat(PAD_INPUT, &imguFormat);
 	if (ret)
@@ -973,9 +969,8 @@  int ImgUDevice::configureOutput(ImgUOutput *output,
 	unsigned int pad = output->pad;
 
 	V4L2SubdeviceFormat imguFormat = {};
-	imguFormat.width = config.width;
-	imguFormat.height = config.height;
 	imguFormat.mbus_code = MEDIA_BUS_FMT_FIXED;
+	imguFormat.size = config.size;
 
 	int ret = imgu_->setFormat(pad, &imguFormat);
 	if (ret)
@@ -986,9 +981,8 @@  int ImgUDevice::configureOutput(ImgUOutput *output,
 		return 0;
 
 	V4L2DeviceFormat outputFormat = {};
-	outputFormat.width = config.width;
-	outputFormat.height = config.height;
 	outputFormat.fourcc = V4L2_PIX_FMT_NV12;
+	outputFormat.size = config.size;
 	outputFormat.planesCount = 2;
 
 	ret = dev->setFormat(&outputFormat);
@@ -1288,9 +1282,8 @@  int CIO2Device::configure(const Size &size,
 	if (ret)
 		return ret;
 
-	outputFormat->width = sensorFormat.width;
-	outputFormat->height = sensorFormat.height;
 	outputFormat->fourcc = mediaBusToFormat(sensorFormat.mbus_code);
+	outputFormat->size = sensorFormat.size;
 	outputFormat->planesCount = 1;
 
 	ret = output_->setFormat(outputFormat);
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 9a63a68b81dd..7acf85155e2d 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -116,10 +116,8 @@  CameraConfiguration PipelineHandlerRkISP1::streamConfiguration(Camera *camera,
 	CameraConfiguration configs;
 	StreamConfiguration config{};
 
-	const Size &resolution = data->sensor_->resolution();
-	config.width = resolution.width;
-	config.height = resolution.height;
 	config.pixelFormat = V4L2_PIX_FMT_NV12;
+	config.size = data->sensor_->resolution();
 	config.bufferCount = RKISP1_BUFFER_COUNT;
 
 	configs[&data->stream_] = config;
@@ -137,8 +135,8 @@  int PipelineHandlerRkISP1::configureStreams(Camera *camera,
 
 	/* Verify the configuration. */
 	const Size &resolution = sensor->resolution();
-	if (cfg.width > resolution.width ||
-	    cfg.height > resolution.height) {
+	if (cfg.size.width > resolution.width ||
+	    cfg.size.height > resolution.height) {
 		LOG(RkISP1, Error)
 			<< "Invalid stream size: larger than sensor resolution";
 		return -EINVAL;
@@ -193,7 +191,7 @@  int PipelineHandlerRkISP1::configureStreams(Camera *camera,
 				     MEDIA_BUS_FMT_SGBRG8_1X8,
 				     MEDIA_BUS_FMT_SGRBG8_1X8,
 				     MEDIA_BUS_FMT_SRGGB8_1X8 },
-				   Size(cfg.width, cfg.height));
+				   cfg.size);
 
 	LOG(RkISP1, Debug) << "Configuring sensor with " << format.toString();
 
@@ -216,17 +214,15 @@  int PipelineHandlerRkISP1::configureStreams(Camera *camera,
 		return ret;
 
 	V4L2DeviceFormat outputFormat = {};
-	outputFormat.width = cfg.width;
-	outputFormat.height = cfg.height;
 	outputFormat.fourcc = cfg.pixelFormat;
+	outputFormat.size = cfg.size;
 	outputFormat.planesCount = 2;
 
 	ret = video_->setFormat(&outputFormat);
 	if (ret)
 		return ret;
 
-	if (outputFormat.width != cfg.width ||
-	    outputFormat.height != cfg.height ||
+	if (outputFormat.size != cfg.size ||
 	    outputFormat.fourcc != cfg.pixelFormat) {
 		LOG(RkISP1, Error)
 			<< "Unable to configure capture in " << cfg.toString();
diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
index fd1d6df177d9..358e247baade 100644
--- a/src/libcamera/pipeline/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo.cpp
@@ -92,9 +92,8 @@  PipelineHandlerUVC::streamConfiguration(Camera *camera,
 	CameraConfiguration configs;
 	StreamConfiguration config{};
 
-	config.width = 640;
-	config.height = 480;
 	config.pixelFormat = V4L2_PIX_FMT_YUYV;
+	config.size = { 640, 480 };
 	config.bufferCount = 4;
 
 	configs[&data->stream_] = config;
@@ -110,16 +109,14 @@  int PipelineHandlerUVC::configureStreams(Camera *camera,
 	int ret;
 
 	V4L2DeviceFormat format = {};
-	format.width = cfg->width;
-	format.height = cfg->height;
 	format.fourcc = cfg->pixelFormat;
+	format.size = cfg->size;
 
 	ret = data->video_->setFormat(&format);
 	if (ret)
 		return ret;
 
-	if (format.width != cfg->width ||
-	    format.height != cfg->height ||
+	if (format.size != cfg->size ||
 	    format.fourcc != cfg->pixelFormat)
 		return -EINVAL;
 
diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
index f95140661b2a..83fa9cf4033a 100644
--- a/src/libcamera/pipeline/vimc.cpp
+++ b/src/libcamera/pipeline/vimc.cpp
@@ -92,9 +92,8 @@  PipelineHandlerVimc::streamConfiguration(Camera *camera,
 	CameraConfiguration configs;
 	StreamConfiguration config{};
 
-	config.width = 640;
-	config.height = 480;
 	config.pixelFormat = V4L2_PIX_FMT_RGB24;
+	config.size = { 640, 480 };
 	config.bufferCount = 4;
 
 	configs[&data->stream_] = config;
@@ -110,16 +109,14 @@  int PipelineHandlerVimc::configureStreams(Camera *camera,
 	int ret;
 
 	V4L2DeviceFormat format = {};
-	format.width = cfg->width;
-	format.height = cfg->height;
 	format.fourcc = cfg->pixelFormat;
+	format.size = cfg->size;
 
 	ret = data->video_->setFormat(&format);
 	if (ret)
 		return ret;
 
-	if (format.width != cfg->width ||
-	    format.height != cfg->height ||
+	if (format.size != cfg->size ||
 	    format.fourcc != cfg->pixelFormat)
 		return -EINVAL;
 
diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp
index aeb479c57b37..4ff296e3a75b 100644
--- a/src/libcamera/stream.cpp
+++ b/src/libcamera/stream.cpp
@@ -41,13 +41,8 @@  namespace libcamera {
  */
 
 /**
- * \var StreamConfiguration::width
- * \brief Stream width in pixels
- */
-
-/**
- * \var StreamConfiguration::height
- * \brief Stream height in pixels
+ * \var StreamConfiguration::size
+ * \brief Stream size in pixels
  */
 
 /**
@@ -73,8 +68,8 @@  std::string StreamConfiguration::toString() const
 	std::stringstream ss;
 
 	ss.fill(0);
-	ss << width << "x" << height << "-0x" << std::hex
-	   << std::setw(8) << pixelFormat;
+	ss << size.toString() << "-0x" << std::hex << std::setw(8)
+	   << pixelFormat;
 
 	return ss.str();
 }
@@ -128,11 +123,10 @@  StreamUsage::StreamUsage(Role role)
 /**
  * \brief Create a stream usage with a desired size
  * \param[in] role Stream role
- * \param[in] width The desired width
- * \param[in] height The desired height
+ * \param[in] size The desired size
  */
-StreamUsage::StreamUsage(Role role, int width, int height)
-	: role_(role), size_(Size(width, height))
+StreamUsage::StreamUsage(Role role, const Size &size)
+	: role_(role), size_(size)
 {
 }
 
@@ -183,7 +177,7 @@  Stream::VideoRecording::VideoRecording()
  * \param[in] height The desired viewfinder height
  */
 Stream::Viewfinder::Viewfinder(int width, int height)
-	: StreamUsage(Role::Viewfinder, width, height)
+	: StreamUsage(Role::Viewfinder, Size(width, height))
 {
 }
 
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index a21944160253..1bbf79673c16 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -195,13 +195,8 @@  LOG_DEFINE_CATEGORY(V4L2)
  */
 
 /**
- * \var V4L2DeviceFormat::width
- * \brief The image width in pixels
- */
-
-/**
- * \var V4L2DeviceFormat::height
- * \brief The image height in pixels
+ * \var V4L2DeviceFormat::size
+ * \brief The image size in pixels
  */
 
 /**
@@ -236,8 +231,7 @@  const std::string V4L2DeviceFormat::toString() const
 	std::stringstream ss;
 
 	ss.fill(0);
-	ss << width << "x" << height << "-0x" << std::hex
-	   << std::setw(8) << fourcc;
+	ss << size.toString() << "-0x" << std::hex << std::setw(8) << fourcc;
 
 	return ss.str();
 }
@@ -458,8 +452,8 @@  int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *format)
 		return ret;
 	}
 
-	format->width = pix->width;
-	format->height = pix->height;
+	format->size.width = pix->width;
+	format->size.height = pix->height;
 	format->fourcc = pix->pixelformat;
 	format->planesCount = 1;
 	format->planes[0].bpl = pix->bytesperline;
@@ -475,8 +469,8 @@  int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *format)
 	int ret;
 
 	v4l2Format.type = bufferType_;
-	pix->width = format->width;
-	pix->height = format->height;
+	pix->width = format->size.width;
+	pix->height = format->size.height;
 	pix->pixelformat = format->fourcc;
 	pix->bytesperline = format->planes[0].bpl;
 	pix->field = V4L2_FIELD_NONE;
@@ -492,8 +486,8 @@  int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *format)
 	 * Return to caller the format actually applied on the device,
 	 * which might differ from the requested one.
 	 */
-	format->width = pix->width;
-	format->height = pix->height;
+	format->size.width = pix->width;
+	format->size.height = pix->height;
 	format->fourcc = pix->pixelformat;
 	format->planesCount = 1;
 	format->planes[0].bpl = pix->bytesperline;
@@ -516,8 +510,8 @@  int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *format)
 		return ret;
 	}
 
-	format->width = pix->width;
-	format->height = pix->height;
+	format->size.width = pix->width;
+	format->size.height = pix->height;
 	format->fourcc = pix->pixelformat;
 	format->planesCount = pix->num_planes;
 
@@ -536,8 +530,8 @@  int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
 	int ret;
 
 	v4l2Format.type = bufferType_;
-	pix->width = format->width;
-	pix->height = format->height;
+	pix->width = format->size.width;
+	pix->height = format->size.height;
 	pix->pixelformat = format->fourcc;
 	pix->num_planes = format->planesCount;
 	pix->field = V4L2_FIELD_NONE;
@@ -558,8 +552,8 @@  int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
 	 * Return to caller the format actually applied on the device,
 	 * which might differ from the requested one.
 	 */
-	format->width = pix->width;
-	format->height = pix->height;
+	format->size.width = pix->width;
+	format->size.height = pix->height;
 	format->fourcc = pix->pixelformat;
 	format->planesCount = pix->num_planes;
 	for (unsigned int i = 0; i < format->planesCount; ++i) {
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
index 6fc866a6450b..fceee33156e9 100644
--- a/src/libcamera/v4l2_subdevice.cpp
+++ b/src/libcamera/v4l2_subdevice.cpp
@@ -65,13 +65,8 @@  LOG_DEFINE_CATEGORY(V4L2Subdev)
  */
 
 /**
- * \var V4L2SubdeviceFormat::width
- * \brief The image width in pixels
- */
-
-/**
- * \var V4L2SubdeviceFormat::height
- * \brief The image height in pixels
+ * \var V4L2SubdeviceFormat::size
+ * \brief The image size in pixels
  */
 
 /**
@@ -83,8 +78,7 @@  const std::string V4L2SubdeviceFormat::toString() const
 	std::stringstream ss;
 
 	ss.fill(0);
-	ss << width << "x" << height << "-0x" << std::hex
-	   << std::setw(4) << mbus_code;
+	ss << size.toString() << "-0x" << std::hex << std::setw(4) << mbus_code;
 
 	return ss.str();
 }
@@ -265,8 +259,8 @@  int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
 		return ret;
 	}
 
-	format->width = subdevFmt.format.width;
-	format->height = subdevFmt.format.height;
+	format->size.width = subdevFmt.format.width;
+	format->size.height = subdevFmt.format.height;
 	format->mbus_code = subdevFmt.format.code;
 
 	return 0;
@@ -288,8 +282,8 @@  int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
 	struct v4l2_subdev_format subdevFmt = {};
 	subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
 	subdevFmt.pad = pad;
-	subdevFmt.format.width = format->width;
-	subdevFmt.format.height = format->height;
+	subdevFmt.format.width = format->size.width;
+	subdevFmt.format.height = format->size.height;
 	subdevFmt.format.code = format->mbus_code;
 
 	int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);
@@ -301,8 +295,8 @@  int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
 		return ret;
 	}
 
-	format->width = subdevFmt.format.width;
-	format->height = subdevFmt.format.height;
+	format->size.width = subdevFmt.format.width;
+	format->size.height = subdevFmt.format.height;
 	format->mbus_code = subdevFmt.format.code;
 
 	return 0;
diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
index 4bc044037041..6e92f6a1124d 100644
--- a/src/qcam/main_window.cpp
+++ b/src/qcam/main_window.cpp
@@ -106,7 +106,8 @@  int MainWindow::startCapture()
 	}
 
 	const StreamConfiguration &sconf = config_[stream];
-	ret = viewfinder_->setFormat(sconf.pixelFormat, sconf.width, sconf.height);
+	ret = viewfinder_->setFormat(sconf.pixelFormat, sconf.size.width,
+				     sconf.size.height);
 	if (ret < 0) {
 		std::cout << "Failed to set viewfinder format" << std::endl;
 		return ret;
diff --git a/test/camera/configuration_set.cpp b/test/camera/configuration_set.cpp
index 5ac2a7a908c8..0c932bc1de18 100644
--- a/test/camera/configuration_set.cpp
+++ b/test/camera/configuration_set.cpp
@@ -64,8 +64,8 @@  protected:
 		 * the default configuration of the VIMC camera is known to
 		 * work.
 		 */
-		sconf->width *= 2;
-		sconf->height *= 2;
+		sconf->size.width *= 2;
+		sconf->size.height *= 2;
 		if (camera_->configureStreams(conf)) {
 			cout << "Failed to set modified configuration" << endl;
 			return TestFail;
@@ -74,8 +74,7 @@  protected:
 		/*
 		 * Test that setting an invalid configuration fails.
 		 */
-		sconf->width = 0;
-		sconf->height = 0;
+		sconf->size = { 0, 0 };
 		if (!camera_->configureStreams(conf)) {
 			cout << "Invalid configuration incorrectly accepted" << endl;
 			return TestFail;
diff --git a/test/v4l2_device/formats.cpp b/test/v4l2_device/formats.cpp
index 30b8b5c3f3f5..007e7e9487b5 100644
--- a/test/v4l2_device/formats.cpp
+++ b/test/v4l2_device/formats.cpp
@@ -31,8 +31,7 @@  int Format::run()
 		return TestFail;
 	}
 
-	format.width = UINT_MAX;
-	format.height = UINT_MAX;
+	format.size = { UINT_MAX, UINT_MAX };
 	ret = capture_->setFormat(&format);
 	if (ret) {
 		cerr << "Failed to set format: image resolution is invalid: "
@@ -41,7 +40,8 @@  int Format::run()
 		return TestFail;
 	}
 
-	if (format.width == UINT_MAX || format.height == UINT_MAX) {
+	if (format.size.width == UINT_MAX ||
+	    format.size.height == UINT_MAX) {
 		cerr << "Failed to update image format = (UINT_MAX x UINT_MAX)"
 		     << endl;
 		return TestFail;
diff --git a/test/v4l2_subdevice/test_formats.cpp b/test/v4l2_subdevice/test_formats.cpp
index af954459a3d8..e90c2c2426da 100644
--- a/test/v4l2_subdevice/test_formats.cpp
+++ b/test/v4l2_subdevice/test_formats.cpp
@@ -44,8 +44,7 @@  int FormatHandlingTest::run()
 	/*
 	 * Set unrealistic image resolutions and make sure it gets updated.
 	 */
-	format.width = UINT_MAX;
-	format.height = UINT_MAX;
+	format.size = { UINT_MAX, UINT_MAX };
 	ret = scaler_->setFormat(0, &format);
 	if (ret) {
 		cerr << "Failed to set format: image resolution is wrong, but "
@@ -53,13 +52,13 @@  int FormatHandlingTest::run()
 		return TestFail;
 	}
 
-	if (format.width == UINT_MAX || format.height == UINT_MAX) {
+	if (format.size.width == UINT_MAX ||
+	    format.size.height == UINT_MAX) {
 		cerr << "Failed to update image format" << endl;
 		return TestFail;
 	}
 
-	format.width = 0;
-	format.height = 0;
+	format.size = { 0, 0 };
 	ret = scaler_->setFormat(0, &format);
 	if (ret) {
 		cerr << "Failed to set format: image resolution is wrong, but "
@@ -67,7 +66,7 @@  int FormatHandlingTest::run()
 		return TestFail;
 	}
 
-	if (format.width == 0 || format.height == 0) {
+	if (format.size.width == 0 || format.size.height == 0) {
 		cerr << "Failed to update image format" << endl;
 		return TestFail;
 	}