From: Kirill A. Korinsky Subject: sys/uvideo: support colorformat from device To: OpenBSD tech Cc: Marcus Glocker Date: Mon, 17 Feb 2025 01:51:30 +0100 tech@, I'd like to add support for the color profile of the webcam when it announces it. My Jabra PanaCast 20 does this for mjpeg with non-default luma and chroma values profile: Rec 609 vs 709, and this diff improves the quality of the picture, and I look alive :), and not a bit green or blue. Ok? Index: sys/dev/usb/uvideo.c =================================================================== RCS file: /home/cvs/src/sys/dev/usb/uvideo.c,v diff -u -p -r1.238 uvideo.c --- sys/dev/usb/uvideo.c 15 Feb 2025 09:05:15 -0000 1.238 +++ sys/dev/usb/uvideo.c 17 Feb 2025 00:36:13 -0000 @@ -131,6 +131,8 @@ usbd_status uvideo_vs_parse_desc(struct usbd_status uvideo_vs_parse_desc_input_header(struct uvideo_softc *, const usb_descriptor_t *); usbd_status uvideo_vs_parse_desc_format(struct uvideo_softc *); +usbd_status uvideo_vs_parse_desc_colorformat(struct uvideo_softc *, + const usb_descriptor_t *); usbd_status uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc *, const usb_descriptor_t *); usbd_status uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc *, @@ -420,6 +422,35 @@ const struct uvideo_map_fmts { { UVIDEO_FORMAT_GUID_INVI, V4L2_PIX_FMT_Y10 }, }; +const enum v4l2_colorspace uvideo_color_primaries[] = { + V4L2_COLORSPACE_SRGB, /* Unspecified */ + V4L2_COLORSPACE_SRGB, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, +}; + +const enum v4l2_xfer_func uvideo_transfer_characteristics[] = { + V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ + V4L2_XFER_FUNC_709, + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ + V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ + V4L2_XFER_FUNC_SMPTE240M, + V4L2_XFER_FUNC_NONE, + V4L2_XFER_FUNC_SRGB, +}; + +const enum v4l2_ycbcr_encoding uvideo_matrix_coefficients[] = { + V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ + V4L2_YCBCR_ENC_709, + V4L2_YCBCR_ENC_601, /* Substitution for FCC */ + V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ + V4L2_YCBCR_ENC_601, + V4L2_YCBCR_ENC_SMPTE240M, +}; + int uvideo_open(void *addr, int flags, int *size, uint8_t *buffer, void (*intr)(void *), void *arg) @@ -1010,6 +1041,12 @@ uvideo_vs_parse_desc_format(struct uvide } switch (desc->bDescriptorSubtype) { + case UDESCSUB_VS_COLORFORMAT: + if (desc->bLength == 6) { + (void)uvideo_vs_parse_desc_colorformat( + sc, desc); + } + break; case UDESCSUB_VS_FORMAT_MJPEG: if (desc->bLength == 11) { (void)uvideo_vs_parse_desc_format_mjpeg( @@ -1040,6 +1077,43 @@ uvideo_vs_parse_desc_format(struct uvide } usbd_status +uvideo_vs_parse_desc_colorformat(struct uvideo_softc *sc, + const usb_descriptor_t *desc) +{ + int fmtidx; + struct usb_video_colorformat_desc *d; + + d = (struct usb_video_colorformat_desc *)(uint8_t *)desc; + + fmtidx = sc->sc_fmtgrp_idx - 1; + if (fmtidx < 0) { + printf("%s: no format before the color matching descriptor!\n", + DEVNAME(sc)); + return (USBD_INVAL); + } + + if (d->bColorPrimaries < nitems(uvideo_color_primaries)) + sc->sc_fmtgrp[fmtidx].colorspace = + uvideo_color_primaries[d->bColorPrimaries]; + else + sc->sc_fmtgrp[fmtidx].colorspace = V4L2_COLORSPACE_SRGB; + + if (d->bTransferCharacteristics < nitems(uvideo_transfer_characteristics)) + sc->sc_fmtgrp[fmtidx].xfer_func = + uvideo_transfer_characteristics[d->bTransferCharacteristics]; + else + sc->sc_fmtgrp[fmtidx].xfer_func = V4L2_XFER_FUNC_DEFAULT; + + if (d->bMatrixCoefficients < nitems(uvideo_matrix_coefficients)) + sc->sc_fmtgrp[fmtidx].ycbcr_enc = + uvideo_matrix_coefficients[d->bMatrixCoefficients]; + else + sc->sc_fmtgrp[fmtidx].ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + + return (USBD_NORMAL_COMPLETION); +} + +usbd_status uvideo_vs_parse_desc_format_mjpeg(struct uvideo_softc *sc, const usb_descriptor_t *desc) { @@ -3242,6 +3316,9 @@ uvideo_g_fmt(void *v, struct v4l2_format return (EINVAL); fmt->fmt.pix.pixelformat = sc->sc_fmtgrp_cur->pixelformat; + fmt->fmt.pix.colorspace = sc->sc_fmtgrp_cur->colorspace; + fmt->fmt.pix.xfer_func = sc->sc_fmtgrp_cur->xfer_func; + fmt->fmt.pix.ycbcr_enc = sc->sc_fmtgrp_cur->ycbcr_enc; fmt->fmt.pix.width = UGETW(sc->sc_fmtgrp_cur->frame_cur->wWidth); fmt->fmt.pix.height = UGETW(sc->sc_fmtgrp_cur->frame_cur->wHeight); fmt->fmt.pix.sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize); Index: sys/dev/usb/uvideo.h =================================================================== RCS file: /home/cvs/src/sys/dev/usb/uvideo.h,v diff -u -p -r1.63 uvideo.h --- sys/dev/usb/uvideo.h 16 Jan 2025 22:58:19 -0000 1.63 +++ sys/dev/usb/uvideo.h 17 Feb 2025 00:35:54 -0000 @@ -412,6 +412,16 @@ struct usb_video_stream_header { /* TODO complete struct */ } __packed; +/* Table 3-19: Color Matching Descriptor */ +struct usb_video_colorformat_desc { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bColorPrimaries; + uByte bTransferCharacteristics; + uByte bMatrixCoefficients; +} __packed; + /* Table 3-1: Motion-JPEG Video Format Descriptor */ struct usb_video_format_mjpeg_desc { uByte bLength; @@ -551,6 +561,9 @@ typedef SIMPLEQ_HEAD(, uvideo_mmap) q_mm struct uvideo_format_group { uint32_t pixelformat; + uint32_t colorspace; + uint32_t xfer_func; + uint32_t ycbcr_enc; uint8_t format_dfidx; struct uvideo_format_desc *format; /* frame descriptors for mjpeg and uncompressed are identical */