[libcamera-devel,v1,1/2] gstreamer: Provide colorimetry <> ColorSpace mappings
diff mbox series

Message ID 20220807121233.18353-2-rishikeshdonadkar@gmail.com
State Accepted
Headers show
Series
  • Multiple colorimetry support for libcamerasrc.
Related show

Commit Message

Rishikesh Donadkar Aug. 7, 2022, 12:12 p.m. UTC
Provide colorimetry <=> libcamera::ColorSpace mappings via:
- GstVideoColorimetry colorimetry_from_colorspace(colorspace);
- ColorSpace colorspace_from_colorimetry(colorimetry);

Read the colorimetry field from caps into the stream configuration.
After stream validation, the sensor supported colorimetry will
be retrieved and the caps will be updated accordingly.
---
 src/gstreamer/gstlibcamera-utils.cpp | 158 +++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)

Comments

Laurent Pinchart Aug. 8, 2022, 9:20 p.m. UTC | #1
Hi Rishikesh,

Thank you for the patch.

On Sun, Aug 07, 2022 at 05:42:32PM +0530, Rishikesh Donadkar via libcamera-devel wrote:
> Provide colorimetry <=> libcamera::ColorSpace mappings via:
> - GstVideoColorimetry colorimetry_from_colorspace(colorspace);
> - ColorSpace colorspace_from_colorimetry(colorimetry);
> 
> Read the colorimetry field from caps into the stream configuration.
> After stream validation, the sensor supported colorimetry will
> be retrieved and the caps will be updated accordingly.

Your SoB line is missing.

> ---
>  src/gstreamer/gstlibcamera-utils.cpp | 158 +++++++++++++++++++++++++++
>  1 file changed, 158 insertions(+)
> 
> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
> index c97c0d43..24d8d035 100644
> --- a/src/gstreamer/gstlibcamera-utils.cpp
> +++ b/src/gstreamer/gstlibcamera-utils.cpp
> @@ -45,6 +45,152 @@ static struct {
>  	/* \todo NV42 is used in libcamera but is not mapped in GStreamer yet. */
>  };
>  
> +static GstVideoColorimetry
> +colorimetry_from_colorspace(const ColorSpace &colorSpace)
> +{
> +	GstVideoColorimetry colorimetry;
> +
> +	switch (colorSpace.primaries) {
> +	case ColorSpace::Primaries::Raw:
> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
> +		break;
> +	case ColorSpace::Primaries::Smpte170m:
> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
> +		break;
> +	case ColorSpace::Primaries::Rec709:
> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
> +		break;
> +	case ColorSpace::Primaries::Rec2020:
> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
> +		break;
> +	}
> +
> +	switch (colorSpace.transferFunction) {
> +	case ColorSpace::TransferFunction::Linear:
> +		colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10;
> +		break;
> +	case ColorSpace::TransferFunction::Srgb:
> +		colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB;
> +		break;
> +	case ColorSpace::TransferFunction::Rec709:
> +		colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
> +		break;
> +	}
> +
> +	switch (colorSpace.ycbcrEncoding) {
> +	case ColorSpace::YcbcrEncoding::None:
> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
> +		break;
> +	case ColorSpace::YcbcrEncoding::Rec601:
> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
> +		break;
> +	case ColorSpace::YcbcrEncoding::Rec709:
> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
> +		break;
> +	case ColorSpace::YcbcrEncoding::Rec2020:
> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
> +		break;
> +	}
> +
> +	switch (colorSpace.range) {
> +	case ColorSpace::Range::Full:
> +		colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
> +		break;
> +	case ColorSpace::Range::Limited:
> +		colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
> +		break;
> +	}
> +
> +	return colorimetry;
> +}
> +
> +static ColorSpace
> +colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
> +{
> +	ColorSpace colorspace = ColorSpace::Raw;
> +
> +	switch (colorimetry.primaries) {
> +	case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN:
> +		/* Unknown primaries map to raw colorspace in gstreamer */

s/gstreamer/GStreamer./

> +		return ColorSpace::Raw;
> +	case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
> +		colorspace.primaries = ColorSpace::Primaries::Smpte170m;
> +		break;
> +	case GST_VIDEO_COLOR_PRIMARIES_BT709:
> +		colorspace.primaries = ColorSpace::Primaries::Rec709;
> +		break;
> +	case GST_VIDEO_COLOR_PRIMARIES_BT2020:
> +		colorspace.primaries = ColorSpace::Primaries::Rec2020;
> +		break;
> +	default:
> +		GST_WARNING("Colorimetry primaries %d not mapped in gstlibcamera",
> +			    colorimetry.primaries);
> +		return ColorSpace::Raw;
> +	}
> +
> +	switch (colorimetry.transfer) {
> +	/* Transfer function mappings inspired from v4l2src plugin */
> +	case GST_VIDEO_TRANSFER_GAMMA18:
> +	case GST_VIDEO_TRANSFER_GAMMA20:
> +	case GST_VIDEO_TRANSFER_GAMMA22:
> +	case GST_VIDEO_TRANSFER_GAMMA28:
> +		GST_WARNING("GAMMA 18, 20, 22, 28 transfer functions not supported");
> +	/* fallthrough */
> +	case GST_VIDEO_TRANSFER_GAMMA10:
> +		colorspace.transferFunction = ColorSpace::TransferFunction::Linear;
> +		break;
> +	case GST_VIDEO_TRANSFER_SRGB:
> +		colorspace.transferFunction = ColorSpace::TransferFunction::Srgb;
> +		break;
> +	case GST_VIDEO_TRANSFER_BT601:
> +	case GST_VIDEO_TRANSFER_BT2020_12:
> +	case GST_VIDEO_TRANSFER_BT2020_10:
> +	case GST_VIDEO_TRANSFER_BT709:
> +		colorspace.transferFunction = ColorSpace::TransferFunction::Rec709;
> +		break;
> +	default:
> +		GST_WARNING("Colorimetry transfer function %d not mapped in gstlibcamera",
> +			    colorimetry.transfer);
> +		return ColorSpace::Raw;
> +	}
> +
> +	switch (colorimetry.matrix) {
> +	case GST_VIDEO_COLOR_MATRIX_RGB:
> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::None;
> +		break;
> +	/* FCC is about the same as BT601 with less digit */

s/digit/digits./

> +	case GST_VIDEO_COLOR_MATRIX_FCC:
> +	case GST_VIDEO_COLOR_MATRIX_BT601:
> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec601;
> +		break;
> +	case GST_VIDEO_COLOR_MATRIX_BT709:
> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec709;
> +		break;
> +	case GST_VIDEO_COLOR_MATRIX_BT2020:
> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec2020;
> +		break;
> +	default:
> +		GST_WARNING("Colorimetry matrix %d not mapped in gstlibcamera",
> +			    colorimetry.matrix);
> +		return ColorSpace::Raw;
> +	}
> +
> +	switch (colorimetry.range) {
> +	case GST_VIDEO_COLOR_RANGE_0_255:
> +		colorspace.range = ColorSpace::Range::Full;
> +		break;
> +	case GST_VIDEO_COLOR_RANGE_16_235:
> +		colorspace.range = ColorSpace::Range::Limited;
> +		break;
> +	default:
> +		GST_WARNING("Colorimetry range %d not mapped in gstlibcamera",
> +			    colorimetry.range);
> +		return ColorSpace::Raw;
> +	}
> +
> +	return colorspace;
> +}
> +
>  static GstVideoFormat
>  pixel_format_to_gst_format(const PixelFormat &format)
>  {
> @@ -139,6 +285,18 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
>  			  "width", G_TYPE_INT, stream_cfg.size.width,
>  			  "height", G_TYPE_INT, stream_cfg.size.height,
>  			  nullptr);
> +
> +	if (stream_cfg.colorSpace) {
> +		GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value());
> +		gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
> +
> +		if (colorimetry_str)
> +			gst_structure_set(s, "colorimetry", G_TYPE_STRING, colorimetry_str, nullptr);
> +		else
> +			g_error("Got invalid colorimetry from ColorSpace: %s",
> +				ColorSpace::toString(stream_cfg.colorSpace).c_str());
> +	}
> +

This looks fine, but I think you're missing a part of the patch, as
colorspace_from_colorimetry() is not used. One option is to move the
function to patch 2/2, otherwise compiling this patch alone will
generate a compilation warning.

If you do that, the commit message should also be updated, to reflect
that this patch only handles the colorimetry in the libcamera to
GStreamer direction.

>  	gst_caps_append_structure(caps, s);
>  
>  	return caps;
Umang Jain Aug. 10, 2022, 4:30 a.m. UTC | #2
Hi Laurent and Rishi,

On 8/9/22 02:50, Laurent Pinchart via libcamera-devel wrote:
> Hi Rishikesh,
>
> Thank you for the patch.
>
> On Sun, Aug 07, 2022 at 05:42:32PM +0530, Rishikesh Donadkar via libcamera-devel wrote:
>> Provide colorimetry <=> libcamera::ColorSpace mappings via:
>> - GstVideoColorimetry colorimetry_from_colorspace(colorspace);
>> - ColorSpace colorspace_from_colorimetry(colorimetry);
>>
>> Read the colorimetry field from caps into the stream configuration.
>> After stream validation, the sensor supported colorimetry will
>> be retrieved and the caps will be updated accordingly.
> Your SoB line is missing.


This is should ideally be absent, as the mappings are handled as part of 
more general colorspaces rework

[v3 PATCH 4/4] gstreamer: Provide colorimetry <> ColorSpace mappings


Rishi, in cases like this, when you base your work on some out-of-tree 
patches, you can just the bits that are relevant:

For e.g. in this case - the 2/2 patch about multiple colorimetry and in 
the cover letter you mention :

     `Multiple colorimetry support`

     ....
     Based on patch/series "...."
     ....

So that the out-of-tree patches (but present on the list already for 
reviews) don't accidently get re-reviewed (or reviewed at a wrong place)

So, please ignore this patch (in v2 as well) and let's focus on multiple 
colorimetry plumbing :-)

>
>> ---
>>   src/gstreamer/gstlibcamera-utils.cpp | 158 +++++++++++++++++++++++++++
>>   1 file changed, 158 insertions(+)
>>
>> diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
>> index c97c0d43..24d8d035 100644
>> --- a/src/gstreamer/gstlibcamera-utils.cpp
>> +++ b/src/gstreamer/gstlibcamera-utils.cpp
>> @@ -45,6 +45,152 @@ static struct {
>>   	/* \todo NV42 is used in libcamera but is not mapped in GStreamer yet. */
>>   };
>>   
>> +static GstVideoColorimetry
>> +colorimetry_from_colorspace(const ColorSpace &colorSpace)
>> +{
>> +	GstVideoColorimetry colorimetry;
>> +
>> +	switch (colorSpace.primaries) {
>> +	case ColorSpace::Primaries::Raw:
>> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
>> +		break;
>> +	case ColorSpace::Primaries::Smpte170m:
>> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
>> +		break;
>> +	case ColorSpace::Primaries::Rec709:
>> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
>> +		break;
>> +	case ColorSpace::Primaries::Rec2020:
>> +		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
>> +		break;
>> +	}
>> +
>> +	switch (colorSpace.transferFunction) {
>> +	case ColorSpace::TransferFunction::Linear:
>> +		colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10;
>> +		break;
>> +	case ColorSpace::TransferFunction::Srgb:
>> +		colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB;
>> +		break;
>> +	case ColorSpace::TransferFunction::Rec709:
>> +		colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
>> +		break;
>> +	}
>> +
>> +	switch (colorSpace.ycbcrEncoding) {
>> +	case ColorSpace::YcbcrEncoding::None:
>> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
>> +		break;
>> +	case ColorSpace::YcbcrEncoding::Rec601:
>> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
>> +		break;
>> +	case ColorSpace::YcbcrEncoding::Rec709:
>> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
>> +		break;
>> +	case ColorSpace::YcbcrEncoding::Rec2020:
>> +		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
>> +		break;
>> +	}
>> +
>> +	switch (colorSpace.range) {
>> +	case ColorSpace::Range::Full:
>> +		colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
>> +		break;
>> +	case ColorSpace::Range::Limited:
>> +		colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
>> +		break;
>> +	}
>> +
>> +	return colorimetry;
>> +}
>> +
>> +static ColorSpace
>> +colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
>> +{
>> +	ColorSpace colorspace = ColorSpace::Raw;
>> +
>> +	switch (colorimetry.primaries) {
>> +	case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN:
>> +		/* Unknown primaries map to raw colorspace in gstreamer */
> s/gstreamer/GStreamer./
>
>> +		return ColorSpace::Raw;
>> +	case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
>> +		colorspace.primaries = ColorSpace::Primaries::Smpte170m;
>> +		break;
>> +	case GST_VIDEO_COLOR_PRIMARIES_BT709:
>> +		colorspace.primaries = ColorSpace::Primaries::Rec709;
>> +		break;
>> +	case GST_VIDEO_COLOR_PRIMARIES_BT2020:
>> +		colorspace.primaries = ColorSpace::Primaries::Rec2020;
>> +		break;
>> +	default:
>> +		GST_WARNING("Colorimetry primaries %d not mapped in gstlibcamera",
>> +			    colorimetry.primaries);
>> +		return ColorSpace::Raw;
>> +	}
>> +
>> +	switch (colorimetry.transfer) {
>> +	/* Transfer function mappings inspired from v4l2src plugin */
>> +	case GST_VIDEO_TRANSFER_GAMMA18:
>> +	case GST_VIDEO_TRANSFER_GAMMA20:
>> +	case GST_VIDEO_TRANSFER_GAMMA22:
>> +	case GST_VIDEO_TRANSFER_GAMMA28:
>> +		GST_WARNING("GAMMA 18, 20, 22, 28 transfer functions not supported");
>> +	/* fallthrough */
>> +	case GST_VIDEO_TRANSFER_GAMMA10:
>> +		colorspace.transferFunction = ColorSpace::TransferFunction::Linear;
>> +		break;
>> +	case GST_VIDEO_TRANSFER_SRGB:
>> +		colorspace.transferFunction = ColorSpace::TransferFunction::Srgb;
>> +		break;
>> +	case GST_VIDEO_TRANSFER_BT601:
>> +	case GST_VIDEO_TRANSFER_BT2020_12:
>> +	case GST_VIDEO_TRANSFER_BT2020_10:
>> +	case GST_VIDEO_TRANSFER_BT709:
>> +		colorspace.transferFunction = ColorSpace::TransferFunction::Rec709;
>> +		break;
>> +	default:
>> +		GST_WARNING("Colorimetry transfer function %d not mapped in gstlibcamera",
>> +			    colorimetry.transfer);
>> +		return ColorSpace::Raw;
>> +	}
>> +
>> +	switch (colorimetry.matrix) {
>> +	case GST_VIDEO_COLOR_MATRIX_RGB:
>> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::None;
>> +		break;
>> +	/* FCC is about the same as BT601 with less digit */
> s/digit/digits./
>
>> +	case GST_VIDEO_COLOR_MATRIX_FCC:
>> +	case GST_VIDEO_COLOR_MATRIX_BT601:
>> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec601;
>> +		break;
>> +	case GST_VIDEO_COLOR_MATRIX_BT709:
>> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec709;
>> +		break;
>> +	case GST_VIDEO_COLOR_MATRIX_BT2020:
>> +		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec2020;
>> +		break;
>> +	default:
>> +		GST_WARNING("Colorimetry matrix %d not mapped in gstlibcamera",
>> +			    colorimetry.matrix);
>> +		return ColorSpace::Raw;
>> +	}
>> +
>> +	switch (colorimetry.range) {
>> +	case GST_VIDEO_COLOR_RANGE_0_255:
>> +		colorspace.range = ColorSpace::Range::Full;
>> +		break;
>> +	case GST_VIDEO_COLOR_RANGE_16_235:
>> +		colorspace.range = ColorSpace::Range::Limited;
>> +		break;
>> +	default:
>> +		GST_WARNING("Colorimetry range %d not mapped in gstlibcamera",
>> +			    colorimetry.range);
>> +		return ColorSpace::Raw;
>> +	}
>> +
>> +	return colorspace;
>> +}
>> +
>>   static GstVideoFormat
>>   pixel_format_to_gst_format(const PixelFormat &format)
>>   {
>> @@ -139,6 +285,18 @@ gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
>>   			  "width", G_TYPE_INT, stream_cfg.size.width,
>>   			  "height", G_TYPE_INT, stream_cfg.size.height,
>>   			  nullptr);
>> +
>> +	if (stream_cfg.colorSpace) {
>> +		GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value());
>> +		gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
>> +
>> +		if (colorimetry_str)
>> +			gst_structure_set(s, "colorimetry", G_TYPE_STRING, colorimetry_str, nullptr);
>> +		else
>> +			g_error("Got invalid colorimetry from ColorSpace: %s",
>> +				ColorSpace::toString(stream_cfg.colorSpace).c_str());
>> +	}
>> +
> This looks fine, but I think you're missing a part of the patch, as
> colorspace_from_colorimetry() is not used. One option is to move the
> function to patch 2/2, otherwise compiling this patch alone will
> generate a compilation warning.
>
> If you do that, the commit message should also be updated, to reflect
> that this patch only handles the colorimetry in the libcamera to
> GStreamer direction.
>
>>   	gst_caps_append_structure(caps, s);
>>   
>>   	return caps;

Patch
diff mbox series

diff --git a/src/gstreamer/gstlibcamera-utils.cpp b/src/gstreamer/gstlibcamera-utils.cpp
index c97c0d43..24d8d035 100644
--- a/src/gstreamer/gstlibcamera-utils.cpp
+++ b/src/gstreamer/gstlibcamera-utils.cpp
@@ -45,6 +45,152 @@  static struct {
 	/* \todo NV42 is used in libcamera but is not mapped in GStreamer yet. */
 };
 
+static GstVideoColorimetry
+colorimetry_from_colorspace(const ColorSpace &colorSpace)
+{
+	GstVideoColorimetry colorimetry;
+
+	switch (colorSpace.primaries) {
+	case ColorSpace::Primaries::Raw:
+		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN;
+		break;
+	case ColorSpace::Primaries::Smpte170m:
+		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
+		break;
+	case ColorSpace::Primaries::Rec709:
+		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
+		break;
+	case ColorSpace::Primaries::Rec2020:
+		colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
+		break;
+	}
+
+	switch (colorSpace.transferFunction) {
+	case ColorSpace::TransferFunction::Linear:
+		colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10;
+		break;
+	case ColorSpace::TransferFunction::Srgb:
+		colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB;
+		break;
+	case ColorSpace::TransferFunction::Rec709:
+		colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
+		break;
+	}
+
+	switch (colorSpace.ycbcrEncoding) {
+	case ColorSpace::YcbcrEncoding::None:
+		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
+		break;
+	case ColorSpace::YcbcrEncoding::Rec601:
+		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
+		break;
+	case ColorSpace::YcbcrEncoding::Rec709:
+		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
+		break;
+	case ColorSpace::YcbcrEncoding::Rec2020:
+		colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
+		break;
+	}
+
+	switch (colorSpace.range) {
+	case ColorSpace::Range::Full:
+		colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
+		break;
+	case ColorSpace::Range::Limited:
+		colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
+		break;
+	}
+
+	return colorimetry;
+}
+
+static ColorSpace
+colorspace_from_colorimetry(const GstVideoColorimetry &colorimetry)
+{
+	ColorSpace colorspace = ColorSpace::Raw;
+
+	switch (colorimetry.primaries) {
+	case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN:
+		/* Unknown primaries map to raw colorspace in gstreamer */
+		return ColorSpace::Raw;
+	case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
+		colorspace.primaries = ColorSpace::Primaries::Smpte170m;
+		break;
+	case GST_VIDEO_COLOR_PRIMARIES_BT709:
+		colorspace.primaries = ColorSpace::Primaries::Rec709;
+		break;
+	case GST_VIDEO_COLOR_PRIMARIES_BT2020:
+		colorspace.primaries = ColorSpace::Primaries::Rec2020;
+		break;
+	default:
+		GST_WARNING("Colorimetry primaries %d not mapped in gstlibcamera",
+			    colorimetry.primaries);
+		return ColorSpace::Raw;
+	}
+
+	switch (colorimetry.transfer) {
+	/* Transfer function mappings inspired from v4l2src plugin */
+	case GST_VIDEO_TRANSFER_GAMMA18:
+	case GST_VIDEO_TRANSFER_GAMMA20:
+	case GST_VIDEO_TRANSFER_GAMMA22:
+	case GST_VIDEO_TRANSFER_GAMMA28:
+		GST_WARNING("GAMMA 18, 20, 22, 28 transfer functions not supported");
+	/* fallthrough */
+	case GST_VIDEO_TRANSFER_GAMMA10:
+		colorspace.transferFunction = ColorSpace::TransferFunction::Linear;
+		break;
+	case GST_VIDEO_TRANSFER_SRGB:
+		colorspace.transferFunction = ColorSpace::TransferFunction::Srgb;
+		break;
+	case GST_VIDEO_TRANSFER_BT601:
+	case GST_VIDEO_TRANSFER_BT2020_12:
+	case GST_VIDEO_TRANSFER_BT2020_10:
+	case GST_VIDEO_TRANSFER_BT709:
+		colorspace.transferFunction = ColorSpace::TransferFunction::Rec709;
+		break;
+	default:
+		GST_WARNING("Colorimetry transfer function %d not mapped in gstlibcamera",
+			    colorimetry.transfer);
+		return ColorSpace::Raw;
+	}
+
+	switch (colorimetry.matrix) {
+	case GST_VIDEO_COLOR_MATRIX_RGB:
+		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::None;
+		break;
+	/* FCC is about the same as BT601 with less digit */
+	case GST_VIDEO_COLOR_MATRIX_FCC:
+	case GST_VIDEO_COLOR_MATRIX_BT601:
+		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec601;
+		break;
+	case GST_VIDEO_COLOR_MATRIX_BT709:
+		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec709;
+		break;
+	case GST_VIDEO_COLOR_MATRIX_BT2020:
+		colorspace.ycbcrEncoding = ColorSpace::YcbcrEncoding::Rec2020;
+		break;
+	default:
+		GST_WARNING("Colorimetry matrix %d not mapped in gstlibcamera",
+			    colorimetry.matrix);
+		return ColorSpace::Raw;
+	}
+
+	switch (colorimetry.range) {
+	case GST_VIDEO_COLOR_RANGE_0_255:
+		colorspace.range = ColorSpace::Range::Full;
+		break;
+	case GST_VIDEO_COLOR_RANGE_16_235:
+		colorspace.range = ColorSpace::Range::Limited;
+		break;
+	default:
+		GST_WARNING("Colorimetry range %d not mapped in gstlibcamera",
+			    colorimetry.range);
+		return ColorSpace::Raw;
+	}
+
+	return colorspace;
+}
+
 static GstVideoFormat
 pixel_format_to_gst_format(const PixelFormat &format)
 {
@@ -139,6 +285,18 @@  gst_libcamera_stream_configuration_to_caps(const StreamConfiguration &stream_cfg
 			  "width", G_TYPE_INT, stream_cfg.size.width,
 			  "height", G_TYPE_INT, stream_cfg.size.height,
 			  nullptr);
+
+	if (stream_cfg.colorSpace) {
+		GstVideoColorimetry colorimetry = colorimetry_from_colorspace(stream_cfg.colorSpace.value());
+		gchar *colorimetry_str = gst_video_colorimetry_to_string(&colorimetry);
+
+		if (colorimetry_str)
+			gst_structure_set(s, "colorimetry", G_TYPE_STRING, colorimetry_str, nullptr);
+		else
+			g_error("Got invalid colorimetry from ColorSpace: %s",
+				ColorSpace::toString(stream_cfg.colorSpace).c_str());
+	}
+
 	gst_caps_append_structure(caps, s);
 
 	return caps;