<div dir="ltr"><div dir="ltr">Hi David,<div><br></div><div>Thank you for your work.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, 20 Oct 2021 at 12:08, David Plowman <<a href="mailto:david.plowman@raspberrypi.com">david.plowman@raspberrypi.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">These methods are added to the base V4L2Device class so that they can<br>
be shared both by the video device class and subdevices.<br>
<br>
With the ColorSpace class, the color space and related other fields<br>
are stored together, corresponding to a number of fields in the<br>
various different V4L2 format structures. Template methods are<br>
therefore a convenient implementation.<br>
<br>
Note that we must explicitly instantiate the templates that will be<br>
needed.<br>
<br>
Signed-off-by: David Plowman <<a href="mailto:david.plowman@raspberrypi.com" target="_blank">david.plowman@raspberrypi.com</a>><br>
---<br>
include/libcamera/internal/v4l2_device.h | 7 +<br>
src/libcamera/v4l2_device.cpp | 171 +++++++++++++++++++++++<br>
2 files changed, 178 insertions(+)<br>
<br>
diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h<br>
index f21bc370..77ea30f6 100644<br>
--- a/include/libcamera/internal/v4l2_device.h<br>
+++ b/include/libcamera/internal/v4l2_device.h<br>
@@ -17,6 +17,7 @@<br>
#include <libcamera/base/signal.h><br>
#include <libcamera/base/span.h><br>
<br>
+#include <libcamera/color_space.h><br>
#include <libcamera/controls.h><br>
<br>
namespace libcamera {<br>
@@ -44,6 +45,12 @@ public:<br>
<br>
void updateControlInfo();<br>
<br>
+ template<typename T><br>
+ static ColorSpace v4l2ToColorSpace(const T &v4l2Format);<br>
+<br>
+ template<typename T><br>
+ static int colorSpaceToV4l2(const ColorSpace &colorSpace, T &v4l2Format);<br></blockquote><div><br></div><div>For consistency with other classes, perhaps these should be renamed to</div><div>toColorSpace and fromColorSpace?</div><div><br></div><div>I wonder if this implementation should also live in the ColorSpace class? This would</div><div>sort of mirror what the various format classes do. But I have no strong preference</div><div>either w</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
protected:<br>
V4L2Device(const std::string &deviceNode);<br>
~V4L2Device();<br>
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp<br>
index 9c783c9c..771dc096 100644<br>
--- a/src/libcamera/v4l2_device.cpp<br>
+++ b/src/libcamera/v4l2_device.cpp<br>
@@ -16,6 +16,8 @@<br>
#include <sys/syscall.h><br>
#include <unistd.h><br>
<br>
+#include <linux/v4l2-mediabus.h><br>
+<br>
#include <libcamera/base/event_notifier.h><br>
#include <libcamera/base/log.h><br>
#include <libcamera/base/utils.h><br>
@@ -731,4 +733,173 @@ void V4L2Device::eventAvailable()<br>
frameStart.emit(event.u.frame_sync.frame_sequence);<br>
}<br>
<br>
+static const std::map<uint32_t, ColorSpace> v4l2ToColorSpaceTable = {<br>
+ { V4L2_COLORSPACE_RAW, ColorSpace::Raw },<br>
+ { V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg },<br>
+ { V4L2_COLORSPACE_SRGB, ColorSpace::Jpeg },<br>
+ { V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m },<br>
+ { V4L2_COLORSPACE_REC709, ColorSpace::Rec709 },<br>
+ { V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 },<br>
+};<br>
+<br>
+static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncodingTable = {<br>
+ { V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 },<br>
+ { V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 },<br>
+ { V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 },<br>
+};<br>
+<br>
+static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunctionTable = {<br>
+ { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear },<br>
+ { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb },<br>
+ { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 },<br>
+};<br>
+<br>
+static const std::map<uint32_t, ColorSpace::Range> v4l2ToRangeTable = {<br>
+ { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full },<br>
+ { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited },<br>
+};<br>
+<br>
+static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2Table = {<br>
+ { ColorSpace::Raw, V4L2_COLORSPACE_RAW },<br>
+ { ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG },<br>
+ { ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M },<br>
+ { ColorSpace::Rec709, V4L2_COLORSPACE_REC709 },<br>
+ { ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 },<br>
+};<br>
+<br>
+static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2Table = {<br>
+ { ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW },<br>
+ { ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M },<br>
+ { ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 },<br>
+ { ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 },<br>
+};<br>
+<br>
+static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2Table = {<br>
+ { ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 },<br>
+ { ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 },<br>
+ { ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 },<br>
+};<br>
+<br>
+static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2Table = {<br>
+ { ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE },<br>
+ { ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB },<br>
+ { ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 },<br>
+};<br>
+<br>
+static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2Table = {<br>
+ { ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE },<br>
+ { ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE },<br>
+};<br>
+<br>
+/**<br>
+ * \brief Convert the color space fields in a V4L2 format to a ColorSpace<br>
+ * \param[in] v4l2Format A V4L2 format containing color space information<br>
+ *<br>
+ * The colorspace, ycbcr_enc, xfer_func and quantization fields within a<br>
+ * V4L2 format structure are converted to a corresponding ColorSpace.<br>
+ *<br>
+ * If any V4L2 fields are not recognised those are marked as undefined in<br>
+ * the ColorSpace, but other fields are still initialised where possible.<br>
+ * This situation can be detected using the returned value's<br>
+ * ColorSpace::isFullyDefined() method.<br>
+ *<br>
+ * \return The ColorSpace corresponding to the input V4L2 format<br>
+ */<br>
+template<typename T><br>
+ColorSpace V4L2Device::v4l2ToColorSpace(const T &v4l2Format)<br>
+{<br>
+ ColorSpace colorSpace;<br>
+<br>
+ auto itColor = v4l2ToColorSpaceTable.find(v4l2Format.colorspace);<br>
+ if (itColor != v4l2ToColorSpaceTable.end())<br>
+ colorSpace = itColor->second;<br>
+<br>
+ auto itYcbcrEncoding = v4l2ToYcbcrEncodingTable.find(v4l2Format.ycbcr_enc);<br>
+ if (itYcbcrEncoding != v4l2ToYcbcrEncodingTable.end())<br>
+ colorSpace.ycbcrEncoding = itYcbcrEncoding->second;<br>
+<br>
+ auto itTransfer = v4l2ToTransferFunctionTable.find(v4l2Format.xfer_func);<br>
+ if (itTransfer != v4l2ToTransferFunctionTable.end())<br>
+ colorSpace.transferFunction = itTransfer->second;<br>
+<br>
+ auto itRange = v4l2ToRangeTable.find(v4l2Format.quantization);<br>
+ if (itRange != v4l2ToRangeTable.end())<br>
+ colorSpace.range = itRange->second;<br>
+<br>
+ return colorSpace;<br>
+}<br>
+<br>
+template ColorSpace V4L2Device::v4l2ToColorSpace(const struct v4l2_pix_format &);<br>
+template ColorSpace V4L2Device::v4l2ToColorSpace(const struct v4l2_pix_format_mplane &);<br>
+template ColorSpace V4L2Device::v4l2ToColorSpace(const struct v4l2_mbus_framefmt &);<br>
+<br>
+/**<br>
+ * \brief Fill in the color space fields of a V4L2 format from a ColorSpace<br>
+ * \param[in] colorSpace The ColorSpace to be converted<br>
+ * \param[out] v4l2Format A V4L2 format containing color space information<br>
+ *<br>
+ * The colorspace, ycbcr_enc, xfer_func and quantization fields within a<br>
+ * V4L2 format structure are filled in from a corresponding ColorSpace.<br>
+ *<br>
+ * An error is returned if any of the V4L2 fields do not support the<br>
+ * value given in the ColorSpace. Such fields are set to the V4L2<br>
+ * "default" values, but all other fields are still filled in where<br>
+ * possible.<br>
+ *<br>
+ * \return 0 on success or a negative error code otherwise<br>
+ */<br>
+template<typename T><br>
+int V4L2Device::colorSpaceToV4l2(const ColorSpace &colorSpace, T &v4l2Format)<br>
+{<br>
+ int ret = 0;<br>
+<br>
+ v4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT;<br>
+ v4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;<br>
+ v4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT;<br>
+ v4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT;<br>
+<br>
+ auto itColor = std::find_if(colorSpaceToV4l2Table.begin(), colorSpaceToV4l2Table.end(),<br>
+ [&colorSpace](const auto &item) {<br>
+ return colorSpace == item.first;<br>
+ });<br></blockquote><div><br></div><div>You can use std::map::find() here, to cut down on the typing :)</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+ if (itColor != colorSpaceToV4l2Table.end()) {<br>
+ v4l2Format.colorspace = itColor->second;<br>
+ return ret;<br>
+ }<br>
+<br>
+ /*<br>
+ * If the colorSpace doesn't precisely match a standard color space,<br>
+ * then we must choose a V4L2 colorspace with matching primaries.<br>
+ */<br>
+ auto itPrimaries = primariesToV4l2Table.find(colorSpace.primaries);<br>
+ if (itPrimaries != primariesToV4l2Table.end())<br>
+ v4l2Format.colorspace = itPrimaries->second;<br>
+ else<br>
+ ret = -1;<br>
+<br>
+ auto itYcbcrEncoding = ycbcrEncodingToV4l2Table.find(colorSpace.ycbcrEncoding);<br>
+ if (itYcbcrEncoding != ycbcrEncodingToV4l2Table.end())<br>
+ v4l2Format.ycbcr_enc = itYcbcrEncoding->second;<br>
+ else<br>
+ ret = -1;<br>
+<br>
+ auto itTransfer = transferFunctionToV4l2Table.find(colorSpace.transferFunction);<br>
+ if (itTransfer != transferFunctionToV4l2Table.end())<br>
+ v4l2Format.xfer_func = itTransfer->second;<br>
+ else<br>
+ ret = -1;<br>
+<br>
+ auto itRange = rangeToV4l2Table.find(colorSpace.range);<br>
+ if (itRange != rangeToV4l2Table.end())<br>
+ v4l2Format.quantization = itRange->second;<br>
+ else<br>
+ ret = -1;<br>
+<br>
+ return ret;<br>
+}<br>
+<br>
+template int V4L2Device::colorSpaceToV4l2(const ColorSpace &, struct v4l2_pix_format &);<br>
+template int V4L2Device::colorSpaceToV4l2(const ColorSpace &, struct v4l2_pix_format_mplane &);<br>
+template int V4L2Device::colorSpaceToV4l2(const ColorSpace &, struct v4l2_mbus_framefmt &);<br>
+<br>
} /* namespace libcamera */<br>
-- <br>
2.20.1<br>
<br>
</blockquote></div></div>