[libcamera-devel,RFC,2/3] libcamera: Support passing ColorSpaces to V4L2 drivers
diff mbox series

Message ID 20210608144413.1529-3-david.plowman@raspberrypi.com
State Superseded
Headers show
Series
  • Colour spaces
Related show

Commit Message

David Plowman June 8, 2021, 2:44 p.m. UTC
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(+)

Comments

Naushir Patuck June 14, 2021, 10:56 a.m. UTC | #1
Hi David.

Thank you for your patch.

On Tue, 8 Jun 2021 at 15:44, David Plowman <david.plowman@raspberrypi.com>
wrote:

> 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)
>

Could you expand on why this (and below) needs to be templated?


> +{
> +       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; });
>

Could you use a map for colorSpaceToV4l2 like you do for the others?

Regards,
Naush


> +       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
>
>

Patch
diff mbox series

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)