[{"id":20686,"web_url":"https://patchwork.libcamera.org/comment/20686/","msgid":"<163611432442.275423.14818616881347865796@Monstersaurus>","date":"2021-11-05T12:12:04","subject":"Re: [libcamera-devel] [PATCH v5 3/7] libcamera: Convert between\n\tColorSpace class and V4L2 formats","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi David,\n\nQuoting David Plowman (2021-11-04 13:58:01)\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.\n> \n> Note that we must explicitly instantiate the templates that will be\n> needed.\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            | 171 +++++++++++++++++++++++\n>  2 files changed, 178 insertions(+)\n> \n> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h\n> index f21bc370..24fc4984 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 ColorSpace toColorSpace(const T &v4l2Format);\n> +\n> +       template<typename T>\n> +       static int fromColorSpace(const ColorSpace &colorSpace, T &v4l2Format);\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..34ec2bf3 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,173 @@ 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::Jpeg },\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::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 those are marked as undefined in\n> + * the ColorSpace, but other fields are still initialised where possible.\n> + * This situation can be detected using the returned value's\n> + * ColorSpace::isFullyDefined() method.\n> + *\n> + * \\return The ColorSpace corresponding to the input V4L2 format\n> + */\n> +template<typename T>\n> +ColorSpace V4L2Device::toColorSpace(const T &v4l2Format)\n> +{\n> +       ColorSpace colorSpace;\n> +\n> +       auto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace);\n> +       if (itColor != v4l2ToColorSpace.end())\n> +               colorSpace = itColor->second;\n> +\n> +       auto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc);\n> +       if (itYcbcrEncoding != v4l2ToYcbcrEncoding.end())\n> +               colorSpace.ycbcrEncoding = itYcbcrEncoding->second;\n> +\n> +       auto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func);\n> +       if (itTransfer != v4l2ToTransferFunction.end())\n> +               colorSpace.transferFunction = itTransfer->second;\n> +\n> +       auto itRange = v4l2ToRange.find(v4l2Format.quantization);\n> +       if (itRange != v4l2ToRange.end())\n> +               colorSpace.range = itRange->second;\n> +\n> +       return colorSpace;\n> +}\n> +\n> +template ColorSpace V4L2Device::toColorSpace(const struct v4l2_pix_format &);\n> +template ColorSpace V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &);\n> +template 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> + * \\return 0 on success or a negative error code otherwise\n\nWe normally consider a 'negative error code' to be an errno...\n\n> + */\n> +template<typename T>\n> +int V4L2Device::fromColorSpace(const ColorSpace &colorSpace, T &v4l2Format)\n> +{\n> +       int ret = 0;\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> +       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> +               return ret;\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> +       auto itPrimaries = primariesToV4l2.find(colorSpace.primaries);\n> +       if (itPrimaries != primariesToV4l2.end())\n> +               v4l2Format.colorspace = itPrimaries->second;\n> +       else\n> +               ret = -1;\n\nSo this now represents\n  EPERM 1 Operation not permitted\n\nIs there something more suited to use?\nPeraps just -EINVAL?\n\nSame for the others below of course.\n\n> +\n> +       auto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace.ycbcrEncoding);\n> +       if (itYcbcrEncoding != ycbcrEncodingToV4l2.end())\n> +               v4l2Format.ycbcr_enc = itYcbcrEncoding->second;\n> +       else\n> +               ret = -1;\n> +\n> +       auto itTransfer = transferFunctionToV4l2.find(colorSpace.transferFunction);\n> +       if (itTransfer != transferFunctionToV4l2.end())\n> +               v4l2Format.xfer_func = itTransfer->second;\n> +       else\n> +               ret = -1;\n> +\n> +       auto itRange = rangeToV4l2.find(colorSpace.range);\n> +       if (itRange != rangeToV4l2.end())\n> +               v4l2Format.quantization = itRange->second;\n> +       else\n> +               ret = -1;\n> +\n> +       return ret;\n> +}\n> +\n> +template int V4L2Device::fromColorSpace(const ColorSpace &, struct v4l2_pix_format &);\n> +template int V4L2Device::fromColorSpace(const ColorSpace &, struct v4l2_pix_format_mplane &);\n> +template int V4L2Device::fromColorSpace(const ColorSpace &, struct v4l2_mbus_framefmt &);\n> +\n>  } /* namespace libcamera */\n> -- \n> 2.20.1\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 C95FFBDB1C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  5 Nov 2021 12:12:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 305A16032C;\n\tFri,  5 Nov 2021 13:12:09 +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 AE4F4600B8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  5 Nov 2021 13:12:07 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3C3B7E7;\n\tFri,  5 Nov 2021 13:12:07 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"jwpTflcd\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1636114327;\n\tbh=QqcvTvAWFAnmohaqybRjRpsPQBgoAfiXbZdq+dM7O54=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=jwpTflcdRQp7mafno/q54ahubsD/p58Zgpehu8fcDmxf0Da33VVGPbUSnnrynQICt\n\ty2jm4k8dHN1lVCIi0jzZHq2ivusDaR3AIAmHEWmdPyU1NW6TR8wNVrB5QpJv6ricMi\n\tY+TY/sxiQePRCw7MnLEgh8g+q3FoMAfs4Ih5vmV4=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20211104135805.5269-4-david.plowman@raspberrypi.com>","References":"<20211104135805.5269-1-david.plowman@raspberrypi.com>\n\t<20211104135805.5269-4-david.plowman@raspberrypi.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"David Plowman <david.plowman@raspberrypi.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Fri, 05 Nov 2021 12:12:04 +0000","Message-ID":"<163611432442.275423.14818616881347865796@Monstersaurus>","User-Agent":"alot/0.9.1","Subject":"Re: [libcamera-devel] [PATCH v5 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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]