Message ID | 20211210112142.18441-4-david.plowman@raspberrypi.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi David, Thank you for the patch. On Fri, Dec 10, 2021 at 11:21:37AM +0000, David Plowman wrote: > Add functions to the V4L2Device class to convert to and from > libcamera ColorSpace. > > These functions are added to the base V4L2Device class so that they can > be shared both by the video device class and subdevices. > > With the ColorSpace class, the color space and related other fields > are stored together, corresponding to a number of fields in the > various different V4L2 format structures. Template functions are > therefore a convenient implementation, and we must explicitly > instantiate the templates that will be needed. > > Note that unset color spaces are converted to requests for the > device's "default" color space. > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > include/libcamera/internal/v4l2_device.h | 8 + > src/libcamera/v4l2_device.cpp | 194 +++++++++++++++++++++++ > 2 files changed, 202 insertions(+) > > diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h > index 8886b750..a52a5f2c 100644 > --- a/include/libcamera/internal/v4l2_device.h > +++ b/include/libcamera/internal/v4l2_device.h > @@ -9,6 +9,7 @@ > > #include <map> > #include <memory> > +#include <optional> > #include <vector> > > #include <linux/videodev2.h> > @@ -18,6 +19,7 @@ > #include <libcamera/base/span.h> > #include <libcamera/base/unique_fd.h> > > +#include <libcamera/color_space.h> > #include <libcamera/controls.h> > > namespace libcamera { > @@ -56,6 +58,12 @@ protected: > > int fd() const { return fd_.get(); } > > + template<typename T> > + static std::optional<ColorSpace> toColorSpace(const T &v4l2Format); > + > + template<typename T> > + static int fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format); > + > private: > static ControlType v4l2CtrlType(uint32_t ctrlType); > static std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl); > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp > index 39f36009..3e8a180f 100644 > --- a/src/libcamera/v4l2_device.cpp > +++ b/src/libcamera/v4l2_device.cpp > @@ -10,11 +10,15 @@ > #include <fcntl.h> > #include <iomanip> > #include <limits.h> > +#include <map> > #include <stdlib.h> > #include <string.h> > #include <sys/ioctl.h> > #include <sys/syscall.h> > #include <unistd.h> > +#include <vector> > + > +#include <linux/v4l2-mediabus.h> > > #include <libcamera/base/event_notifier.h> > #include <libcamera/base/log.h> > @@ -728,4 +732,194 @@ void V4L2Device::eventAvailable() > frameStart.emit(event.u.frame_sync.frame_sequence); > } > > +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = { > + { V4L2_COLORSPACE_RAW, ColorSpace::Raw }, > + { V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg }, > + { V4L2_COLORSPACE_SRGB, ColorSpace::Srgb }, > + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m }, > + { V4L2_COLORSPACE_REC709, ColorSpace::Rec709 }, > + { V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = { > + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear }, > + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb }, > + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncoding = { > + { V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 }, > + { V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 }, > + { V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = { > + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full }, > + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited }, > +}; > + > +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = { > + { ColorSpace::Raw, V4L2_COLORSPACE_RAW }, > + { ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG }, > + { ColorSpace::Srgb, V4L2_COLORSPACE_SRGB }, > + { ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, > + { ColorSpace::Rec709, V4L2_COLORSPACE_REC709 }, > + { ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 }, > +}; > + > +static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2 = { > + { ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW }, > + { ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, > + { ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 }, > + { ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 }, > +}; > + > +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = { > + { ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE }, > + { ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB }, > + { ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 }, > +}; > + > +static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2 = { > + { ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 }, > + { ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 }, > + { ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 }, > +}; > + > +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = { > + { ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE }, > + { ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE }, > +}; > + > +/** > + * \brief Convert the color space fields in a V4L2 format to a ColorSpace > + * \param[in] v4l2Format A V4L2 format containing color space information > + * > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a > + * V4L2 format structure are converted to a corresponding ColorSpace. > + * > + * If any V4L2 fields are not recognised then we return an "unset" > + * color space. > + * > + * \return The ColorSpace corresponding to the input V4L2 format > + * \retval std::nullopt One or more V4L2 color space fields were not recognised > + */ > +template<typename T> > +std::optional<ColorSpace> V4L2Device::toColorSpace(const T &v4l2Format) > +{ > + auto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace); > + if (itColor == v4l2ToColorSpace.end()) > + return std::nullopt; > + > + /* This sets all the color space fields to the correct "default" values. */ > + ColorSpace colorSpace = itColor->second; > + > + if (v4l2Format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) { > + auto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc); > + if (itYcbcrEncoding == v4l2ToYcbcrEncoding.end()) > + return std::nullopt; > + > + colorSpace.ycbcrEncoding = itYcbcrEncoding->second; > + } > + > + if (v4l2Format.xfer_func != V4L2_XFER_FUNC_DEFAULT) { > + auto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func); > + if (itTransfer == v4l2ToTransferFunction.end()) > + return std::nullopt; > + > + colorSpace.transferFunction = itTransfer->second; > + } There's also a comment in v10 which hasn't been taken into account (or explicitly rejected). > + > + if (v4l2Format.quantization != V4L2_QUANTIZATION_DEFAULT) { > + auto itRange = v4l2ToRange.find(v4l2Format.quantization); > + if (itRange == v4l2ToRange.end()) > + return std::nullopt; > + > + colorSpace.range = itRange->second; > + } > + > + return colorSpace; > +} > + > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format &); > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &); > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_mbus_framefmt &); > + > +/** > + * \brief Fill in the color space fields of a V4L2 format from a ColorSpace > + * \param[in] colorSpace The ColorSpace to be converted > + * \param[out] v4l2Format A V4L2 format containing color space information > + * > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a > + * V4L2 format structure are filled in from a corresponding ColorSpace. > + * > + * An error is returned if any of the V4L2 fields do not support the > + * value given in the ColorSpace. Such fields are set to the V4L2 > + * "default" values, but all other fields are still filled in where > + * possible. > + * > + * If the color space is completely unset, "default" V4L2 values are used > + * everywhere, so a driver would then choose its preferred color space. > + * > + * \return 0 on success or a negative error code otherwise > + * \retval -EINVAL The ColorSpace does not have a representation using V4L2 enums > + */ > +template<typename T> > +int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format) > +{ > + v4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT; > + v4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + v4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT; > + v4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT; > + > + if (!colorSpace) > + return 0; > + > + auto itColor = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(), > + [&colorSpace](const auto &item) { > + return colorSpace == item.first; > + }); > + if (itColor != colorSpaceToV4l2.end()) { > + v4l2Format.colorspace = itColor->second; > + /* Leaving all the other fields as "default" should be fine. */ > + return 0; > + } > + > + /* > + * If the colorSpace doesn't precisely match a standard color space, > + * then we must choose a V4L2 colorspace with matching primaries. > + */ > + int ret = 0; > + > + auto itPrimaries = primariesToV4l2.find(colorSpace->primaries); > + if (itPrimaries != primariesToV4l2.end()) > + v4l2Format.colorspace = itPrimaries->second; > + else > + ret = -EINVAL; > + > + auto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace->ycbcrEncoding); > + if (itYcbcrEncoding != ycbcrEncodingToV4l2.end()) > + v4l2Format.ycbcr_enc = itYcbcrEncoding->second; > + else > + ret = -EINVAL; > + > + auto itTransfer = transferFunctionToV4l2.find(colorSpace->transferFunction); > + if (itTransfer != transferFunctionToV4l2.end()) > + v4l2Format.xfer_func = itTransfer->second; > + else > + ret = -EINVAL; > + > + auto itRange = rangeToV4l2.find(colorSpace->range); > + if (itRange != rangeToV4l2.end()) > + v4l2Format.quantization = itRange->second; > + else > + ret = -EINVAL; > + > + return ret; > +} > + > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format &); > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format_mplane &); > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_mbus_framefmt &); > + > } /* namespace libcamera */
Hi Laurent Yes, I seem to have missed a couple, but I am intending to add everything. v12 arriving shortly... Thanks David On Fri, 10 Dec 2021 at 12:47, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote: > > Hi David, > > Thank you for the patch. > > On Fri, Dec 10, 2021 at 11:21:37AM +0000, David Plowman wrote: > > Add functions to the V4L2Device class to convert to and from > > libcamera ColorSpace. > > > > These functions are added to the base V4L2Device class so that they can > > be shared both by the video device class and subdevices. > > > > With the ColorSpace class, the color space and related other fields > > are stored together, corresponding to a number of fields in the > > various different V4L2 format structures. Template functions are > > therefore a convenient implementation, and we must explicitly > > instantiate the templates that will be needed. > > > > Note that unset color spaces are converted to requests for the > > device's "default" color space. > > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com> > > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > --- > > include/libcamera/internal/v4l2_device.h | 8 + > > src/libcamera/v4l2_device.cpp | 194 +++++++++++++++++++++++ > > 2 files changed, 202 insertions(+) > > > > diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h > > index 8886b750..a52a5f2c 100644 > > --- a/include/libcamera/internal/v4l2_device.h > > +++ b/include/libcamera/internal/v4l2_device.h > > @@ -9,6 +9,7 @@ > > > > #include <map> > > #include <memory> > > +#include <optional> > > #include <vector> > > > > #include <linux/videodev2.h> > > @@ -18,6 +19,7 @@ > > #include <libcamera/base/span.h> > > #include <libcamera/base/unique_fd.h> > > > > +#include <libcamera/color_space.h> > > #include <libcamera/controls.h> > > > > namespace libcamera { > > @@ -56,6 +58,12 @@ protected: > > > > int fd() const { return fd_.get(); } > > > > + template<typename T> > > + static std::optional<ColorSpace> toColorSpace(const T &v4l2Format); > > + > > + template<typename T> > > + static int fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format); > > + > > private: > > static ControlType v4l2CtrlType(uint32_t ctrlType); > > static std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl); > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp > > index 39f36009..3e8a180f 100644 > > --- a/src/libcamera/v4l2_device.cpp > > +++ b/src/libcamera/v4l2_device.cpp > > @@ -10,11 +10,15 @@ > > #include <fcntl.h> > > #include <iomanip> > > #include <limits.h> > > +#include <map> > > #include <stdlib.h> > > #include <string.h> > > #include <sys/ioctl.h> > > #include <sys/syscall.h> > > #include <unistd.h> > > +#include <vector> > > + > > +#include <linux/v4l2-mediabus.h> > > > > #include <libcamera/base/event_notifier.h> > > #include <libcamera/base/log.h> > > @@ -728,4 +732,194 @@ void V4L2Device::eventAvailable() > > frameStart.emit(event.u.frame_sync.frame_sequence); > > } > > > > +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = { > > + { V4L2_COLORSPACE_RAW, ColorSpace::Raw }, > > + { V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg }, > > + { V4L2_COLORSPACE_SRGB, ColorSpace::Srgb }, > > + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m }, > > + { V4L2_COLORSPACE_REC709, ColorSpace::Rec709 }, > > + { V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 }, > > +}; > > + > > +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = { > > + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear }, > > + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb }, > > + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 }, > > +}; > > + > > +static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncoding = { > > + { V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 }, > > + { V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 }, > > + { V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 }, > > +}; > > + > > +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = { > > + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full }, > > + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited }, > > +}; > > + > > +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = { > > + { ColorSpace::Raw, V4L2_COLORSPACE_RAW }, > > + { ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG }, > > + { ColorSpace::Srgb, V4L2_COLORSPACE_SRGB }, > > + { ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, > > + { ColorSpace::Rec709, V4L2_COLORSPACE_REC709 }, > > + { ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 }, > > +}; > > + > > +static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2 = { > > + { ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW }, > > + { ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, > > + { ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 }, > > + { ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 }, > > +}; > > + > > +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = { > > + { ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE }, > > + { ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB }, > > + { ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 }, > > +}; > > + > > +static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2 = { > > + { ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 }, > > + { ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 }, > > + { ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 }, > > +}; > > + > > +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = { > > + { ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE }, > > + { ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE }, > > +}; > > + > > +/** > > + * \brief Convert the color space fields in a V4L2 format to a ColorSpace > > + * \param[in] v4l2Format A V4L2 format containing color space information > > + * > > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a > > + * V4L2 format structure are converted to a corresponding ColorSpace. > > + * > > + * If any V4L2 fields are not recognised then we return an "unset" > > + * color space. > > + * > > + * \return The ColorSpace corresponding to the input V4L2 format > > + * \retval std::nullopt One or more V4L2 color space fields were not recognised > > + */ > > +template<typename T> > > +std::optional<ColorSpace> V4L2Device::toColorSpace(const T &v4l2Format) > > +{ > > + auto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace); > > + if (itColor == v4l2ToColorSpace.end()) > > + return std::nullopt; > > + > > + /* This sets all the color space fields to the correct "default" values. */ > > + ColorSpace colorSpace = itColor->second; > > + > > + if (v4l2Format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) { > > + auto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc); > > + if (itYcbcrEncoding == v4l2ToYcbcrEncoding.end()) > > + return std::nullopt; > > + > > + colorSpace.ycbcrEncoding = itYcbcrEncoding->second; > > + } > > + > > + if (v4l2Format.xfer_func != V4L2_XFER_FUNC_DEFAULT) { > > + auto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func); > > + if (itTransfer == v4l2ToTransferFunction.end()) > > + return std::nullopt; > > + > > + colorSpace.transferFunction = itTransfer->second; > > + } > > There's also a comment in v10 which hasn't been taken into account (or > explicitly rejected). > > > + > > + if (v4l2Format.quantization != V4L2_QUANTIZATION_DEFAULT) { > > + auto itRange = v4l2ToRange.find(v4l2Format.quantization); > > + if (itRange == v4l2ToRange.end()) > > + return std::nullopt; > > + > > + colorSpace.range = itRange->second; > > + } > > + > > + return colorSpace; > > +} > > + > > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format &); > > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &); > > +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_mbus_framefmt &); > > + > > +/** > > + * \brief Fill in the color space fields of a V4L2 format from a ColorSpace > > + * \param[in] colorSpace The ColorSpace to be converted > > + * \param[out] v4l2Format A V4L2 format containing color space information > > + * > > + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a > > + * V4L2 format structure are filled in from a corresponding ColorSpace. > > + * > > + * An error is returned if any of the V4L2 fields do not support the > > + * value given in the ColorSpace. Such fields are set to the V4L2 > > + * "default" values, but all other fields are still filled in where > > + * possible. > > + * > > + * If the color space is completely unset, "default" V4L2 values are used > > + * everywhere, so a driver would then choose its preferred color space. > > + * > > + * \return 0 on success or a negative error code otherwise > > + * \retval -EINVAL The ColorSpace does not have a representation using V4L2 enums > > + */ > > +template<typename T> > > +int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format) > > +{ > > + v4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT; > > + v4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > > + v4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT; > > + v4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT; > > + > > + if (!colorSpace) > > + return 0; > > + > > + auto itColor = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(), > > + [&colorSpace](const auto &item) { > > + return colorSpace == item.first; > > + }); > > + if (itColor != colorSpaceToV4l2.end()) { > > + v4l2Format.colorspace = itColor->second; > > + /* Leaving all the other fields as "default" should be fine. */ > > + return 0; > > + } > > + > > + /* > > + * If the colorSpace doesn't precisely match a standard color space, > > + * then we must choose a V4L2 colorspace with matching primaries. > > + */ > > + int ret = 0; > > + > > + auto itPrimaries = primariesToV4l2.find(colorSpace->primaries); > > + if (itPrimaries != primariesToV4l2.end()) > > + v4l2Format.colorspace = itPrimaries->second; > > + else > > + ret = -EINVAL; > > + > > + auto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace->ycbcrEncoding); > > + if (itYcbcrEncoding != ycbcrEncodingToV4l2.end()) > > + v4l2Format.ycbcr_enc = itYcbcrEncoding->second; > > + else > > + ret = -EINVAL; > > + > > + auto itTransfer = transferFunctionToV4l2.find(colorSpace->transferFunction); > > + if (itTransfer != transferFunctionToV4l2.end()) > > + v4l2Format.xfer_func = itTransfer->second; > > + else > > + ret = -EINVAL; > > + > > + auto itRange = rangeToV4l2.find(colorSpace->range); > > + if (itRange != rangeToV4l2.end()) > > + v4l2Format.quantization = itRange->second; > > + else > > + ret = -EINVAL; > > + > > + return ret; > > +} > > + > > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format &); > > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format_mplane &); > > +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_mbus_framefmt &); > > + > > } /* namespace libcamera */ > > -- > Regards, > > Laurent Pinchart
diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h index 8886b750..a52a5f2c 100644 --- a/include/libcamera/internal/v4l2_device.h +++ b/include/libcamera/internal/v4l2_device.h @@ -9,6 +9,7 @@ #include <map> #include <memory> +#include <optional> #include <vector> #include <linux/videodev2.h> @@ -18,6 +19,7 @@ #include <libcamera/base/span.h> #include <libcamera/base/unique_fd.h> +#include <libcamera/color_space.h> #include <libcamera/controls.h> namespace libcamera { @@ -56,6 +58,12 @@ protected: int fd() const { return fd_.get(); } + template<typename T> + static std::optional<ColorSpace> toColorSpace(const T &v4l2Format); + + template<typename T> + static int fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format); + private: static ControlType v4l2CtrlType(uint32_t ctrlType); static std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl); diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 39f36009..3e8a180f 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -10,11 +10,15 @@ #include <fcntl.h> #include <iomanip> #include <limits.h> +#include <map> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/syscall.h> #include <unistd.h> +#include <vector> + +#include <linux/v4l2-mediabus.h> #include <libcamera/base/event_notifier.h> #include <libcamera/base/log.h> @@ -728,4 +732,194 @@ void V4L2Device::eventAvailable() frameStart.emit(event.u.frame_sync.frame_sequence); } +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = { + { V4L2_COLORSPACE_RAW, ColorSpace::Raw }, + { V4L2_COLORSPACE_JPEG, ColorSpace::Jpeg }, + { V4L2_COLORSPACE_SRGB, ColorSpace::Srgb }, + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::Smpte170m }, + { V4L2_COLORSPACE_REC709, ColorSpace::Rec709 }, + { V4L2_COLORSPACE_BT2020, ColorSpace::Rec2020 }, +}; + +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = { + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::Linear }, + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::Srgb }, + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::Rec709 }, +}; + +static const std::map<uint32_t, ColorSpace::YcbcrEncoding> v4l2ToYcbcrEncoding = { + { V4L2_YCBCR_ENC_601, ColorSpace::YcbcrEncoding::Rec601 }, + { V4L2_YCBCR_ENC_709, ColorSpace::YcbcrEncoding::Rec709 }, + { V4L2_YCBCR_ENC_BT2020, ColorSpace::YcbcrEncoding::Rec2020 }, +}; + +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = { + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::Full }, + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::Limited }, +}; + +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = { + { ColorSpace::Raw, V4L2_COLORSPACE_RAW }, + { ColorSpace::Jpeg, V4L2_COLORSPACE_JPEG }, + { ColorSpace::Srgb, V4L2_COLORSPACE_SRGB }, + { ColorSpace::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, + { ColorSpace::Rec709, V4L2_COLORSPACE_REC709 }, + { ColorSpace::Rec2020, V4L2_COLORSPACE_BT2020 }, +}; + +static const std::map<ColorSpace::Primaries, v4l2_colorspace> primariesToV4l2 = { + { ColorSpace::Primaries::Raw, V4L2_COLORSPACE_RAW }, + { ColorSpace::Primaries::Smpte170m, V4L2_COLORSPACE_SMPTE170M }, + { ColorSpace::Primaries::Rec709, V4L2_COLORSPACE_REC709 }, + { ColorSpace::Primaries::Rec2020, V4L2_COLORSPACE_BT2020 }, +}; + +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = { + { ColorSpace::TransferFunction::Linear, V4L2_XFER_FUNC_NONE }, + { ColorSpace::TransferFunction::Srgb, V4L2_XFER_FUNC_SRGB }, + { ColorSpace::TransferFunction::Rec709, V4L2_XFER_FUNC_709 }, +}; + +static const std::map<ColorSpace::YcbcrEncoding, v4l2_ycbcr_encoding> ycbcrEncodingToV4l2 = { + { ColorSpace::YcbcrEncoding::Rec601, V4L2_YCBCR_ENC_601 }, + { ColorSpace::YcbcrEncoding::Rec709, V4L2_YCBCR_ENC_709 }, + { ColorSpace::YcbcrEncoding::Rec2020, V4L2_YCBCR_ENC_BT2020 }, +}; + +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = { + { ColorSpace::Range::Full, V4L2_QUANTIZATION_FULL_RANGE }, + { ColorSpace::Range::Limited, V4L2_QUANTIZATION_LIM_RANGE }, +}; + +/** + * \brief Convert the color space fields in a V4L2 format to a ColorSpace + * \param[in] v4l2Format A V4L2 format containing color space information + * + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a + * V4L2 format structure are converted to a corresponding ColorSpace. + * + * If any V4L2 fields are not recognised then we return an "unset" + * color space. + * + * \return The ColorSpace corresponding to the input V4L2 format + * \retval std::nullopt One or more V4L2 color space fields were not recognised + */ +template<typename T> +std::optional<ColorSpace> V4L2Device::toColorSpace(const T &v4l2Format) +{ + auto itColor = v4l2ToColorSpace.find(v4l2Format.colorspace); + if (itColor == v4l2ToColorSpace.end()) + return std::nullopt; + + /* This sets all the color space fields to the correct "default" values. */ + ColorSpace colorSpace = itColor->second; + + if (v4l2Format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT) { + auto itYcbcrEncoding = v4l2ToYcbcrEncoding.find(v4l2Format.ycbcr_enc); + if (itYcbcrEncoding == v4l2ToYcbcrEncoding.end()) + return std::nullopt; + + colorSpace.ycbcrEncoding = itYcbcrEncoding->second; + } + + if (v4l2Format.xfer_func != V4L2_XFER_FUNC_DEFAULT) { + auto itTransfer = v4l2ToTransferFunction.find(v4l2Format.xfer_func); + if (itTransfer == v4l2ToTransferFunction.end()) + return std::nullopt; + + colorSpace.transferFunction = itTransfer->second; + } + + if (v4l2Format.quantization != V4L2_QUANTIZATION_DEFAULT) { + auto itRange = v4l2ToRange.find(v4l2Format.quantization); + if (itRange == v4l2ToRange.end()) + return std::nullopt; + + colorSpace.range = itRange->second; + } + + return colorSpace; +} + +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format &); +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_pix_format_mplane &); +template std::optional<ColorSpace> V4L2Device::toColorSpace(const struct v4l2_mbus_framefmt &); + +/** + * \brief Fill in the color space fields of a V4L2 format from a ColorSpace + * \param[in] colorSpace The ColorSpace to be converted + * \param[out] v4l2Format A V4L2 format containing color space information + * + * The colorspace, ycbcr_enc, xfer_func and quantization fields within a + * V4L2 format structure are filled in from a corresponding ColorSpace. + * + * An error is returned if any of the V4L2 fields do not support the + * value given in the ColorSpace. Such fields are set to the V4L2 + * "default" values, but all other fields are still filled in where + * possible. + * + * If the color space is completely unset, "default" V4L2 values are used + * everywhere, so a driver would then choose its preferred color space. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The ColorSpace does not have a representation using V4L2 enums + */ +template<typename T> +int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &colorSpace, T &v4l2Format) +{ + v4l2Format.colorspace = V4L2_COLORSPACE_DEFAULT; + v4l2Format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + v4l2Format.xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2Format.quantization = V4L2_QUANTIZATION_DEFAULT; + + if (!colorSpace) + return 0; + + auto itColor = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(), + [&colorSpace](const auto &item) { + return colorSpace == item.first; + }); + if (itColor != colorSpaceToV4l2.end()) { + v4l2Format.colorspace = itColor->second; + /* Leaving all the other fields as "default" should be fine. */ + return 0; + } + + /* + * If the colorSpace doesn't precisely match a standard color space, + * then we must choose a V4L2 colorspace with matching primaries. + */ + int ret = 0; + + auto itPrimaries = primariesToV4l2.find(colorSpace->primaries); + if (itPrimaries != primariesToV4l2.end()) + v4l2Format.colorspace = itPrimaries->second; + else + ret = -EINVAL; + + auto itYcbcrEncoding = ycbcrEncodingToV4l2.find(colorSpace->ycbcrEncoding); + if (itYcbcrEncoding != ycbcrEncodingToV4l2.end()) + v4l2Format.ycbcr_enc = itYcbcrEncoding->second; + else + ret = -EINVAL; + + auto itTransfer = transferFunctionToV4l2.find(colorSpace->transferFunction); + if (itTransfer != transferFunctionToV4l2.end()) + v4l2Format.xfer_func = itTransfer->second; + else + ret = -EINVAL; + + auto itRange = rangeToV4l2.find(colorSpace->range); + if (itRange != rangeToV4l2.end()) + v4l2Format.quantization = itRange->second; + else + ret = -EINVAL; + + return ret; +} + +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format &); +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_pix_format_mplane &); +template int V4L2Device::fromColorSpace(const std::optional<ColorSpace> &, struct v4l2_mbus_framefmt &); + } /* namespace libcamera */