[libcamera-devel] [RFC PATCH 2/3] libcamera: Support passing ColorSpaces to V4L2 drivers

David Plowman david.plowman at raspberrypi.com
Tue Jun 8 16:44:12 CEST 2021


The ColorSpace class is added to the StreamConfiguration, and is now
passed to V4L2 devices where it is handled appropriately. Note how
this means that the colour space is configured per-stream (though
platforms may restrict this).
---
 include/libcamera/internal/v4l2_videodevice.h |   2 +
 include/libcamera/stream.h                    |   3 +
 src/libcamera/v4l2_videodevice.cpp            | 112 ++++++++++++++++++
 3 files changed, 117 insertions(+)

diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
index 7938343b..5122d422 100644
--- a/include/libcamera/internal/v4l2_videodevice.h
+++ b/include/libcamera/internal/v4l2_videodevice.h
@@ -18,6 +18,7 @@
 
 #include <libcamera/buffer.h>
 #include <libcamera/class.h>
+#include <libcamera/color_space.h>
 #include <libcamera/geometry.h>
 #include <libcamera/pixel_format.h>
 #include <libcamera/signal.h>
@@ -162,6 +163,7 @@ public:
 
 	V4L2PixelFormat fourcc;
 	Size size;
+	ColorSpace colorSpace;
 
 	std::array<Plane, 3> planes;
 	unsigned int planesCount = 0;
diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
index bb47c390..f79c6987 100644
--- a/include/libcamera/stream.h
+++ b/include/libcamera/stream.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include <libcamera/buffer.h>
+#include <libcamera/color_space.h>
 #include <libcamera/geometry.h>
 #include <libcamera/pixel_format.h>
 
@@ -47,6 +48,8 @@ struct StreamConfiguration {
 
 	unsigned int bufferCount;
 
+	ColorSpace colorSpace;
+
 	Stream *stream() const { return stream_; }
 	void setStream(Stream *stream) { stream_ = stream; }
 	const StreamFormats &formats() const { return formats_; }
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 12c09dc7..d606f81d 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -730,6 +730,114 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format)
 		return getFormatSingleplane(format);
 }
 
+static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = {
+	{ ColorSpace::RAW, V4L2_COLORSPACE_RAW },
+	{ ColorSpace::JFIF, V4L2_COLORSPACE_JPEG },
+	{ ColorSpace::SMPTE170M, V4L2_COLORSPACE_SMPTE170M },
+	{ ColorSpace::REC709, V4L2_COLORSPACE_REC709 },
+	{ ColorSpace::REC2020, V4L2_COLORSPACE_BT2020 },
+};
+
+static const std::map<ColorSpace::Encoding, v4l2_ycbcr_encoding> encodingToV4l2 = {
+	{ ColorSpace::Encoding::REC601, V4L2_YCBCR_ENC_601 },
+	{ ColorSpace::Encoding::REC709, V4L2_YCBCR_ENC_709 },
+	{ ColorSpace::Encoding::REC2020, V4L2_YCBCR_ENC_BT2020 },
+};
+
+static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = {
+	{ ColorSpace::TransferFunction::IDENTITY, V4L2_XFER_FUNC_NONE },
+	{ ColorSpace::TransferFunction::SRGB, V4L2_XFER_FUNC_SRGB },
+	{ ColorSpace::TransferFunction::REC709, V4L2_XFER_FUNC_709 },
+};
+
+static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = {
+	{ ColorSpace::Range::FULL, V4L2_QUANTIZATION_FULL_RANGE },
+	{ ColorSpace::Range::LIMITED, V4L2_QUANTIZATION_LIM_RANGE },
+};
+
+template<typename T>
+static void setColorSpace(const ColorSpace &colorSpace, T &v4l2PixFormat)
+{
+	if (!colorSpace.isFullyDefined())
+		LOG(V4L2, Warning) << "Setting non-fully defined colour space"
+				   << colorSpace.toString();
+
+	v4l2PixFormat.colorspace = V4L2_COLORSPACE_DEFAULT;
+	v4l2PixFormat.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	v4l2PixFormat.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+	v4l2PixFormat.quantization = V4L2_QUANTIZATION_DEFAULT;
+
+	auto it_color = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(),
+				     [&colorSpace](const std::pair<ColorSpace, v4l2_colorspace> &item) { return colorSpace == item.first; });
+	if (it_color != colorSpaceToV4l2.end())
+		v4l2PixFormat.colorspace = it_color->second;
+
+	auto it_encoding = encodingToV4l2.find(colorSpace.encoding);
+	if (it_encoding != encodingToV4l2.end())
+		v4l2PixFormat.ycbcr_enc = it_encoding->second;
+
+	auto it_transfer = transferFunctionToV4l2.find(colorSpace.transferFunction);
+	if (it_transfer != transferFunctionToV4l2.end())
+		v4l2PixFormat.xfer_func = it_transfer->second;
+
+	auto it_range = rangeToV4l2.find(colorSpace.range);
+	if (it_range != rangeToV4l2.end())
+		v4l2PixFormat.quantization = it_range->second;
+}
+
+static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = {
+	{ V4L2_COLORSPACE_RAW, ColorSpace::RAW },
+	{ V4L2_COLORSPACE_JPEG, ColorSpace::JFIF },
+	{ V4L2_COLORSPACE_SRGB, ColorSpace::JFIF },
+	{ V4L2_COLORSPACE_SMPTE170M, ColorSpace::SMPTE170M },
+	{ V4L2_COLORSPACE_REC709, ColorSpace::REC709 },
+	{ V4L2_COLORSPACE_BT2020, ColorSpace::REC2020 },
+};
+
+static const std::map<uint32_t, ColorSpace::Encoding> v4l2ToEncoding = {
+	{ V4L2_YCBCR_ENC_601, ColorSpace::Encoding::REC601 },
+	{ V4L2_YCBCR_ENC_709, ColorSpace::Encoding::REC709 },
+	{ V4L2_YCBCR_ENC_BT2020, ColorSpace::Encoding::REC2020 },
+};
+
+static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = {
+	{ V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::IDENTITY },
+	{ V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::SRGB },
+	{ V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::REC709 },
+};
+
+static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = {
+	{ V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::FULL },
+	{ V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::LIMITED },
+};
+
+template<typename T>
+ColorSpace getColorSpace(const T &v4l2PixFormat)
+{
+	ColorSpace colorSpace;
+
+	auto it_color = v4l2ToColorSpace.find(v4l2PixFormat.colorspace);
+	if (it_color != v4l2ToColorSpace.end())
+		colorSpace = it_color->second;
+
+	auto it_encoding = v4l2ToEncoding.find(v4l2PixFormat.ycbcr_enc);
+	if (it_encoding != v4l2ToEncoding.end())
+		colorSpace.encoding = it_encoding->second;
+
+	auto it_transfer = v4l2ToTransferFunction.find(v4l2PixFormat.xfer_func);
+	if (it_transfer != v4l2ToTransferFunction.end())
+		colorSpace.transferFunction = it_transfer->second;
+
+	auto it_range = v4l2ToRange.find(v4l2PixFormat.quantization);
+	if (it_range != v4l2ToRange.end())
+		colorSpace.range = it_range->second;
+
+	if (!colorSpace.isFullyDefined())
+		LOG(V4L2, Warning) << "Returning non-fully defined colour space"
+				   << colorSpace.toString();
+	return colorSpace;
+}
+
 /**
  * \brief Try an image format on the V4L2 video device
  * \param[inout] format The image format to test applicability to the video device
@@ -839,6 +947,7 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)
 	format->size.width = pix->width;
 	format->size.height = pix->height;
 	format->fourcc = V4L2PixelFormat(pix->pixelformat);
+	format->colorSpace = getColorSpace(*pix);
 	format->planesCount = pix->num_planes;
 
 	for (unsigned int i = 0; i < format->planesCount; ++i) {
@@ -861,6 +970,7 @@ int V4L2VideoDevice::trySetFormatMultiplane(V4L2DeviceFormat *format, bool set)
 	pix->pixelformat = format->fourcc;
 	pix->num_planes = format->planesCount;
 	pix->field = V4L2_FIELD_NONE;
+	setColorSpace(format->colorSpace, *pix);
 
 	ASSERT(pix->num_planes <= std::size(pix->plane_fmt));
 
@@ -909,6 +1019,7 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)
 	format->size.width = pix->width;
 	format->size.height = pix->height;
 	format->fourcc = V4L2PixelFormat(pix->pixelformat);
+	format->colorSpace = getColorSpace(*pix);
 	format->planesCount = 1;
 	format->planes[0].bpl = pix->bytesperline;
 	format->planes[0].size = pix->sizeimage;
@@ -928,6 +1039,7 @@ int V4L2VideoDevice::trySetFormatSingleplane(V4L2DeviceFormat *format, bool set)
 	pix->pixelformat = format->fourcc;
 	pix->bytesperline = format->planes[0].bpl;
 	pix->field = V4L2_FIELD_NONE;
+	setColorSpace(format->colorSpace, *pix);
 	ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format);
 	if (ret) {
 		LOG(V4L2, Error)
-- 
2.20.1



More information about the libcamera-devel mailing list