[libcamera-devel,v2,6/6] libcamera: formats: Add additional data to PixelFormatInfo

Message ID 20200430030723.8908-7-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Improve conversion between DRM and V4L2 formats
Related show

Commit Message

Laurent Pinchart April 30, 2020, 3:07 a.m. UTC
Add three additional fields to PixelFormatInfo to report the number of
bits per pixel, the colour encoding, and the data packing.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/formats.cpp       | 127 ++++++++++++++++++++++++++++++++
 src/libcamera/include/formats.h |   9 +++
 2 files changed, 136 insertions(+)

Comments

Kieran Bingham April 30, 2020, 10:16 a.m. UTC | #1
Hi Laurent,

On 30/04/2020 04:07, Laurent Pinchart wrote:
> Add three additional fields to PixelFormatInfo to report the number of
> bits per pixel, the colour encoding, and the data packing.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  src/libcamera/formats.cpp       | 127 ++++++++++++++++++++++++++++++++
>  src/libcamera/include/formats.h |   9 +++
>  2 files changed, 136 insertions(+)
> 
> diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
> index f6f8bc1202c2..331affe9fb25 100644
> --- a/src/libcamera/formats.cpp
> +++ b/src/libcamera/formats.cpp
> @@ -121,6 +121,45 @@ const std::map<unsigned int, std::vector<SizeRange>> &ImageFormats::data() const
>   *
>   * \var PixelFormatInfo::v4l2Format
>   * \brief The V4L2 pixel format corresponding to the PixelFormat
> + *
> + * \var PixelFormatInfo::bitsPerPixel
> + * \brief The average number of bits per pixel
> + *
> + * The number per pixel averages the total number of bits for all colour
> + * components over the whole image, excluding any padding bits or padding
> + * pixels.
> + *
> + * For formats that store pixels with bit padding within words, only the
> + * effective bits are taken into account. For instance, 12-bit Bayer data
> + * stored in two bytes per pixel report 12, not 16, in this field.
> + *
> + * Formats that don't have a fixed number of bits per pixel, such as compressed
> + * formats, report 0 in this field.
> + *
> + * \var PixelFormatInfo::colourEncoding
> + * \brief The colour encoding type
> + *
> + * \var PixelFormatInfo::packed
> + * \brief Tell if multiple pixels are packed in the same bytes
> + *
> + * Packed formats are defined as storing data from multiple pixels in the same
> + * bytes. For instance, 12-bit Bayer data with two pixels stored in three bytes
> + * is packed, while the same data stored with 4 bits of padding in two bytes
> + * per pixel is not packed.
> + */
> +
> +/**
> + * \enum PixelFormatInfo::ColourEncoding
> + * \brief The colour encoding type
> + *
> + * \var PixelFormatInfo::ColourEncodingRGB
> + * \brief RGB colour encoding
> + *
> + * \var PixelFormatInfo::ColourEncodingYUV
> + * \brief YUV colour encoding
> + *
> + * \var PixelFormatInfo::ColourEncodingRAW
> + * \brief RAW colour encoding
>   */
>  
>  namespace {
> @@ -130,156 +169,244 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
>  	{ PixelFormat(DRM_FORMAT_BGR888), {
>  		.format = PixelFormat(DRM_FORMAT_BGR888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),
> +		.bitsPerPixel = 24,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,

Somehow we're going to have to deal with extra colourspace parameters
too I think, but not now ;-)

> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_RGB888), {
>  		.format = PixelFormat(DRM_FORMAT_RGB888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),
> +		.bitsPerPixel = 24,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_ABGR8888), {
>  		.format = PixelFormat(DRM_FORMAT_ABGR8888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),
> +		.bitsPerPixel = 32,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_ARGB8888), {
>  		.format = PixelFormat(DRM_FORMAT_ARGB8888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),
> +		.bitsPerPixel = 32,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_BGRA8888), {
>  		.format = PixelFormat(DRM_FORMAT_BGRA8888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),
> +		.bitsPerPixel = 32,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_RGBA8888), {
>  		.format = PixelFormat(DRM_FORMAT_RGBA8888),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),
> +		.bitsPerPixel = 32,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
> +		.packed = false,
>  	} },
>  
>  	/* YUV packed formats. */
>  	{ PixelFormat(DRM_FORMAT_YUYV), {
>  		.format = PixelFormat(DRM_FORMAT_YUYV),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_YVYU), {
>  		.format = PixelFormat(DRM_FORMAT_YVYU),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_UYVY), {
>  		.format = PixelFormat(DRM_FORMAT_UYVY),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_VYUY), {
>  		.format = PixelFormat(DRM_FORMAT_VYUY),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  
>  	/* YUV planar formats. */
>  	{ PixelFormat(DRM_FORMAT_NV16), {
>  		.format = PixelFormat(DRM_FORMAT_NV16),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_NV61), {
>  		.format = PixelFormat(DRM_FORMAT_NV61),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),
> +		.bitsPerPixel = 16,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_NV12), {
>  		.format = PixelFormat(DRM_FORMAT_NV12),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),
> +		.bitsPerPixel = 12,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_NV21), {
>  		.format = PixelFormat(DRM_FORMAT_NV21),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),
> +		.bitsPerPixel = 12,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  
>  	/* Greyscale formats. */
>  	{ PixelFormat(DRM_FORMAT_R8), {
>  		.format = PixelFormat(DRM_FORMAT_R8),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),
> +		.bitsPerPixel = 8,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  
>  	/* Bayer formats. */
>  	{ PixelFormat(DRM_FORMAT_SBGGR8), {
>  		.format = PixelFormat(DRM_FORMAT_SBGGR8),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),
> +		.bitsPerPixel = 8,
> +		.packed = false,

You're missing all the .colourEncoding = RAW statements on the raw
formats, so they're going to default to RGB (0).

With these added,

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>



>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGBRG8), {
>  		.format = PixelFormat(DRM_FORMAT_SGBRG8),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),
> +		.bitsPerPixel = 8,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGRBG8), {
>  		.format = PixelFormat(DRM_FORMAT_SGRBG8),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),
> +		.bitsPerPixel = 8,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SRGGB8), {
>  		.format = PixelFormat(DRM_FORMAT_SRGGB8),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),
> +		.bitsPerPixel = 8,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SBGGR10), {
>  		.format = PixelFormat(DRM_FORMAT_SBGGR10),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),
> +		.bitsPerPixel = 10,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGBRG10), {
>  		.format = PixelFormat(DRM_FORMAT_SGBRG10),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),
> +		.bitsPerPixel = 10,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGRBG10), {
>  		.format = PixelFormat(DRM_FORMAT_SGRBG10),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),
> +		.bitsPerPixel = 10,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SRGGB10), {
>  		.format = PixelFormat(DRM_FORMAT_SRGGB10),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
> +		.bitsPerPixel = 10,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
> +		.bitsPerPixel = 10,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
> +		.bitsPerPixel = 10,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),
> +		.bitsPerPixel = 10,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
> +		.bitsPerPixel = 10,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SBGGR12), {
>  		.format = PixelFormat(DRM_FORMAT_SBGGR12),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
> +		.bitsPerPixel = 12,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGBRG12), {
>  		.format = PixelFormat(DRM_FORMAT_SGBRG12),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
> +		.bitsPerPixel = 12,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGRBG12), {
>  		.format = PixelFormat(DRM_FORMAT_SGRBG12),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),
> +		.bitsPerPixel = 12,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SRGGB12), {
>  		.format = PixelFormat(DRM_FORMAT_SRGGB12),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),
> +		.bitsPerPixel = 12,
> +		.packed = false,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),
> +		.bitsPerPixel = 12,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),
> +		.bitsPerPixel = 12,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),
> +		.bitsPerPixel = 12,
> +		.packed = true,
>  	} },
>  	{ PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED), {
>  		.format = PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),
> +		.bitsPerPixel = 12,
> +		.packed = true,
>  	} },
>  
>  	/* Compressed formats. */
>  	{ PixelFormat(DRM_FORMAT_MJPEG), {
>  		.format = PixelFormat(DRM_FORMAT_MJPEG),
>  		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),
> +		.bitsPerPixel = 0,
> +		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
> +		.packed = false,
>  	} },
>  };
>  
> diff --git a/src/libcamera/include/formats.h b/src/libcamera/include/formats.h
> index 560df07c4451..291a1108aa33 100644
> --- a/src/libcamera/include/formats.h
> +++ b/src/libcamera/include/formats.h
> @@ -35,6 +35,12 @@ private:
>  class PixelFormatInfo
>  {
>  public:
> +	enum ColourEncoding {
> +		ColourEncodingRGB,
> +		ColourEncodingYUV,
> +		ColourEncodingRAW,
> +	};
> +
>  	bool isValid() const { return format.isValid(); }
>  
>  	static const PixelFormatInfo &info(const PixelFormat &format);
> @@ -42,6 +48,9 @@ public:
>  	/* \todo Add support for non-contiguous memory planes */
>  	PixelFormat format;
>  	V4L2PixelFormat v4l2Format;
> +	unsigned int bitsPerPixel;
> +	enum ColourEncoding colourEncoding;
> +	bool packed;
>  };
>  
>  } /* namespace libcamera */
>

Patch

diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
index f6f8bc1202c2..331affe9fb25 100644
--- a/src/libcamera/formats.cpp
+++ b/src/libcamera/formats.cpp
@@ -121,6 +121,45 @@  const std::map<unsigned int, std::vector<SizeRange>> &ImageFormats::data() const
  *
  * \var PixelFormatInfo::v4l2Format
  * \brief The V4L2 pixel format corresponding to the PixelFormat
+ *
+ * \var PixelFormatInfo::bitsPerPixel
+ * \brief The average number of bits per pixel
+ *
+ * The number per pixel averages the total number of bits for all colour
+ * components over the whole image, excluding any padding bits or padding
+ * pixels.
+ *
+ * For formats that store pixels with bit padding within words, only the
+ * effective bits are taken into account. For instance, 12-bit Bayer data
+ * stored in two bytes per pixel report 12, not 16, in this field.
+ *
+ * Formats that don't have a fixed number of bits per pixel, such as compressed
+ * formats, report 0 in this field.
+ *
+ * \var PixelFormatInfo::colourEncoding
+ * \brief The colour encoding type
+ *
+ * \var PixelFormatInfo::packed
+ * \brief Tell if multiple pixels are packed in the same bytes
+ *
+ * Packed formats are defined as storing data from multiple pixels in the same
+ * bytes. For instance, 12-bit Bayer data with two pixels stored in three bytes
+ * is packed, while the same data stored with 4 bits of padding in two bytes
+ * per pixel is not packed.
+ */
+
+/**
+ * \enum PixelFormatInfo::ColourEncoding
+ * \brief The colour encoding type
+ *
+ * \var PixelFormatInfo::ColourEncodingRGB
+ * \brief RGB colour encoding
+ *
+ * \var PixelFormatInfo::ColourEncodingYUV
+ * \brief YUV colour encoding
+ *
+ * \var PixelFormatInfo::ColourEncodingRAW
+ * \brief RAW colour encoding
  */
 
 namespace {
@@ -130,156 +169,244 @@  const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
 	{ PixelFormat(DRM_FORMAT_BGR888), {
 		.format = PixelFormat(DRM_FORMAT_BGR888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),
+		.bitsPerPixel = 24,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_RGB888), {
 		.format = PixelFormat(DRM_FORMAT_RGB888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),
+		.bitsPerPixel = 24,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_ABGR8888), {
 		.format = PixelFormat(DRM_FORMAT_ABGR8888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),
+		.bitsPerPixel = 32,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_ARGB8888), {
 		.format = PixelFormat(DRM_FORMAT_ARGB8888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),
+		.bitsPerPixel = 32,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_BGRA8888), {
 		.format = PixelFormat(DRM_FORMAT_BGRA8888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),
+		.bitsPerPixel = 32,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_RGBA8888), {
 		.format = PixelFormat(DRM_FORMAT_RGBA8888),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),
+		.bitsPerPixel = 32,
+		.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
+		.packed = false,
 	} },
 
 	/* YUV packed formats. */
 	{ PixelFormat(DRM_FORMAT_YUYV), {
 		.format = PixelFormat(DRM_FORMAT_YUYV),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_YVYU), {
 		.format = PixelFormat(DRM_FORMAT_YVYU),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_UYVY), {
 		.format = PixelFormat(DRM_FORMAT_UYVY),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_VYUY), {
 		.format = PixelFormat(DRM_FORMAT_VYUY),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 
 	/* YUV planar formats. */
 	{ PixelFormat(DRM_FORMAT_NV16), {
 		.format = PixelFormat(DRM_FORMAT_NV16),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_NV61), {
 		.format = PixelFormat(DRM_FORMAT_NV61),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),
+		.bitsPerPixel = 16,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_NV12), {
 		.format = PixelFormat(DRM_FORMAT_NV12),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),
+		.bitsPerPixel = 12,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_NV21), {
 		.format = PixelFormat(DRM_FORMAT_NV21),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),
+		.bitsPerPixel = 12,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 
 	/* Greyscale formats. */
 	{ PixelFormat(DRM_FORMAT_R8), {
 		.format = PixelFormat(DRM_FORMAT_R8),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),
+		.bitsPerPixel = 8,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 
 	/* Bayer formats. */
 	{ PixelFormat(DRM_FORMAT_SBGGR8), {
 		.format = PixelFormat(DRM_FORMAT_SBGGR8),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),
+		.bitsPerPixel = 8,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGBRG8), {
 		.format = PixelFormat(DRM_FORMAT_SGBRG8),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),
+		.bitsPerPixel = 8,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGRBG8), {
 		.format = PixelFormat(DRM_FORMAT_SGRBG8),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),
+		.bitsPerPixel = 8,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SRGGB8), {
 		.format = PixelFormat(DRM_FORMAT_SRGGB8),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),
+		.bitsPerPixel = 8,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SBGGR10), {
 		.format = PixelFormat(DRM_FORMAT_SBGGR10),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),
+		.bitsPerPixel = 10,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGBRG10), {
 		.format = PixelFormat(DRM_FORMAT_SGBRG10),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),
+		.bitsPerPixel = 10,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGRBG10), {
 		.format = PixelFormat(DRM_FORMAT_SGRBG10),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),
+		.bitsPerPixel = 10,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SRGGB10), {
 		.format = PixelFormat(DRM_FORMAT_SRGGB10),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
+		.bitsPerPixel = 10,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SBGGR10, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
+		.bitsPerPixel = 10,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SGBRG10, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
+		.bitsPerPixel = 10,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SGRBG10, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),
+		.bitsPerPixel = 10,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SRGGB10, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
+		.bitsPerPixel = 10,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SBGGR12), {
 		.format = PixelFormat(DRM_FORMAT_SBGGR12),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
+		.bitsPerPixel = 12,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGBRG12), {
 		.format = PixelFormat(DRM_FORMAT_SGBRG12),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
+		.bitsPerPixel = 12,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGRBG12), {
 		.format = PixelFormat(DRM_FORMAT_SGRBG12),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),
+		.bitsPerPixel = 12,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SRGGB12), {
 		.format = PixelFormat(DRM_FORMAT_SRGGB12),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),
+		.bitsPerPixel = 12,
+		.packed = false,
 	} },
 	{ PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SBGGR12, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),
+		.bitsPerPixel = 12,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SGBRG12, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),
+		.bitsPerPixel = 12,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SGRBG12, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),
+		.bitsPerPixel = 12,
+		.packed = true,
 	} },
 	{ PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED), {
 		.format = PixelFormat(DRM_FORMAT_SRGGB12, MIPI_FORMAT_MOD_CSI2_PACKED),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),
+		.bitsPerPixel = 12,
+		.packed = true,
 	} },
 
 	/* Compressed formats. */
 	{ PixelFormat(DRM_FORMAT_MJPEG), {
 		.format = PixelFormat(DRM_FORMAT_MJPEG),
 		.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),
+		.bitsPerPixel = 0,
+		.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
+		.packed = false,
 	} },
 };
 
diff --git a/src/libcamera/include/formats.h b/src/libcamera/include/formats.h
index 560df07c4451..291a1108aa33 100644
--- a/src/libcamera/include/formats.h
+++ b/src/libcamera/include/formats.h
@@ -35,6 +35,12 @@  private:
 class PixelFormatInfo
 {
 public:
+	enum ColourEncoding {
+		ColourEncodingRGB,
+		ColourEncodingYUV,
+		ColourEncodingRAW,
+	};
+
 	bool isValid() const { return format.isValid(); }
 
 	static const PixelFormatInfo &info(const PixelFormat &format);
@@ -42,6 +48,9 @@  public:
 	/* \todo Add support for non-contiguous memory planes */
 	PixelFormat format;
 	V4L2PixelFormat v4l2Format;
+	unsigned int bitsPerPixel;
+	enum ColourEncoding colourEncoding;
+	bool packed;
 };
 
 } /* namespace libcamera */