Message ID | 20210608144413.1529-3-david.plowman@raspberrypi.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi David. Thank you for your patch. On Tue, 8 Jun 2021 at 15:44, David Plowman <david.plowman@raspberrypi.com> wrote: > The ColorSpace class is added to the StreamConfiguration, and is now > passed to V4L2 devices where it is handled appropriately. Note how > this means that the colour space is configured per-stream (though > platforms may restrict this). > --- > include/libcamera/internal/v4l2_videodevice.h | 2 + > include/libcamera/stream.h | 3 + > src/libcamera/v4l2_videodevice.cpp | 112 ++++++++++++++++++ > 3 files changed, 117 insertions(+) > > diff --git a/include/libcamera/internal/v4l2_videodevice.h > b/include/libcamera/internal/v4l2_videodevice.h > index 7938343b..5122d422 100644 > --- a/include/libcamera/internal/v4l2_videodevice.h > +++ b/include/libcamera/internal/v4l2_videodevice.h > @@ -18,6 +18,7 @@ > > #include <libcamera/buffer.h> > #include <libcamera/class.h> > +#include <libcamera/color_space.h> > #include <libcamera/geometry.h> > #include <libcamera/pixel_format.h> > #include <libcamera/signal.h> > @@ -162,6 +163,7 @@ public: > > V4L2PixelFormat fourcc; > Size size; > + ColorSpace colorSpace; > > std::array<Plane, 3> planes; > unsigned int planesCount = 0; > diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h > index bb47c390..f79c6987 100644 > --- a/include/libcamera/stream.h > +++ b/include/libcamera/stream.h > @@ -13,6 +13,7 @@ > #include <vector> > > #include <libcamera/buffer.h> > +#include <libcamera/color_space.h> > #include <libcamera/geometry.h> > #include <libcamera/pixel_format.h> > > @@ -47,6 +48,8 @@ struct StreamConfiguration { > > unsigned int bufferCount; > > + ColorSpace colorSpace; > + > Stream *stream() const { return stream_; } > void setStream(Stream *stream) { stream_ = stream; } > const StreamFormats &formats() const { return formats_; } > diff --git a/src/libcamera/v4l2_videodevice.cpp > b/src/libcamera/v4l2_videodevice.cpp > index 12c09dc7..d606f81d 100644 > --- a/src/libcamera/v4l2_videodevice.cpp > +++ b/src/libcamera/v4l2_videodevice.cpp > @@ -730,6 +730,114 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat > *format) > return getFormatSingleplane(format); > } > > +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> > colorSpaceToV4l2 = { > + { ColorSpace::RAW, V4L2_COLORSPACE_RAW }, > + { ColorSpace::JFIF, V4L2_COLORSPACE_JPEG }, > + { ColorSpace::SMPTE170M, V4L2_COLORSPACE_SMPTE170M }, > + { ColorSpace::REC709, V4L2_COLORSPACE_REC709 }, > + { ColorSpace::REC2020, V4L2_COLORSPACE_BT2020 }, > +}; > + > +static const std::map<ColorSpace::Encoding, v4l2_ycbcr_encoding> > encodingToV4l2 = { > + { ColorSpace::Encoding::REC601, V4L2_YCBCR_ENC_601 }, > + { ColorSpace::Encoding::REC709, V4L2_YCBCR_ENC_709 }, > + { ColorSpace::Encoding::REC2020, V4L2_YCBCR_ENC_BT2020 }, > +}; > + > +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> > transferFunctionToV4l2 = { > + { ColorSpace::TransferFunction::IDENTITY, V4L2_XFER_FUNC_NONE }, > + { ColorSpace::TransferFunction::SRGB, V4L2_XFER_FUNC_SRGB }, > + { ColorSpace::TransferFunction::REC709, V4L2_XFER_FUNC_709 }, > +}; > + > +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = > { > + { ColorSpace::Range::FULL, V4L2_QUANTIZATION_FULL_RANGE }, > + { ColorSpace::Range::LIMITED, V4L2_QUANTIZATION_LIM_RANGE }, > +}; > + > +template<typename T> > +static void setColorSpace(const ColorSpace &colorSpace, T &v4l2PixFormat) > Could you expand on why this (and below) needs to be templated? > +{ > + if (!colorSpace.isFullyDefined()) > + LOG(V4L2, Warning) << "Setting non-fully defined colour > space" > + << colorSpace.toString(); > + > + v4l2PixFormat.colorspace = V4L2_COLORSPACE_DEFAULT; > + v4l2PixFormat.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > + v4l2PixFormat.xfer_func = V4L2_XFER_FUNC_DEFAULT; > + v4l2PixFormat.quantization = V4L2_QUANTIZATION_DEFAULT; > + > + auto it_color = std::find_if(colorSpaceToV4l2.begin(), > colorSpaceToV4l2.end(), > + [&colorSpace](const > std::pair<ColorSpace, v4l2_colorspace> &item) { return colorSpace == > item.first; }); > Could you use a map for colorSpaceToV4l2 like you do for the others? Regards, Naush > + if (it_color != colorSpaceToV4l2.end()) > + v4l2PixFormat.colorspace = it_color->second; > + > + auto it_encoding = encodingToV4l2.find(colorSpace.encoding); > + if (it_encoding != encodingToV4l2.end()) > + v4l2PixFormat.ycbcr_enc = it_encoding->second; > + > + auto it_transfer = > transferFunctionToV4l2.find(colorSpace.transferFunction); > + if (it_transfer != transferFunctionToV4l2.end()) > + v4l2PixFormat.xfer_func = it_transfer->second; > + > + auto it_range = rangeToV4l2.find(colorSpace.range); > + if (it_range != rangeToV4l2.end()) > + v4l2PixFormat.quantization = it_range->second; > +} > + > +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = { > + { V4L2_COLORSPACE_RAW, ColorSpace::RAW }, > + { V4L2_COLORSPACE_JPEG, ColorSpace::JFIF }, > + { V4L2_COLORSPACE_SRGB, ColorSpace::JFIF }, > + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::SMPTE170M }, > + { V4L2_COLORSPACE_REC709, ColorSpace::REC709 }, > + { V4L2_COLORSPACE_BT2020, ColorSpace::REC2020 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::Encoding> v4l2ToEncoding = { > + { V4L2_YCBCR_ENC_601, ColorSpace::Encoding::REC601 }, > + { V4L2_YCBCR_ENC_709, ColorSpace::Encoding::REC709 }, > + { V4L2_YCBCR_ENC_BT2020, ColorSpace::Encoding::REC2020 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::TransferFunction> > v4l2ToTransferFunction = { > + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::IDENTITY }, > + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::SRGB }, > + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::REC709 }, > +}; > + > +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = { > + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::FULL }, > + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::LIMITED }, > +}; > + > +template<typename T> > +ColorSpace getColorSpace(const T &v4l2PixFormat) > +{ > + ColorSpace colorSpace; > + > + auto it_color = v4l2ToColorSpace.find(v4l2PixFormat.colorspace); > + if (it_color != v4l2ToColorSpace.end()) > + colorSpace = it_color->second; > + > + auto it_encoding = v4l2ToEncoding.find(v4l2PixFormat.ycbcr_enc); > + if (it_encoding != v4l2ToEncoding.end()) > + colorSpace.encoding = it_encoding->second; > + > + auto it_transfer = > v4l2ToTransferFunction.find(v4l2PixFormat.xfer_func); > + if (it_transfer != v4l2ToTransferFunction.end()) > + colorSpace.transferFunction = it_transfer->second; > + > + auto it_range = v4l2ToRange.find(v4l2PixFormat.quantization); > + if (it_range != v4l2ToRange.end()) > + colorSpace.range = it_range->second; > + > + if (!colorSpace.isFullyDefined()) > + LOG(V4L2, Warning) << "Returning non-fully defined colour > space" > + << colorSpace.toString(); > + return colorSpace; > +} > + > /** > * \brief Try an image format on the V4L2 video device > * \param[inout] format The image format to test applicability to the > video device > @@ -839,6 +947,7 @@ int > V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format) > format->size.width = pix->width; > format->size.height = pix->height; > format->fourcc = V4L2PixelFormat(pix->pixelformat); > + format->colorSpace = getColorSpace(*pix); > format->planesCount = pix->num_planes; > > for (unsigned int i = 0; i < format->planesCount; ++i) { > @@ -861,6 +970,7 @@ int > V4L2VideoDevice::trySetFormatMultiplane(V4L2DeviceFormat *format, bool set) > pix->pixelformat = format->fourcc; > pix->num_planes = format->planesCount; > pix->field = V4L2_FIELD_NONE; > + setColorSpace(format->colorSpace, *pix); > > ASSERT(pix->num_planes <= std::size(pix->plane_fmt)); > > @@ -909,6 +1019,7 @@ int > V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format) > format->size.width = pix->width; > format->size.height = pix->height; > format->fourcc = V4L2PixelFormat(pix->pixelformat); > + format->colorSpace = getColorSpace(*pix); > format->planesCount = 1; > format->planes[0].bpl = pix->bytesperline; > format->planes[0].size = pix->sizeimage; > @@ -928,6 +1039,7 @@ int > V4L2VideoDevice::trySetFormatSingleplane(V4L2DeviceFormat *format, bool set) > pix->pixelformat = format->fourcc; > pix->bytesperline = format->planes[0].bpl; > pix->field = V4L2_FIELD_NONE; > + setColorSpace(format->colorSpace, *pix); > ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format); > if (ret) { > LOG(V4L2, Error) > -- > 2.20.1 > >
diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h index 7938343b..5122d422 100644 --- a/include/libcamera/internal/v4l2_videodevice.h +++ b/include/libcamera/internal/v4l2_videodevice.h @@ -18,6 +18,7 @@ #include <libcamera/buffer.h> #include <libcamera/class.h> +#include <libcamera/color_space.h> #include <libcamera/geometry.h> #include <libcamera/pixel_format.h> #include <libcamera/signal.h> @@ -162,6 +163,7 @@ public: V4L2PixelFormat fourcc; Size size; + ColorSpace colorSpace; std::array<Plane, 3> planes; unsigned int planesCount = 0; diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index bb47c390..f79c6987 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -13,6 +13,7 @@ #include <vector> #include <libcamera/buffer.h> +#include <libcamera/color_space.h> #include <libcamera/geometry.h> #include <libcamera/pixel_format.h> @@ -47,6 +48,8 @@ struct StreamConfiguration { unsigned int bufferCount; + ColorSpace colorSpace; + Stream *stream() const { return stream_; } void setStream(Stream *stream) { stream_ = stream; } const StreamFormats &formats() const { return formats_; } diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 12c09dc7..d606f81d 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -730,6 +730,114 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format) return getFormatSingleplane(format); } +static const std::vector<std::pair<ColorSpace, v4l2_colorspace>> colorSpaceToV4l2 = { + { ColorSpace::RAW, V4L2_COLORSPACE_RAW }, + { ColorSpace::JFIF, V4L2_COLORSPACE_JPEG }, + { ColorSpace::SMPTE170M, V4L2_COLORSPACE_SMPTE170M }, + { ColorSpace::REC709, V4L2_COLORSPACE_REC709 }, + { ColorSpace::REC2020, V4L2_COLORSPACE_BT2020 }, +}; + +static const std::map<ColorSpace::Encoding, v4l2_ycbcr_encoding> encodingToV4l2 = { + { ColorSpace::Encoding::REC601, V4L2_YCBCR_ENC_601 }, + { ColorSpace::Encoding::REC709, V4L2_YCBCR_ENC_709 }, + { ColorSpace::Encoding::REC2020, V4L2_YCBCR_ENC_BT2020 }, +}; + +static const std::map<ColorSpace::TransferFunction, v4l2_xfer_func> transferFunctionToV4l2 = { + { ColorSpace::TransferFunction::IDENTITY, V4L2_XFER_FUNC_NONE }, + { ColorSpace::TransferFunction::SRGB, V4L2_XFER_FUNC_SRGB }, + { ColorSpace::TransferFunction::REC709, V4L2_XFER_FUNC_709 }, +}; + +static const std::map<ColorSpace::Range, v4l2_quantization> rangeToV4l2 = { + { ColorSpace::Range::FULL, V4L2_QUANTIZATION_FULL_RANGE }, + { ColorSpace::Range::LIMITED, V4L2_QUANTIZATION_LIM_RANGE }, +}; + +template<typename T> +static void setColorSpace(const ColorSpace &colorSpace, T &v4l2PixFormat) +{ + if (!colorSpace.isFullyDefined()) + LOG(V4L2, Warning) << "Setting non-fully defined colour space" + << colorSpace.toString(); + + v4l2PixFormat.colorspace = V4L2_COLORSPACE_DEFAULT; + v4l2PixFormat.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + v4l2PixFormat.xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2PixFormat.quantization = V4L2_QUANTIZATION_DEFAULT; + + auto it_color = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(), + [&colorSpace](const std::pair<ColorSpace, v4l2_colorspace> &item) { return colorSpace == item.first; }); + if (it_color != colorSpaceToV4l2.end()) + v4l2PixFormat.colorspace = it_color->second; + + auto it_encoding = encodingToV4l2.find(colorSpace.encoding); + if (it_encoding != encodingToV4l2.end()) + v4l2PixFormat.ycbcr_enc = it_encoding->second; + + auto it_transfer = transferFunctionToV4l2.find(colorSpace.transferFunction); + if (it_transfer != transferFunctionToV4l2.end()) + v4l2PixFormat.xfer_func = it_transfer->second; + + auto it_range = rangeToV4l2.find(colorSpace.range); + if (it_range != rangeToV4l2.end()) + v4l2PixFormat.quantization = it_range->second; +} + +static const std::map<uint32_t, ColorSpace> v4l2ToColorSpace = { + { V4L2_COLORSPACE_RAW, ColorSpace::RAW }, + { V4L2_COLORSPACE_JPEG, ColorSpace::JFIF }, + { V4L2_COLORSPACE_SRGB, ColorSpace::JFIF }, + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::SMPTE170M }, + { V4L2_COLORSPACE_REC709, ColorSpace::REC709 }, + { V4L2_COLORSPACE_BT2020, ColorSpace::REC2020 }, +}; + +static const std::map<uint32_t, ColorSpace::Encoding> v4l2ToEncoding = { + { V4L2_YCBCR_ENC_601, ColorSpace::Encoding::REC601 }, + { V4L2_YCBCR_ENC_709, ColorSpace::Encoding::REC709 }, + { V4L2_YCBCR_ENC_BT2020, ColorSpace::Encoding::REC2020 }, +}; + +static const std::map<uint32_t, ColorSpace::TransferFunction> v4l2ToTransferFunction = { + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::IDENTITY }, + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::SRGB }, + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::REC709 }, +}; + +static const std::map<uint32_t, ColorSpace::Range> v4l2ToRange = { + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::FULL }, + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::LIMITED }, +}; + +template<typename T> +ColorSpace getColorSpace(const T &v4l2PixFormat) +{ + ColorSpace colorSpace; + + auto it_color = v4l2ToColorSpace.find(v4l2PixFormat.colorspace); + if (it_color != v4l2ToColorSpace.end()) + colorSpace = it_color->second; + + auto it_encoding = v4l2ToEncoding.find(v4l2PixFormat.ycbcr_enc); + if (it_encoding != v4l2ToEncoding.end()) + colorSpace.encoding = it_encoding->second; + + auto it_transfer = v4l2ToTransferFunction.find(v4l2PixFormat.xfer_func); + if (it_transfer != v4l2ToTransferFunction.end()) + colorSpace.transferFunction = it_transfer->second; + + auto it_range = v4l2ToRange.find(v4l2PixFormat.quantization); + if (it_range != v4l2ToRange.end()) + colorSpace.range = it_range->second; + + if (!colorSpace.isFullyDefined()) + LOG(V4L2, Warning) << "Returning non-fully defined colour space" + << colorSpace.toString(); + return colorSpace; +} + /** * \brief Try an image format on the V4L2 video device * \param[inout] format The image format to test applicability to the video device @@ -839,6 +947,7 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format) format->size.width = pix->width; format->size.height = pix->height; format->fourcc = V4L2PixelFormat(pix->pixelformat); + format->colorSpace = getColorSpace(*pix); format->planesCount = pix->num_planes; for (unsigned int i = 0; i < format->planesCount; ++i) { @@ -861,6 +970,7 @@ int V4L2VideoDevice::trySetFormatMultiplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->num_planes = format->planesCount; pix->field = V4L2_FIELD_NONE; + setColorSpace(format->colorSpace, *pix); ASSERT(pix->num_planes <= std::size(pix->plane_fmt)); @@ -909,6 +1019,7 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format) format->size.width = pix->width; format->size.height = pix->height; format->fourcc = V4L2PixelFormat(pix->pixelformat); + format->colorSpace = getColorSpace(*pix); format->planesCount = 1; format->planes[0].bpl = pix->bytesperline; format->planes[0].size = pix->sizeimage; @@ -928,6 +1039,7 @@ int V4L2VideoDevice::trySetFormatSingleplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->bytesperline = format->planes[0].bpl; pix->field = V4L2_FIELD_NONE; + setColorSpace(format->colorSpace, *pix); ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format); if (ret) { LOG(V4L2, Error)