[{"id":21465,"web_url":"https://patchwork.libcamera.org/comment/21465/","msgid":"<20211130211105.v6yx2crkwea5us6u@uno.localdomain>","date":"2021-11-30T21:11:05","subject":"Re: [libcamera-devel] [PATCH v7 3/7] libcamera: Convert between\n\tColorSpace class and V4L2 formats","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi David\n\nOn Fri, Nov 26, 2021 at 10:40:41AM +0000, David Plowman wrote:\n> Add functions to the V4L2Device class to convert to and from\n> libcamera ColorSpace.\n>\n> These methods 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 methods 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> ---\n>  include/libcamera/internal/v4l2_device.h |   7 +\n>  src/libcamera/v4l2_device.cpp            | 192 +++++++++++++++++++++++\n>  2 files changed, 199 insertions(+)\n>\n> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h\n> index 7816a290..7f9b0f42 100644\n> --- a/include/libcamera/internal/v4l2_device.h\n> +++ b/include/libcamera/internal/v4l2_device.h\n> @@ -17,6 +17,7 @@\n>  #include <libcamera/base/signal.h>\n>  #include <libcamera/base/span.h>\n>\n> +#include <libcamera/color_space.h>\n>  #include <libcamera/controls.h>\n>\n>  namespace libcamera {\n> @@ -44,6 +45,12 @@ public:\n>\n>  \tvoid updateControlInfo();\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\nThese can be protected\n\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n  j\n\n>  protected:\n>  \tV4L2Device(const std::string &deviceNode);\n>  \t~V4L2Device();\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index 9c783c9c..df92fa9e 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -16,6 +16,8 @@\n>  #include <sys/syscall.h>\n>  #include <unistd.h>\n>\n> +#include <linux/v4l2-mediabus.h>\n> +\n>  #include <libcamera/base/event_notifier.h>\n>  #include <libcamera/base/log.h>\n>  #include <libcamera/base/utils.h>\n> @@ -731,4 +733,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::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::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::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::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::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::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> +\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 */\n> --\n> 2.30.2\n>","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 AD7C9BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 30 Nov 2021 21:10:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B85A060723;\n\tTue, 30 Nov 2021 22:10:17 +0100 (CET)","from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net\n\t[217.70.183.193])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DCE45605C4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Nov 2021 22:10:15 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 78A9E240002;\n\tTue, 30 Nov 2021 21:10:13 +0000 (UTC)"],"Date":"Tue, 30 Nov 2021 22:11:05 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"David Plowman <david.plowman@raspberrypi.com>","Message-ID":"<20211130211105.v6yx2crkwea5us6u@uno.localdomain>","References":"<20211126104045.4756-1-david.plowman@raspberrypi.com>\n\t<20211126104045.4756-4-david.plowman@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211126104045.4756-4-david.plowman@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v7 3/7] libcamera: Convert between\n\tColorSpace 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":21515,"web_url":"https://patchwork.libcamera.org/comment/21515/","msgid":"<CAHW6GY+fuj3RuWaVk8ouSKFd+LkkjHF19Skw+d=bWNOsuSyYXg@mail.gmail.com>","date":"2021-12-01T11:50:50","subject":"Re: [libcamera-devel] [PATCH v7 3/7] libcamera: Convert between\n\tColorSpace 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 Jacopo\n\nThanks for the review!\n\nOn Tue, 30 Nov 2021 at 21:10, Jacopo Mondi <jacopo@jmondi.org> wrote:\n>\n> Hi David\n>\n> On Fri, Nov 26, 2021 at 10:40:41AM +0000, David Plowman wrote:\n> > Add functions to the V4L2Device class to convert to and from\n> > libcamera ColorSpace.\n> >\n> > These methods 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 methods 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> > ---\n> >  include/libcamera/internal/v4l2_device.h |   7 +\n> >  src/libcamera/v4l2_device.cpp            | 192 +++++++++++++++++++++++\n> >  2 files changed, 199 insertions(+)\n> >\n> > diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h\n> > index 7816a290..7f9b0f42 100644\n> > --- a/include/libcamera/internal/v4l2_device.h\n> > +++ b/include/libcamera/internal/v4l2_device.h\n> > @@ -17,6 +17,7 @@\n> >  #include <libcamera/base/signal.h>\n> >  #include <libcamera/base/span.h>\n> >\n> > +#include <libcamera/color_space.h>\n> >  #include <libcamera/controls.h>\n> >\n> >  namespace libcamera {\n> > @@ -44,6 +45,12 @@ public:\n> >\n> >       void updateControlInfo();\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>\n> These can be protected\n\nSo the only reason I didn't do this was that I could envisage pipeline\nhandlers wanting to check if their chosen colour space is known to\nV4L2. I know that can't happen at the moment, but there is a possible\nfuture with new or custom colour spaces... But that's all a bit\nhypothetical, so maybe it's better to make them protected now and\nchange it back later if we ever want to?\n\nBut I don't feel strongly - happy to change this if folks prefer it that way!\n\nThanks\nDavid\n\n>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n>\n> Thanks\n>   j\n>\n> >  protected:\n> >       V4L2Device(const std::string &deviceNode);\n> >       ~V4L2Device();\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > index 9c783c9c..df92fa9e 100644\n> > --- a/src/libcamera/v4l2_device.cpp\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -16,6 +16,8 @@\n> >  #include <sys/syscall.h>\n> >  #include <unistd.h>\n> >\n> > +#include <linux/v4l2-mediabus.h>\n> > +\n> >  #include <libcamera/base/event_notifier.h>\n> >  #include <libcamera/base/log.h>\n> >  #include <libcamera/base/utils.h>\n> > @@ -731,4 +733,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::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::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::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::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::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::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> > +     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> > 2.30.2\n> >","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 48052BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  1 Dec 2021 11:51:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A48E26071A;\n\tWed,  1 Dec 2021 12:51:03 +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 AF2466011A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  1 Dec 2021 12:51:01 +0100 (CET)","by mail-wr1-x433.google.com with SMTP id o13so51485255wrs.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 01 Dec 2021 03:51:01 -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=\"h/oggjTt\"; 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=wdm8Yko28y/tiWAa5+W1zzWvUhr1IrEDkZcAlT9DxBE=;\n\tb=h/oggjTtdCGJv+gpAx91/GTi1B5hAKEYjqgQGMLsoETuuS+vN+8lw89R0U930qE9Bh\n\t92hvnODUAkc7B/JHlxi7VvS0xSe4r/AxQjQa0zYwJPql9+zJBaYVqmt9dUf42odfTEZh\n\tbiT15+wSNNMPHcA53Dt8cbdOssHbQzlASyxr5Nzhw4tob1Ck59nhy1Zv7MjIRSto8hmF\n\tnCgLMXwBipzmTISDhH+U1M1q6UX4v55veIgwbx8/jhPkWpPGUghep/rHOTM19SrB1zag\n\tsCvJEv0UvhSQJZ/MxwR+tzcPBaINlrck+qXPaFBsxVyb7KdnJ4tCRng6MYg/jxAabgyf\n\tmHFw==","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=wdm8Yko28y/tiWAa5+W1zzWvUhr1IrEDkZcAlT9DxBE=;\n\tb=24hVfjJOmhbrQB+7D/L0/1GJ8qsXa+X7YLj9FUhvAsAQ7FeZhkJ/Jwbto8241+040l\n\t4EvZeFd1VacaWaeTIDBivCPB2s6aElA5aGVTFriCFFHFJBg8MJYUgoKaqej5ojIp/Eyf\n\tV5oimMCBQHdB7/NwtFIBqbgHodinFGqWNClelzuH5aFpsvb6QCFVwn5XeQVQYZjZelho\n\toTLUQux5mUBbMvJul+dLbjVIX1oFXmbwPoEcQxpqjxvLy0DvPvujwl3N/Q2R3TIaPVf5\n\tsC1A8qerkvdPqKXfNnHXmhz9rXblT1Bvfah4gg2QXvOBfFnHEH+c4Za3nYsUdETp2h98\n\tRfTA==","X-Gm-Message-State":"AOAM530C2HYLmTBY0isXcH7AqzeindMGFTUIXu91GBhIf/0Dbtd5lMQj\n\t2YACEnEvw0eN4niz0Ha+GW0rt4ffUCUxTHzmE5ITpw==","X-Google-Smtp-Source":"ABdhPJzoxRwvIcoVSUDs8jYtYN5ryTfbT/lUe9HziGNJx9vfP1/Ul3z1r9itW3IKhvPvThfDXJed0FGkC7gsHvlVCbg=","X-Received":"by 2002:adf:f44e:: with SMTP id f14mr6268219wrp.37.1638359461265;\n\tWed, 01 Dec 2021 03:51:01 -0800 (PST)","MIME-Version":"1.0","References":"<20211126104045.4756-1-david.plowman@raspberrypi.com>\n\t<20211126104045.4756-4-david.plowman@raspberrypi.com>\n\t<20211130211105.v6yx2crkwea5us6u@uno.localdomain>","In-Reply-To":"<20211130211105.v6yx2crkwea5us6u@uno.localdomain>","From":"David Plowman <david.plowman@raspberrypi.com>","Date":"Wed, 1 Dec 2021 11:50:50 +0000","Message-ID":"<CAHW6GY+fuj3RuWaVk8ouSKFd+LkkjHF19Skw+d=bWNOsuSyYXg@mail.gmail.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH v7 3/7] libcamera: Convert between\n\tColorSpace 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>"}}]