[{"id":21744,"web_url":"https://patchwork.libcamera.org/comment/21744/","msgid":"<YbNMS7WjmOL8aUgN@pendragon.ideasonboard.com>","date":"2021-12-10T12:47:07","subject":"Re: [libcamera-devel] [PATCH v11 3/8] libcamera: video_device:\n\tConvert between ColorSpace class and V4L2 formats","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi David,\n\nThank you for the patch.\n\nOn Fri, Dec 10, 2021 at 11:21:37AM +0000, David Plowman wrote:\n> Add functions to the V4L2Device class to convert to and from\n> libcamera ColorSpace.\n> \n> These functions are added to the base V4L2Device class so that they can\n> be shared both by the video device class and subdevices.\n> \n> With the ColorSpace class, the color space and related other fields\n> are stored together, corresponding to a number of fields in the\n> various different V4L2 format structures. Template functions are\n> therefore a convenient implementation, and we must explicitly\n> instantiate the templates that will be needed.\n> \n> Note that unset color spaces are converted to requests for the\n> device's \"default\" color space.\n> \n> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/v4l2_device.h |   8 +\n>  src/libcamera/v4l2_device.cpp            | 194 +++++++++++++++++++++++\n>  2 files changed, 202 insertions(+)\n> \n> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h\n> index 8886b750..a52a5f2c 100644\n> --- a/include/libcamera/internal/v4l2_device.h\n> +++ b/include/libcamera/internal/v4l2_device.h\n> @@ -9,6 +9,7 @@\n>  \n>  #include <map>\n>  #include <memory>\n> +#include <optional>\n>  #include <vector>\n>  \n>  #include <linux/videodev2.h>\n> @@ -18,6 +19,7 @@\n>  #include <libcamera/base/span.h>\n>  #include <libcamera/base/unique_fd.h>\n>  \n> +#include <libcamera/color_space.h>\n>  #include <libcamera/controls.h>\n>  \n>  namespace libcamera {\n> @@ -56,6 +58,12 @@ protected:\n>  \n>  \tint fd() const { return fd_.get(); }\n>  \n> +\ttemplate<typename T>\n> +\tstatic std::optional<ColorSpace> toColorSpace(const T &v4l2Format);\n> +\n> +\ttemplate<typename T>\n> +\tstatic int fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format);\n> +\n>  private:\n>  \tstatic ControlType v4l2CtrlType(uint32_t ctrlType);\n>  \tstatic std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl);\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index 39f36009..3e8a180f 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -10,11 +10,15 @@\n>  #include <fcntl.h>\n>  #include <iomanip>\n>  #include <limits.h>\n> +#include <map>\n>  #include <stdlib.h>\n>  #include <string.h>\n>  #include <sys/ioctl.h>\n>  #include <sys/syscall.h>\n>  #include <unistd.h>\n> +#include <vector>\n> +\n> +#include <linux/v4l2-mediabus.h>\n>  \n>  #include <libcamera/base/event_notifier.h>\n>  #include <libcamera/base/log.h>\n> @@ -728,4 +732,194 @@ void V4L2Device::eventAvailable()\n>  \tframeStart.emit(event.u.frame_sync.frame_sequence);\n>  }\n>  \n> +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = {\n> +\t{ V4L2_COLORSPACE_RAW, ColorSpace::Raw },\n> +\t{ V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg },\n> +\t{ V4L2_COLORSPACE_SRGB, ColorSpace::Srgb },\n> +\t{ V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m },\n> +\t{ V4L2_COLORSPACE_REC709, ColorSpace::Rec709 },\n> +\t{ V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 },\n> +};\n> +\n> +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = {\n> +\t{ V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear },\n> +\t{ V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb },\n> +\t{ V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 },\n> +};\n> +\n> +static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncoding = {\n> +\t{ V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 },\n> +\t{ V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 },\n> +\t{ V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 },\n> +};\n> +\n> +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = {\n> +\t{ V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full },\n> +\t{ V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited },\n> +};\n> +\n> +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = {\n> +\t{ ColorSpace::Raw, V4L2_COLORSPACE_RAW },\n> +\t{ ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG },\n> +\t{ ColorSpace::Srgb, V4L2_COLORSPACE_SRGB },\n> +\t{ ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M },\n> +\t{ ColorSpace::Rec709, V4L2_COLORSPACE_REC709 },\n> +\t{ ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 },\n> +};\n> +\n> +static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2 = {\n> +\t{ ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW },\n> +\t{ ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M },\n> +\t{ ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 },\n> +\t{ ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 },\n> +};\n> +\n> +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = {\n> +\t{ ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE },\n> +\t{ ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB },\n> +\t{ ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 },\n> +};\n> +\n> +static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2 = {\n> +\t{ ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 },\n> +\t{ ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 },\n> +\t{ ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 },\n> +};\n> +\n> +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = {\n> +\t{ ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE },\n> +\t{ ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE },\n> +};\n> +\n> +/**\n> + * \\brief Convert the color space fields in a V4L2 format to a ColorSpace\n> + * \\param[in] v4l2Format A V4L2 format containing color space information\n> + *\n> + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a\n> + * V4L2 format structure are converted to a corresponding ColorSpace.\n> + *\n> + * If any V4L2 fields are not recognised then we return an \"unset\"\n> + * color space.\n> + *\n> + * \\return The ColorSpace corresponding to the input V4L2 format\n> + * \\retval std::nullopt One or more V4L2 color space fields were not recognised\n> + */\n> +template<typename T>\n> +std::optional<ColorSpace> V4L2Device::toColorSpace(const T &v4l2Format)\n> +{\n> +\tauto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace);\n> +\tif (itColor == v4l2ToColorSpace.end())\n> +\t\treturn std::nullopt;\n> +\n> +\t/* This sets all the color space fields to the correct \"default\" values. */\n> +\tColorSpace colorSpace = itColor->second;\n> +\n> +\tif (v4l2Format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) {\n> +\t\tauto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc);\n> +\t\tif (itYcbcrEncoding == v4l2ToYcbcrEncoding.end())\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\tcolorSpace.ycbcrEncoding = itYcbcrEncoding->second;\n> +\t}\n> +\n> +\tif (v4l2Format.xfer_func != V4L2_XFER_FUNC_DEFAULT) {\n> +\t\tauto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func);\n> +\t\tif (itTransfer == v4l2ToTransferFunction.end())\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\tcolorSpace.transferFunction = itTransfer->second;\n> +\t}\n\nThere's also a comment in v10 which hasn't been taken into account (or\nexplicitly rejected).\n\n> +\n> +\tif (v4l2Format.quantization != V4L2_QUANTIZATION_DEFAULT) {\n> +\t\tauto itRange = v4l2ToRange.find(v4l2Format.quantization);\n> +\t\tif (itRange == v4l2ToRange.end())\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\tcolorSpace.range = itRange->second;\n> +\t}\n> +\n> +\treturn colorSpace;\n> +}\n> +\n> +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format &);\n> +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &);\n> +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_mbus_framefmt &);\n> +\n> +/**\n> + * \\brief Fill in the color space fields of a V4L2 format from a ColorSpace\n> + * \\param[in] colorSpace The ColorSpace to be converted\n> + * \\param[out] v4l2Format A V4L2 format containing color space information\n> + *\n> + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a\n> + * V4L2 format structure are filled in from a corresponding ColorSpace.\n> + *\n> + * An error is returned if any of the V4L2 fields do not support the\n> + * value given in the ColorSpace. Such fields are set to the V4L2\n> + * \"default\" values, but all other fields are still filled in where\n> + * possible.\n> + *\n> + * If the color space is completely unset, \"default\" V4L2 values are used\n> + * everywhere, so a driver would then choose its preferred color space.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n> + * \\retval -EINVAL The ColorSpace does not have a representation using V4L2 enums\n> + */\n> +template<typename T>\n> +int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format)\n> +{\n> +\tv4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT;\n> +\tv4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;\n> +\tv4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT;\n> +\tv4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT;\n> +\n> +\tif (!colorSpace)\n> +\t\treturn 0;\n> +\n> +\tauto itColor = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(),\n> +\t\t\t\t    [&colorSpace](const auto &item) {\n> +\t\t\t\t\t    return colorSpace == item.first;\n> +\t\t\t\t    });\n> +\tif (itColor != colorSpaceToV4l2.end()) {\n> +\t\tv4l2Format.colorspace = itColor->second;\n> +\t\t/* Leaving all the other fields as \"default\" should be fine. */\n> +\t\treturn 0;\n> +\t}\n> +\n> +\t/*\n> +\t * If the colorSpace doesn't precisely match a standard color space,\n> +\t * then we must choose a V4L2 colorspace with matching primaries.\n> +\t */\n> +\tint ret = 0;\n> +\n> +\tauto itPrimaries = primariesToV4l2.find(colorSpace->primaries);\n> +\tif (itPrimaries != primariesToV4l2.end())\n> +\t\tv4l2Format.colorspace = itPrimaries->second;\n> +\telse\n> +\t\tret = -EINVAL;\n> +\n> +\tauto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace->ycbcrEncoding);\n> +\tif (itYcbcrEncoding != ycbcrEncodingToV4l2.end())\n> +\t\tv4l2Format.ycbcr_enc = itYcbcrEncoding->second;\n> +\telse\n> +\t\tret = -EINVAL;\n> +\n> +\tauto itTransfer = transferFunctionToV4l2.find(colorSpace->transferFunction);\n> +\tif (itTransfer != transferFunctionToV4l2.end())\n> +\t\tv4l2Format.xfer_func = itTransfer->second;\n> +\telse\n> +\t\tret = -EINVAL;\n> +\n> +\tauto itRange = rangeToV4l2.find(colorSpace->range);\n> +\tif (itRange != rangeToV4l2.end())\n> +\t\tv4l2Format.quantization = itRange->second;\n> +\telse\n> +\t\tret = -EINVAL;\n> +\n> +\treturn ret;\n> +}\n> +\n> +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format &);\n> +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format_mplane &);\n> +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_mbus_framefmt &);\n> +\n>  } /* namespace libcamera */","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A4C21BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 10 Dec 2021 12:47:39 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DB5E060890;\n\tFri, 10 Dec 2021 13:47:38 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 21E6660868\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 10 Dec 2021 13:47:37 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7C869F84;\n\tFri, 10 Dec 2021 13:47:36 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"s4R6z8BN\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1639140456;\n\tbh=g4A7qELAL6qwnmIoIFi7Cufcbn+l/gQN0IS2FZAg/tc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=s4R6z8BNZb5EYouK03ls0VXbatu0lwd1QKE0AM92o0Hh9xdKiETzbKMK6Qms8ukOk\n\tpJfARuvLDwXQDepvy1m6D5tUzAXhjRONlD7oDVRjRmc1+zyi+dwilZKISbLNdKEJML\n\tqip9mK8KI2Nyz1kARoOO5UbDMLrVODc5uEm1a26E=","Date":"Fri, 10 Dec 2021 14:47:07 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"David Plowman <david.plowman@raspberrypi.com>","Message-ID":"<YbNMS7WjmOL8aUgN@pendragon.ideasonboard.com>","References":"<20211210112142.18441-1-david.plowman@raspberrypi.com>\n\t<20211210112142.18441-4-david.plowman@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211210112142.18441-4-david.plowman@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v11 3/8] libcamera: video_device:\n\tConvert between ColorSpace class and V4L2 formats","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"Tomasz Figa <tfiga@google.com>, libcamera-devel@lists.libcamera.org,\n\tHans Verkuil <hverkuil-cisco@xs4all.nl>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":21748,"web_url":"https://patchwork.libcamera.org/comment/21748/","msgid":"<CAHW6GYLftJVZSfo2SFGksLqeY6COE39vGO1hpaq9ZoV=2PXg-g@mail.gmail.com>","date":"2021-12-10T12:56:05","subject":"Re: [libcamera-devel] [PATCH v11 3/8] libcamera: video_device:\n\tConvert between ColorSpace class and V4L2 formats","submitter":{"id":42,"url":"https://patchwork.libcamera.org/api/people/42/","name":"David Plowman","email":"david.plowman@raspberrypi.com"},"content":"Hi Laurent\n\nYes, I seem to have missed a couple, but I am intending to add\neverything. v12 arriving shortly...\n\nThanks\nDavid\n\nOn Fri, 10 Dec 2021 at 12:47, Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi David,\n>\n> Thank you for the patch.\n>\n> On Fri, Dec 10, 2021 at 11:21:37AM +0000, David Plowman wrote:\n> > Add functions to the V4L2Device class to convert to and from\n> > libcamera ColorSpace.\n> >\n> > These functions are added to the base V4L2Device class so that they can\n> > be shared both by the video device class and subdevices.\n> >\n> > With the ColorSpace class, the color space and related other fields\n> > are stored together, corresponding to a number of fields in the\n> > various different V4L2 format structures. Template functions are\n> > therefore a convenient implementation, and we must explicitly\n> > instantiate the templates that will be needed.\n> >\n> > Note that unset color spaces are converted to requests for the\n> > device's \"default\" color space.\n> >\n> > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/v4l2_device.h |   8 +\n> >  src/libcamera/v4l2_device.cpp            | 194 +++++++++++++++++++++++\n> >  2 files changed, 202 insertions(+)\n> >\n> > diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h\n> > index 8886b750..a52a5f2c 100644\n> > --- a/include/libcamera/internal/v4l2_device.h\n> > +++ b/include/libcamera/internal/v4l2_device.h\n> > @@ -9,6 +9,7 @@\n> >\n> >  #include <map>\n> >  #include <memory>\n> > +#include <optional>\n> >  #include <vector>\n> >\n> >  #include <linux/videodev2.h>\n> > @@ -18,6 +19,7 @@\n> >  #include <libcamera/base/span.h>\n> >  #include <libcamera/base/unique_fd.h>\n> >\n> > +#include <libcamera/color_space.h>\n> >  #include <libcamera/controls.h>\n> >\n> >  namespace libcamera {\n> > @@ -56,6 +58,12 @@ protected:\n> >\n> >       int fd() const { return fd_.get(); }\n> >\n> > +     template<typename T>\n> > +     static std::optional<ColorSpace> toColorSpace(const T &v4l2Format);\n> > +\n> > +     template<typename T>\n> > +     static int fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format);\n> > +\n> >  private:\n> >       static ControlType v4l2CtrlType(uint32_t ctrlType);\n> >       static std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl);\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > index 39f36009..3e8a180f 100644\n> > --- a/src/libcamera/v4l2_device.cpp\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -10,11 +10,15 @@\n> >  #include <fcntl.h>\n> >  #include <iomanip>\n> >  #include <limits.h>\n> > +#include <map>\n> >  #include <stdlib.h>\n> >  #include <string.h>\n> >  #include <sys/ioctl.h>\n> >  #include <sys/syscall.h>\n> >  #include <unistd.h>\n> > +#include <vector>\n> > +\n> > +#include <linux/v4l2-mediabus.h>\n> >\n> >  #include <libcamera/base/event_notifier.h>\n> >  #include <libcamera/base/log.h>\n> > @@ -728,4 +732,194 @@ void V4L2Device::eventAvailable()\n> >       frameStart.emit(event.u.frame_sync.frame_sequence);\n> >  }\n> >\n> > +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = {\n> > +     { V4L2_COLORSPACE_RAW, ColorSpace::Raw },\n> > +     { V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg },\n> > +     { V4L2_COLORSPACE_SRGB, ColorSpace::Srgb },\n> > +     { V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m },\n> > +     { V4L2_COLORSPACE_REC709, ColorSpace::Rec709 },\n> > +     { V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 },\n> > +};\n> > +\n> > +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = {\n> > +     { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear },\n> > +     { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb },\n> > +     { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 },\n> > +};\n> > +\n> > +static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncoding = {\n> > +     { V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 },\n> > +     { V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 },\n> > +     { V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 },\n> > +};\n> > +\n> > +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = {\n> > +     { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full },\n> > +     { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited },\n> > +};\n> > +\n> > +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = {\n> > +     { ColorSpace::Raw, V4L2_COLORSPACE_RAW },\n> > +     { ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG },\n> > +     { ColorSpace::Srgb, V4L2_COLORSPACE_SRGB },\n> > +     { ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M },\n> > +     { ColorSpace::Rec709, V4L2_COLORSPACE_REC709 },\n> > +     { ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 },\n> > +};\n> > +\n> > +static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2 = {\n> > +     { ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW },\n> > +     { ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M },\n> > +     { ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 },\n> > +     { ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 },\n> > +};\n> > +\n> > +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = {\n> > +     { ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE },\n> > +     { ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB },\n> > +     { ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 },\n> > +};\n> > +\n> > +static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2 = {\n> > +     { ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 },\n> > +     { ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 },\n> > +     { ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 },\n> > +};\n> > +\n> > +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = {\n> > +     { ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE },\n> > +     { ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE },\n> > +};\n> > +\n> > +/**\n> > + * \\brief Convert the color space fields in a V4L2 format to a ColorSpace\n> > + * \\param[in] v4l2Format A V4L2 format containing color space information\n> > + *\n> > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a\n> > + * V4L2 format structure are converted to a corresponding ColorSpace.\n> > + *\n> > + * If any V4L2 fields are not recognised then we return an \"unset\"\n> > + * color space.\n> > + *\n> > + * \\return The ColorSpace corresponding to the input V4L2 format\n> > + * \\retval std::nullopt One or more V4L2 color space fields were not recognised\n> > + */\n> > +template<typename T>\n> > +std::optional<ColorSpace> V4L2Device::toColorSpace(const T &v4l2Format)\n> > +{\n> > +     auto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace);\n> > +     if (itColor == v4l2ToColorSpace.end())\n> > +             return std::nullopt;\n> > +\n> > +     /* This sets all the color space fields to the correct \"default\" values. */\n> > +     ColorSpace colorSpace = itColor->second;\n> > +\n> > +     if (v4l2Format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) {\n> > +             auto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc);\n> > +             if (itYcbcrEncoding == v4l2ToYcbcrEncoding.end())\n> > +                     return std::nullopt;\n> > +\n> > +             colorSpace.ycbcrEncoding = itYcbcrEncoding->second;\n> > +     }\n> > +\n> > +     if (v4l2Format.xfer_func != V4L2_XFER_FUNC_DEFAULT) {\n> > +             auto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func);\n> > +             if (itTransfer == v4l2ToTransferFunction.end())\n> > +                     return std::nullopt;\n> > +\n> > +             colorSpace.transferFunction = itTransfer->second;\n> > +     }\n>\n> There's also a comment in v10 which hasn't been taken into account (or\n> explicitly rejected).\n>\n> > +\n> > +     if (v4l2Format.quantization != V4L2_QUANTIZATION_DEFAULT) {\n> > +             auto itRange = v4l2ToRange.find(v4l2Format.quantization);\n> > +             if (itRange == v4l2ToRange.end())\n> > +                     return std::nullopt;\n> > +\n> > +             colorSpace.range = itRange->second;\n> > +     }\n> > +\n> > +     return colorSpace;\n> > +}\n> > +\n> > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format &);\n> > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &);\n> > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_mbus_framefmt &);\n> > +\n> > +/**\n> > + * \\brief Fill in the color space fields of a V4L2 format from a ColorSpace\n> > + * \\param[in] colorSpace The ColorSpace to be converted\n> > + * \\param[out] v4l2Format A V4L2 format containing color space information\n> > + *\n> > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a\n> > + * V4L2 format structure are filled in from a corresponding ColorSpace.\n> > + *\n> > + * An error is returned if any of the V4L2 fields do not support the\n> > + * value given in the ColorSpace. Such fields are set to the V4L2\n> > + * \"default\" values, but all other fields are still filled in where\n> > + * possible.\n> > + *\n> > + * If the color space is completely unset, \"default\" V4L2 values are used\n> > + * everywhere, so a driver would then choose its preferred color space.\n> > + *\n> > + * \\return 0 on success or a negative error code otherwise\n> > + * \\retval -EINVAL The ColorSpace does not have a representation using V4L2 enums\n> > + */\n> > +template<typename T>\n> > +int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format)\n> > +{\n> > +     v4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT;\n> > +     v4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;\n> > +     v4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT;\n> > +     v4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT;\n> > +\n> > +     if (!colorSpace)\n> > +             return 0;\n> > +\n> > +     auto itColor = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(),\n> > +                                 [&colorSpace](const auto &item) {\n> > +                                         return colorSpace == item.first;\n> > +                                 });\n> > +     if (itColor != colorSpaceToV4l2.end()) {\n> > +             v4l2Format.colorspace = itColor->second;\n> > +             /* Leaving all the other fields as \"default\" should be fine. */\n> > +             return 0;\n> > +     }\n> > +\n> > +     /*\n> > +      * If the colorSpace doesn't precisely match a standard color space,\n> > +      * then we must choose a V4L2 colorspace with matching primaries.\n> > +      */\n> > +     int ret = 0;\n> > +\n> > +     auto itPrimaries = primariesToV4l2.find(colorSpace->primaries);\n> > +     if (itPrimaries != primariesToV4l2.end())\n> > +             v4l2Format.colorspace = itPrimaries->second;\n> > +     else\n> > +             ret = -EINVAL;\n> > +\n> > +     auto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace->ycbcrEncoding);\n> > +     if (itYcbcrEncoding != ycbcrEncodingToV4l2.end())\n> > +             v4l2Format.ycbcr_enc = itYcbcrEncoding->second;\n> > +     else\n> > +             ret = -EINVAL;\n> > +\n> > +     auto itTransfer = transferFunctionToV4l2.find(colorSpace->transferFunction);\n> > +     if (itTransfer != transferFunctionToV4l2.end())\n> > +             v4l2Format.xfer_func = itTransfer->second;\n> > +     else\n> > +             ret = -EINVAL;\n> > +\n> > +     auto itRange = rangeToV4l2.find(colorSpace->range);\n> > +     if (itRange != rangeToV4l2.end())\n> > +             v4l2Format.quantization = itRange->second;\n> > +     else\n> > +             ret = -EINVAL;\n> > +\n> > +     return ret;\n> > +}\n> > +\n> > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format &);\n> > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format_mplane &);\n> > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_mbus_framefmt &);\n> > +\n> >  } /* namespace libcamera */\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5B3A0BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 10 Dec 2021 12:56:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0F06A60897;\n\tFri, 10 Dec 2021 13:56:19 +0100 (CET)","from mail-wr1-x433.google.com (mail-wr1-x433.google.com\n\t[IPv6:2a00:1450:4864:20::433])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id ECDFA60868\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 10 Dec 2021 13:56:16 +0100 (CET)","by mail-wr1-x433.google.com with SMTP id a18so14783239wrn.6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 10 Dec 2021 04:56:16 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"q8EO2QyS\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=O+v8xQpF3+Atz/eu06LoTChYnP8bjtBQuoLbQEv0hLs=;\n\tb=q8EO2QyS5kDsDKxIKOkDgsIX6Znh67KYOmCVfZJGY+mwsRtWhvtL24pp8o7V/q9IUC\n\t8Zf4Qg4CUA7GqqChc3M4NOxyCqXtvelEIDZ+ZVyHm3Wy0SE7JZsa+J/wJs5353Qk/t63\n\tbf5L2mj5RcKD7xTlhGxl6QMxGce+o3u3ZYOKqRvPcmFMix/OuKHF9eU9O4xhzCRdJpXI\n\tnNvS6mSEPPcYW6qvAXqmdiszDbDS3hAKRPN+1ZnaZAPWHUELKRPFpT6/yYUZ7BG5R6jd\n\tChLoTA4p3EeqIDzV7Q65bcUFF2uqgjNwf8r2d+K6jeDHhwBXO/WVquZisHi9q5yasHSr\n\tYW/Q==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=O+v8xQpF3+Atz/eu06LoTChYnP8bjtBQuoLbQEv0hLs=;\n\tb=h+6j1y6C//6YSSd9qg7SVmGIcXFrmbxbPFWP81xAs9rTYxXdYshOCtOwvzW7GjoK4A\n\tCHXK3NGWPltKM/bG1lOLJzDxQV9GVgpnM5Mg3nmQaSGxR8zDAJgAR0Dhw/Q5snbd3CQF\n\tBM9y3lImlkkWNlZE4c0v41EE39eEZwVpQcRECB1VArAw1zsR/friPyrSNezbw0osHMD0\n\tJbMSRtrZFjOO8QMEeTFpaA2NWbIsTZ8V6nR535pfQdCmlBy3Hkl4QZnG+TLOnWnvPXLC\n\to0Dx0KJ1spA9l6veMUlKPxfkCEAsGm32Ci266vZ7UHdHyLRQyTMKPyL/pC814a1Pgnk+\n\tHybQ==","X-Gm-Message-State":"AOAM531bwfWXRbicUuAvt3tNlDpDg+yB/WVtoQhrN2aAunFvro72q8DU\n\tSRAqdP1aEXP/x5BxG9CIBXbecCIdLbCr3GYZNUsfSA==","X-Google-Smtp-Source":"ABdhPJylPTEFqW8+rsL76hDmcdNkaDLDBI8hA0iD7lMbwoBzM8n7S8FoSRKaQ4sV6q5TVYHco9hcFkAtFUUftX1LTe8=","X-Received":"by 2002:adf:f80c:: with SMTP id\n\ts12mr13741460wrp.627.1639140976537; \n\tFri, 10 Dec 2021 04:56:16 -0800 (PST)","MIME-Version":"1.0","References":"<20211210112142.18441-1-david.plowman@raspberrypi.com>\n\t<20211210112142.18441-4-david.plowman@raspberrypi.com>\n\t<YbNMS7WjmOL8aUgN@pendragon.ideasonboard.com>","In-Reply-To":"<YbNMS7WjmOL8aUgN@pendragon.ideasonboard.com>","From":"David Plowman <david.plowman@raspberrypi.com>","Date":"Fri, 10 Dec 2021 12:56:05 +0000","Message-ID":"<CAHW6GYLftJVZSfo2SFGksLqeY6COE39vGO1hpaq9ZoV=2PXg-g@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH v11 3/8] libcamera: video_device:\n\tConvert between ColorSpace class and V4L2 formats","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"Tomasz Figa <tfiga@google.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>,\n\tHans Verkuil <hverkuil-cisco@xs4all.nl>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]