Download raw body.
sys/uvideo: support H.264 format and frame
On Sat, Aug 02, 2025 at 01:18:19PM +0200, Kirill A. Korinsky wrote:
> On Sat, 02 Aug 2025 01:07:59 +0200,
> Kirill A. Korinsky <kirill@korins.ky> wrote:
> >
> > tech@,
> >
> > here a diff which brings support of H.264 stream from webcam.
> >
> > Tested on Elgato Facecam Pro.
> >
> > Also tested as raw and mjpeg against:
> > - Elgato Facecam Pro
> > - Jabra PanaCast 20
> > - Azurewave, USB camera
> >
> > No regression discovered.
> >
>
> Here a bit cleaner version. Changes only related to UVIDEO_FRAME_MIN_LEN and
> UVIDEO_FRAME_H264_MIN_LEN, I've replaced a constant by offsetof and sizeof.
>
> Ok?
The diff looks good to me. No regressions noticed during quick testing.
ok mglocker@
When reviewing the diff I've noticed that we might could do some small
improvements regarding the structure sizes in a second step:
- We probably should define an union structure within a packed structure
also with '__packed', like other drivers are doing it. I'm not sure
depending on the compiler/architecture whether otherwise an union
structure could be interpreted as non-packed. Maybe other developers
can comment on their opinion about that.
- The constant integers used in uvideo_vs_parse_desc_format() for the
format structure sizes should be all replaced by defines in uvideo.h:
desc->bLength == 6
desc->bLength == 11
desc->bLength == 27
desc->bLength == 52
> Index: sys/dev/usb/uvideo.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/usb/uvideo.c,v
> diff -u -p -r1.258 uvideo.c
> --- sys/dev/usb/uvideo.c 19 Apr 2025 19:35:32 -0000 1.258
> +++ sys/dev/usb/uvideo.c 1 Aug 2025 22:53:22 -0000
> @@ -132,11 +132,15 @@ usbd_status uvideo_vs_parse_desc_input_h
> 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_h264(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 *,
> const usb_descriptor_t *);
> usbd_status uvideo_vs_parse_desc_frame(struct uvideo_softc *);
> +usbd_status uvideo_vs_parse_desc_frame_h264(struct uvideo_softc *,
> + const usb_descriptor_t *);
> usbd_status uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *,
> const usb_descriptor_t *);
> uint32_t uvideo_vc_parse_max_packet_size(struct uvideo_softc *,
> @@ -210,8 +214,12 @@ void uvideo_dump_desc_format_mjpeg(stru
> const usb_descriptor_t *);
> void uvideo_dump_desc_format_uncompressed(struct uvideo_softc *,
> const usb_descriptor_t *);
> +void uvideo_dump_desc_format_h264(struct uvideo_softc *,
> + const usb_descriptor_t *);
> void uvideo_dump_desc_frame(struct uvideo_softc *,
> const usb_descriptor_t *);
> +void uvideo_dump_desc_h264_frame(struct uvideo_softc *,
> + const usb_descriptor_t *);
> void uvideo_dump_desc_processing(struct uvideo_softc *,
> const usb_descriptor_t *);
> void uvideo_dump_desc_extension(struct uvideo_softc *,
> @@ -738,10 +746,8 @@ uvideo_vc_parse_desc(struct uvideo_softc
> break;
> case UDESCSUB_VC_PROCESSING_UNIT:
> /* XXX do correct length calculation */
> - if (desc->bLength <
> - sizeof(struct usb_video_frame_desc)) {
> + if (desc->bLength < UVIDEO_FRAME_MIN_LEN)
> (void)uvideo_vc_parse_desc_pu(sc, desc);
> - }
> break;
>
> /* TODO: which VC descriptors do we need else? */
> @@ -1058,6 +1064,13 @@ uvideo_vs_parse_desc_format(struct uvide
> sc, desc);
> }
> break;
> + case UDESCSUB_VS_FORMAT_H264:
> + case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
> + if (desc->bLength == 52) {
> + (void)uvideo_vs_parse_desc_format_h264(
> + sc, desc);
> + }
> + break;
> }
>
> desc = usbd_desc_iter_next(&iter);
> @@ -1153,6 +1166,47 @@ uvideo_vs_parse_desc_format_mjpeg(struct
> }
>
> usbd_status
> +uvideo_vs_parse_desc_format_h264(struct uvideo_softc *sc,
> + const usb_descriptor_t *desc)
> +{
> + struct usb_video_format_h264_desc *d;
> +
> + d = (struct usb_video_format_h264_desc *)(uint8_t *)desc;
> +
> + if (d->bNumFrameDescriptors == 0) {
> + printf("%s: no H.264 frame descriptors available!\n",
> + DEVNAME(sc));
> + return (USBD_INVAL);
> + }
> +
> + if (sc->sc_fmtgrp_idx >= UVIDEO_MAX_FORMAT) {
> + printf("%s: too many format descriptors found!\n", DEVNAME(sc));
> + return (USBD_INVAL);
> + }
> +
> + sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format =
> + (struct uvideo_format_desc *)d;
> + if (d->bDefaultFrameIndex > d->bNumFrameDescriptors ||
> + d->bDefaultFrameIndex < 1) {
> + /* sanitize wrong bDefaultFrameIndex value */
> + sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx = 1;
> + } else {
> + sc->sc_fmtgrp[sc->sc_fmtgrp_idx].format_dfidx =
> + d->bDefaultFrameIndex;
> + }
> + sc->sc_fmtgrp[sc->sc_fmtgrp_idx].pixelformat = V4L2_PIX_FMT_H264;
> +
> + if (sc->sc_fmtgrp_cur == NULL)
> + /* set H.264 format */
> + sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
> +
> + sc->sc_fmtgrp_idx++;
> + sc->sc_fmtgrp_num++;
> +
> + return (USBD_NORMAL_COMPLETION);
> +}
> +
> +usbd_status
> uvideo_vs_parse_desc_format_uncompressed(struct uvideo_softc *sc,
> const usb_descriptor_t *desc)
> {
> @@ -1236,13 +1290,20 @@ uvideo_vs_parse_desc_frame(struct uvideo
> if (desc->bDescriptorType == UDESC_IFACE_ASSOC)
> break;
> if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
> - desc->bLength > sizeof(struct usb_video_frame_desc) &&
> + desc->bLength > UVIDEO_FRAME_MIN_LEN &&
> (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ||
> desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED)) {
> error = uvideo_vs_parse_desc_frame_sub(sc, desc);
> if (error != USBD_NORMAL_COMPLETION)
> return (error);
> }
> + if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
> + desc->bLength > UVIDEO_FRAME_H264_MIN_LEN &&
> + desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) {
> + error = uvideo_vs_parse_desc_frame_h264(sc, desc);
> + if (error != USBD_NORMAL_COMPLETION)
> + return (error);
> + }
> desc = usbd_desc_iter_next(&iter);
> }
>
> @@ -1282,14 +1343,79 @@ uvideo_vs_parse_desc_frame_sub(struct uv
> * Bytes per pixel can vary with compressed formats.
> */
> if (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
> - fbuf_size = UGETW(fd->wWidth) * UGETW(fd->wHeight) *
> + fbuf_size = UGETW(fd->u.d.wWidth) * UGETW(fd->u.d.wHeight) *
> sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel / NBBY;
> DPRINTF(10, "%s: %s: frame buffer size=%d "
> "width=%d height=%d bpp=%d\n", DEVNAME(sc), __func__,
> - fbuf_size, UGETW(fd->wWidth), UGETW(fd->wHeight),
> + fbuf_size, UGETW(fd->u.d.wWidth), UGETW(fd->u.d.wHeight),
> sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel);
> } else
> - fbuf_size = UGETDW(fd->dwMaxVideoFrameBufferSize);
> + fbuf_size = UGETDW(fd->u.d.dwMaxVideoFrameBufferSize);
> +
> + /* store max value */
> + if (fbuf_size > sc->sc_max_fbuf_size)
> + sc->sc_max_fbuf_size = fbuf_size;
> +
> + /*
> + * Increment frame count. If this is the last frame in the
> + * format group, go on to next group.
> + */
> + if (++sc->sc_fmtgrp[fmtidx].frame_num ==
> + sc->sc_fmtgrp[fmtidx].format->bNumFrameDescriptors) {
> + sc->sc_fmtgrp_idx++;
> + }
> +
> + return (USBD_NORMAL_COMPLETION);
> +}
> +
> +usbd_status
> +uvideo_vs_parse_desc_frame_h264(struct uvideo_softc *sc,
> + const usb_descriptor_t *desc)
> +{
> + struct usb_video_frame_desc *fd =
> + (struct usb_video_frame_desc *)(uint8_t *)desc;
> + uint8_t *p;
> + int i, fmtidx, frame_num, length;
> + uint32_t fbuf_size, frame_ival, next_frame_ival;
> +
> + fmtidx = sc->sc_fmtgrp_idx;
> + frame_num = sc->sc_fmtgrp[fmtidx].frame_num;
> + if (frame_num >= UVIDEO_MAX_FRAME) {
> + printf("%s: too many H.264 frame descriptors found!\n",
> + DEVNAME(sc));
> + return (USBD_INVAL);
> + }
> + sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd;
> +
> + if (sc->sc_fmtgrp[fmtidx].frame_cur == NULL ||
> + sc->sc_fmtgrp[fmtidx].format_dfidx == fd->bFrameIndex)
> + sc->sc_fmtgrp[fmtidx].frame_cur = fd;
> +
> + /*
> + * H.264 frame hasn't got dwMaxVideoFrameBufferSize, instead
> + * compute requiers buffer via dwMaxBitRate and dwFrameInterval.
> + */
> +
> + frame_ival = UGETDW(fd->u.h264.dwDefaultFrameInterval);
> +
> + p = (uint8_t *)desc;
> + p += UVIDEO_FRAME_H264_MIN_LEN;
> + length = fd->bLength - UVIDEO_FRAME_H264_MIN_LEN;
> +
> + for (i = 0; i < fd->u.h264.bNumFrameIntervals; i++) {
> + if (length <= 0) {
> + printf("frame descriptor ended early\n");
> + break;
> + }
> + next_frame_ival = UGETDW(p);
> + if (next_frame_ival > frame_ival)
> + frame_ival = next_frame_ival;
> + p += sizeof(uDWord);
> + length -= sizeof(uDWord);
> + }
> +
> + fbuf_size = UGETDW(fd->u.h264.dwMaxBitRate) * frame_ival;
> + fbuf_size /= 8 * 10000000;
>
> /* store max value */
> if (fbuf_size > sc->sc_max_fbuf_size)
> @@ -1574,12 +1700,19 @@ uvideo_find_res(struct uvideo_softc *sc,
> struct uvideo_res *r)
> {
> int i, w, h, diff, diff_best, size_want, size_is;
> + struct usb_video_frame_desc *frame;
>
> size_want = width * height;
>
> for (i = 0; i < sc->sc_fmtgrp[idx].frame_num; i++) {
> - w = UGETW(sc->sc_fmtgrp[idx].frame[i]->wWidth);
> - h = UGETW(sc->sc_fmtgrp[idx].frame[i]->wHeight);
> + frame = sc->sc_fmtgrp[idx].frame[i];
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) {
> + w = UGETW(frame->u.h264.wWidth);
> + h = UGETW(frame->u.h264.wHeight);
> + } else {
> + w = UGETW(frame->u.d.wWidth);
> + h = UGETW(frame->u.d.wHeight);
> + }
> size_is = w * h;
> if (size_is > size_want)
> diff = size_is - size_want;
> @@ -1610,6 +1743,7 @@ uvideo_vs_negotiation(struct uvideo_soft
> uint32_t frame_ival, nivals, min, max, step, diff;
> usbd_status error;
> int i, ival_bytes, changed = 0;
> + size_t len;
>
> pc = (struct usb_video_probe_commit *)probe_data;
>
> @@ -1629,15 +1763,24 @@ uvideo_vs_negotiation(struct uvideo_soft
> pc->bFormatIndex = fmtgrp->format->bFormatIndex;
> pc->bFrameIndex = fmtgrp->frame_cur->bFrameIndex;
> /* dwFrameInterval: 30fps=333333, 15fps=666666, 10fps=1000000 */
> - frame_ival = UGETDW(fmtgrp->frame_cur->dwDefaultFrameInterval);
> + frame = fmtgrp->frame_cur;
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264)
> + frame_ival = UGETDW(frame->u.h264.dwDefaultFrameInterval);
> + else
> + frame_ival = UGETDW(frame->u.d.dwDefaultFrameInterval);
> if (sc->sc_frame_rate != 0) {
> frame_ival = 10000000 / sc->sc_frame_rate;
> /* find closest matching interval the device supports */
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) {
> + len = UVIDEO_FRAME_H264_MIN_LEN;
> + nivals = 0;
> + } else {
> + len = UVIDEO_FRAME_MIN_LEN;
> + nivals = frame->u.d.bFrameIntervalType;
> + }
> p = (uint8_t *)fmtgrp->frame_cur;
> - p += sizeof(struct usb_video_frame_desc);
> - nivals = fmtgrp->frame_cur->bFrameIntervalType;
> - ival_bytes = fmtgrp->frame_cur->bLength -
> - sizeof(struct usb_video_frame_desc);
> + p += len;
> + ival_bytes = frame->bLength - len;
> if (!nivals && (ival_bytes >= sizeof(uDWord) * 3)) {
> /* continuous */
> min = UGETDW(p);
> @@ -1737,12 +1880,12 @@ uvideo_vs_negotiation(struct uvideo_soft
> */
> if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
> USETDW(pc->dwMaxVideoFrameSize,
> - UGETW(frame->wWidth) * UGETW(frame->wHeight) *
> + UGETW(frame->u.d.wWidth) * UGETW(frame->u.d.wHeight) *
> fmtgrp->format->u.uc.bBitsPerPixel / NBBY);
> DPRINTF(1, "fixed dwMaxVideoFrameSize=%d, "
> "width=%d height=%d bpp=%d\n",
> UGETDW(pc->dwMaxVideoFrameSize),
> - UGETW(frame->wWidth), UGETW(frame->wHeight),
> + UGETW(frame->u.d.wWidth), UGETW(frame->u.d.wHeight),
> fmtgrp->format->u.uc.bBitsPerPixel);
> } else {
> /*
> @@ -1754,8 +1897,8 @@ uvideo_vs_negotiation(struct uvideo_soft
> UGETW(hd->bcdUVC) < 0x0110 ) {
> DPRINTF(1, "%s: dwMaxVideoFrameSize == 0, fixed\n",
> DEVNAME(sc));
> - USETDW(pc->dwMaxVideoFrameSize,
> - UGETDW(frame->dwMaxVideoFrameBufferSize));
> + USETDW(pc->dwMaxVideoFrameSize,
> + UGETDW(frame->u.d.dwMaxVideoFrameBufferSize));
> }
> }
>
> @@ -2664,8 +2807,7 @@ uvideo_dump_desc_all(struct uvideo_softc
> case UDESCSUB_VC_PROCESSING_UNIT:
> printf("bDescriptorSubtype=0x%02x",
> desc->bDescriptorSubtype);
> - if (desc->bLength >
> - sizeof(struct usb_video_frame_desc)) {
> + if (desc->bLength > UVIDEO_FRAME_MIN_LEN) {
> printf(" (UDESCSUB_VS_FRAME_"
> "UNCOMPRESSED)\n");
> uvideo_dump_desc_frame(sc, desc);
> @@ -2694,12 +2836,38 @@ uvideo_dump_desc_all(struct uvideo_softc
> printf("bDescriptorSubtype=0x%02x",
> desc->bDescriptorSubtype);
> printf(" (UDESCSUB_VS_FRAME_MJPEG)\n");
> - if (desc->bLength >
> - sizeof(struct usb_video_frame_desc)) {
> + if (desc->bLength > UVIDEO_FRAME_MIN_LEN) {
> printf("|\n");
> uvideo_dump_desc_frame(sc, desc);
> }
> break;
> + case UDESCSUB_VS_FORMAT_H264:
> + printf("bDescriptorSubtype=0x%02x",
> + desc->bDescriptorSubtype);
> + printf(" (UDESCSUB_VS_FORMAT_H264)\n");
> + if (desc->bLength == 52) {
> + printf("|\n");
> + uvideo_dump_desc_format_h264(sc, desc);
> + }
> + break;
> + case UDESCSUB_VS_FRAME_H264:
> + printf("bDescriptorSubtype=0x%02x",
> + desc->bDescriptorSubtype);
> + printf(" (UDESCSUB_VS_FRAME_H264)\n");
> + if (desc->bLength > UVIDEO_FRAME_H264_MIN_LEN) {
> + printf("|\n");
> + uvideo_dump_desc_h264_frame(sc, desc);
> + }
> + break;
> + case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
> + printf("bDescriptorSubtype=0x%02x",
> + desc->bDescriptorSubtype);
> + printf(" (UDESCSUB_VS_FORMAT_H264_SIMULCAST)\n");
> + if (desc->bLength == 52) {
> + printf("|\n");
> + uvideo_dump_desc_format_h264(sc, desc);
> + }
> + break;
> case UDESCSUB_VS_COLORFORMAT:
> printf("bDescriptorSubtype=0x%02x",
> desc->bDescriptorSubtype);
> @@ -2989,6 +3157,7 @@ uvideo_dump_desc_colorformat(struct uvid
> d->bTransferCharacteristics);
> printf("bMatrixCoefficients=0x%02x\n", d->bMatrixCoefficients);
> }
> +
> void
> uvideo_dump_desc_format_mjpeg(struct uvideo_softc *sc,
> const usb_descriptor_t *desc)
> @@ -3023,24 +3192,24 @@ uvideo_dump_desc_frame(struct uvideo_sof
> printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
> printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype);
> printf("bFrameIndex=0x%02x\n", d->bFrameIndex);
> - printf("bmCapabilities=0x%02x\n", d->bmCapabilities);
> - printf("wWidth=%d\n", UGETW(d->wWidth));
> - printf("wHeight=%d\n", UGETW(d->wHeight));
> - printf("dwMinBitRate=%d\n", UGETDW(d->dwMinBitRate));
> - printf("dwMaxBitRate=%d\n", UGETDW(d->dwMaxBitRate));
> + printf("bmCapabilities=0x%02x\n", d->u.d.bmCapabilities);
> + printf("wWidth=%d\n", UGETW(d->u.d.wWidth));
> + printf("wHeight=%d\n", UGETW(d->u.d.wHeight));
> + printf("dwMinBitRate=%d\n", UGETDW(d->u.d.dwMinBitRate));
> + printf("dwMaxBitRate=%d\n", UGETDW(d->u.d.dwMaxBitRate));
> printf("dwMaxVideoFrameBufferSize=%d\n",
> - UGETDW(d->dwMaxVideoFrameBufferSize));
> + UGETDW(d->u.d.dwMaxVideoFrameBufferSize));
> printf("dwDefaultFrameInterval=%d\n",
> - UGETDW(d->dwDefaultFrameInterval));
> - printf("bFrameIntervalType=0x%02x\n", d->bFrameIntervalType);
> + UGETDW(d->u.d.dwDefaultFrameInterval));
> + printf("bFrameIntervalType=0x%02x\n", d->u.d.bFrameIntervalType);
>
> p = (uint8_t *)d;
> - p += sizeof(struct usb_video_frame_desc);
> + p += UVIDEO_FRAME_MIN_LEN;
>
> - if (!d->bFrameIntervalType) {
> + if (!d->u.d.bFrameIntervalType) {
> /* continuous */
> - if (d->bLength < (sizeof(struct usb_video_frame_desc) +
> - sizeof(uDWord) * 3)) {
> + if (d->bLength < UVIDEO_FRAME_MIN_LEN +
> + (sizeof(uDWord) * 3)) {
> printf("invalid frame descriptor length\n");
> } else {
> printf("dwMinFrameInterval = %d\n", UGETDW(p));
> @@ -3052,8 +3221,8 @@ uvideo_dump_desc_frame(struct uvideo_sof
> }
> } else {
> /* discrete */
> - length = d->bLength - sizeof(struct usb_video_frame_desc);
> - for (i = 0; i < d->bFrameIntervalType; i++) {
> + length = d->bLength - UVIDEO_FRAME_MIN_LEN;
> + for (i = 0; i < d->u.d.bFrameIntervalType; i++) {
> if (length <= 0) {
> printf("frame descriptor ended early\n");
> break;
> @@ -3088,6 +3257,108 @@ uvideo_dump_desc_format_uncompressed(str
> }
>
> void
> +uvideo_dump_desc_format_h264(struct uvideo_softc *sc,
> + const usb_descriptor_t *desc)
> +{
> + struct usb_video_format_h264_desc *d;
> +
> + d = (struct usb_video_format_h264_desc *)(uint8_t *)desc;
> +
> + printf("bLength=%d\n", d->bLength);
> + printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
> + printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype);
> + printf("bFormatIndex=0x%02x\n", d->bFormatIndex);
> + printf("bNumFrameDescriptors=0x%02x\n", d->bNumFrameDescriptors);
> + printf("bDefaultFrameIndex=0x%02x\n", d->bDefaultFrameIndex);
> + printf("bMaxCodecConfigDelay=0x%02x\n", d->bMaxCodecConfigDelay);
> + printf("bmSupportedSliceModes=0x%02x\n", d->bmSupportedSliceModes);
> + printf("bmSupportedSyncFrameTypes=0x%02x\n",
> + d->bmSupportedSyncFrameTypes);
> + printf("bmSupportedRateControlModes=0x%02x\n",
> + d->bmSupportedRateControlModes);
> + printf("wMaxMBperSecOneResolutionNoScalability=%d\n",
> + UGETW(d->wMaxMBperSecOneResolutionNoScalability));
> + printf("wMaxMBperSecTwoResolutionsNoScalability=%d\n",
> + UGETW(d->wMaxMBperSecTwoResolutionsNoScalability));
> + printf("wMaxMBperSecOneResolutionTemporalQualityScalability=%d\n",
> + UGETW(d->wMaxMBperSecOneResolutionTemporalQualityScalability));
> + printf("wMaxMBperSecTwoResolutionsTemporalQualityScalability=%d\n",
> + UGETW(d->wMaxMBperSecTwoResolutionsTemporalQualityScalability));
> + printf("wMaxMBperSecThreeResolutionsTemporalQualityScalablity=%d\n",
> + UGETW(d->wMaxMBperSecThreeResolutionsTemporalQualityScalablity));
> + printf("wMaxMBperSecFourResolutionsTemporalQualityScalability=%d\n",
> + UGETW(d->wMaxMBperSecFourResolutionsTemporalQualityScalability));
> + printf("wMaxMBperSecOneResolutionsTemporalSpatialScalability=%d\n",
> + UGETW(d->wMaxMBperSecOneResolutionsTemporalSpatialScalability));
> + printf("wMaxMBperSecTwoResolutionsTemporalSpatialScalability=%d\n",
> + UGETW(d->wMaxMBperSecTwoResolutionsTemporalSpatialScalability));
> + printf("wMaxMBperSecThreeResolutionsTemporalSpatialScalability=%d\n",
> + UGETW(d->wMaxMBperSecThreeResolutionsTemporalSpatialScalability));
> + printf("wMaxMBperSecFourResolutionsTemporalSpatialScalability=%d\n",
> + UGETW(d->wMaxMBperSecFourResolutionsTemporalSpatialScalability));
> + printf("wMaxMBperSecOneResolutionFullScalability=%d\n",
> + UGETW(d->wMaxMBperSecOneResolutionFullScalability));
> + printf("wMaxMBperSecTwoResolutionsFullScalability=%d\n",
> + UGETW(d->wMaxMBperSecTwoResolutionsFullScalability));
> + printf("wMaxMBperSecThreeResolutionsFullScalability=%d\n",
> + UGETW(d->wMaxMBperSecThreeResolutionsFullScalability));
> + printf("wMaxMBperSecFourResolutionsFullScalability=%d\n",
> + UGETW(d->wMaxMBperSecFourResolutionsFullScalability));
> +}
> +
> +void
> +uvideo_dump_desc_h264_frame(struct uvideo_softc *sc,
> + const usb_descriptor_t *desc)
> +{
> + struct usb_video_frame_desc *d;
> + uint8_t *p;
> + int length, i;
> +
> + d = (struct usb_video_frame_desc *)(uint8_t *)desc;
> +
> + printf("bLength=%d\n", d->bLength);
> + printf("bDescriptorType=0x%02x\n", d->bDescriptorType);
> + printf("bDescriptorSubtype=0x%02x\n", d->bDescriptorSubtype);
> + printf("bFrameIndex=0x%02x\n", d->bFrameIndex);
> + printf("wWidth=%d\n", UGETW(d->u.h264.wWidth));
> + printf("wHeight=%d\n", UGETW(d->u.h264.wHeight));
> + printf("wSARwidth=%d\n", UGETW(d->u.h264.wSARwidth));
> + printf("wSARheight=%d\n", UGETW(d->u.h264.wSARheight));
> + printf("wProfile=0x%04x\n", UGETW(d->u.h264.wProfile));
> + printf("bLevelIDC=0x%02x\n", d->u.h264.bLevelIDC);
> + printf("wConstrainedToolset=0x%04d\n",
> + UGETW(d->u.h264.wConstrainedToolset));
> + printf("bmSupportedUsages=0x%08x\n",
> + UGETDW(d->u.h264.bmSupportedUsages));
> + printf("bmCapabilities=0x%04d\n",
> + UGETW(d->u.h264.bmCapabilities));
> + printf("bmSVCCapabilities=0x%08x\n",
> + UGETDW(d->u.h264.bmSVCCapabilities));
> + printf("bmMVCCapabilities=0x%08x\n",
> + UGETDW(d->u.h264.bmMVCCapabilities));
> + printf("dwMinBitRate=%d\n", UGETDW(d->u.h264.dwMinBitRate));
> + printf("dwMaxBitRate=%d\n", UGETDW(d->u.h264.dwMaxBitRate));
> + printf("dwDefaultFrameInterval=%d\n",
> + UGETDW(d->u.h264.dwDefaultFrameInterval));
> + printf("bNumFrameIntervals=0x%02x\n",
> + d->u.h264.bNumFrameIntervals);
> +
> + p = (uint8_t *)d;
> + p += UVIDEO_FRAME_H264_MIN_LEN;;
> +
> + length = d->bLength - UVIDEO_FRAME_H264_MIN_LEN;
> + for (i = 0; i < d->u.h264.bNumFrameIntervals; i++) {
> + if (length <= 0) {
> + printf("frame descriptor ended early\n");
> + break;
> + }
> + printf("dwFrameInterval = %d\n", UGETDW(p));
> + p += sizeof(uDWord);
> + length -= sizeof(uDWord);
> + }
> +}
> +
> +void
> uvideo_dump_desc_processing(struct uvideo_softc *sc,
> const usb_descriptor_t *desc)
> {
> @@ -3198,6 +3469,14 @@ uvideo_enum_fmt(void *v, struct v4l2_fmt
> sizeof(fmtdesc->description));
> bzero(fmtdesc->reserved, sizeof(fmtdesc->reserved));
> break;
> + case UDESCSUB_VS_FORMAT_H264:
> + case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
> + fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
> + (void)strlcpy(fmtdesc->description, "H.264",
> + sizeof(fmtdesc->description));
> + fmtdesc->pixelformat = V4L2_PIX_FMT_H264;
> + bzero(fmtdesc->reserved, sizeof(fmtdesc->reserved));
> + break;
> default:
> fmtdesc->flags = 0;
> (void)strlcpy(fmtdesc->description, "Unknown Format",
> @@ -3215,6 +3494,7 @@ uvideo_enum_fsizes(void *v, struct v4l2_
> {
> struct uvideo_softc *sc = v;
> int idx, found = 0;
> + struct usb_video_frame_desc *frame = NULL;
>
> for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
> if (sc->sc_fmtgrp[idx].pixelformat == fsizes->pixel_format) {
> @@ -3229,10 +3509,14 @@ uvideo_enum_fsizes(void *v, struct v4l2_
> return (EINVAL);
>
> fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> - fsizes->discrete.width =
> - UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wWidth);
> - fsizes->discrete.height =
> - UGETW(sc->sc_fmtgrp[idx].frame[fsizes->index]->wHeight);
> + frame = sc->sc_fmtgrp[idx].frame[fsizes->index];
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) {
> + fsizes->discrete.width = UGETW(frame->u.h264.wWidth);
> + fsizes->discrete.height = UGETW(frame->u.h264.wHeight);
> + } else {
> + fsizes->discrete.width = UGETW(frame->u.d.wWidth);
> + fsizes->discrete.height = UGETW(frame->u.d.wHeight);
> + }
>
> return (0);
> }
> @@ -3245,6 +3529,7 @@ uvideo_enum_fivals(void *v, struct v4l2_
> struct uvideo_format_group *fmtgrp = NULL;
> struct usb_video_frame_desc *frame = NULL;
> uint8_t *p;
> + uint32_t width, height;
>
> for (idx = 0; idx < sc->sc_fmtgrp_num; idx++) {
> if (sc->sc_fmtgrp[idx].pixelformat == fivals->pixel_format) {
> @@ -3256,8 +3541,19 @@ uvideo_enum_fivals(void *v, struct v4l2_
> return (EINVAL);
>
> for (idx = 0; idx < fmtgrp->frame_num; idx++) {
> - if (UGETW(fmtgrp->frame[idx]->wWidth) == fivals->width &&
> - UGETW(fmtgrp->frame[idx]->wHeight) == fivals->height) {
> + switch (fmtgrp->frame[idx]->bDescriptorSubtype) {
> + case UDESCSUB_VS_FRAME_H264:
> + width = UGETW(fmtgrp->frame[idx]->u.h264.wWidth);
> + height = UGETW(fmtgrp->frame[idx]->u.h264.wHeight);
> + break;
> +
> + default:
> + width = UGETW(fmtgrp->frame[idx]->u.d.wWidth);
> + height = UGETW(fmtgrp->frame[idx]->u.d.wHeight);
> + break;
> + }
> +
> + if (width == fivals->width && height == fivals->height) {
> frame = fmtgrp->frame[idx];
> break;
> }
> @@ -3267,9 +3563,13 @@ uvideo_enum_fivals(void *v, struct v4l2_
>
> /* byte-wise pointer to start of frame intervals */
> p = (uint8_t *)frame;
> - p += sizeof(struct usb_video_frame_desc);
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264)
> + p += UVIDEO_FRAME_H264_MIN_LEN;
> + else
> + p += UVIDEO_FRAME_MIN_LEN;
>
> - if (frame->bFrameIntervalType == 0) {
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ||
> + frame->u.d.bFrameIntervalType == 0) {
> if (fivals->index != 0)
> return (EINVAL);
> fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE;
> @@ -3283,7 +3583,7 @@ uvideo_enum_fivals(void *v, struct v4l2_
> fivals->stepwise.step.denominator = 10000000;
> p += sizeof(uDWord);
> } else {
> - if (fivals->index >= frame->bFrameIntervalType)
> + if (fivals->index >= frame->u.d.bFrameIntervalType)
> return (EINVAL);
> p += sizeof(uDWord) * fivals->index;
> if (p > frame->bLength + (uint8_t *)frame) {
> @@ -3370,13 +3670,22 @@ int
> uvideo_g_fmt(void *v, struct v4l2_format *fmt)
> {
> struct uvideo_softc *sc = v;
> + struct usb_video_frame_desc *frame;
>
> if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> return (EINVAL);
>
> fmt->fmt.pix.pixelformat = sc->sc_fmtgrp_cur->pixelformat;
> - fmt->fmt.pix.width = UGETW(sc->sc_fmtgrp_cur->frame_cur->wWidth);
> - fmt->fmt.pix.height = UGETW(sc->sc_fmtgrp_cur->frame_cur->wHeight);
> +
> + frame = sc->sc_fmtgrp_cur->frame_cur;
> + if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) {
> + fmt->fmt.pix.width = UGETW(frame->u.h264.wWidth);
> + fmt->fmt.pix.height = UGETW(frame->u.h264.wHeight);
> + } else {
> + fmt->fmt.pix.width = UGETW(frame->u.d.wWidth);
> + fmt->fmt.pix.height = UGETW(frame->u.d.wHeight);
> + }
> +
> fmt->fmt.pix.sizeimage = UGETDW(sc->sc_desc_probe.dwMaxVideoFrameSize);
>
> if (sc->sc_fmtgrp_cur->has_colorformat) {
> Index: sys/dev/usb/uvideo.h
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/usb/uvideo.h,v
> diff -u -p -r1.65 uvideo.h
> --- sys/dev/usb/uvideo.h 19 Apr 2025 19:35:32 -0000 1.65
> +++ sys/dev/usb/uvideo.h 2 Aug 2025 11:08:37 -0000
> @@ -53,6 +53,9 @@
> #define UDESCSUB_VS_FORMAT_FRAME_BASED 0x10
> #define UDESCSUB_VS_FRAME_FRAME_BASED 0x11
> #define UDESCSUB_VS_FORMAT_STREAM_BASED 0x12
> +#define UDESCSUB_VS_FORMAT_H264 0x13
> +#define UDESCSUB_VS_FRAME_H264 0x14
> +#define UDESCSUB_VS_FORMAT_H264_SIMULCAST 0x15
>
> /* Table A-8: Video Class-Specific Request Codes */
> #define RC_UNDEFINED 0x00
> @@ -437,20 +440,53 @@ struct usb_video_format_mjpeg_desc {
> uByte bCopyProtect;
> } __packed;
>
> -/* Table 3-2: Video Frame Descriptor (same for mjpeg and uncompressed)*/
> struct usb_video_frame_desc {
> uByte bLength;
> uByte bDescriptorType;
> uByte bDescriptorSubtype;
> uByte bFrameIndex;
> - uByte bmCapabilities;
> - uWord wWidth;
> - uWord wHeight;
> - uDWord dwMinBitRate;
> - uDWord dwMaxBitRate;
> - uDWord dwMaxVideoFrameBufferSize;
> - uDWord dwDefaultFrameInterval;
> - uByte bFrameIntervalType;
> + union {
> + /*
> + * Table 3-2: Video Frame Descriptor
> + * (same for mjpeg and uncompressed)
> + */
> + struct {
> + uByte bmCapabilities;
> + uWord wWidth;
> + uWord wHeight;
> + uDWord dwMinBitRate;
> + uDWord dwMaxBitRate;
> + uDWord dwMaxVideoFrameBufferSize;
> + uDWord dwDefaultFrameInterval;
> + uByte bFrameIntervalType;
> + } d;
> +#define UVIDEO_FRAME_MIN_LEN \
> + (offsetof(struct usb_video_frame_desc, u.d) \
> + + sizeof(((struct usb_video_frame_desc *)0)->u.d))
> +
> + /* Table 3-2: H.264 Payload Video Frame Descriptor */
> + struct {
> + uWord wWidth;
> + uWord wHeight;
> + uWord wSARwidth;
> + uWord wSARheight;
> + uWord wProfile;
> + uByte bLevelIDC;
> + uWord wConstrainedToolset;
> + uDWord bmSupportedUsages;
> + uWord bmCapabilities;
> + uDWord bmSVCCapabilities;
> + uDWord bmMVCCapabilities;
> + uDWord dwMinBitRate;
> + uDWord dwMaxBitRate;
> + uDWord dwDefaultFrameInterval;
> + uByte bNumFrameIntervals;
> + } h264;
> +#define UVIDEO_FRAME_H264_MIN_LEN \
> + (offsetof(struct usb_video_frame_desc, u.h264) \
> + + sizeof(((struct usb_video_frame_desc *)0)->u.h264))
> +
> + } u;
> /* uDWord ivals[]; frame intervals, length varies */
> } __packed;
>
> @@ -473,6 +509,36 @@ struct usb_video_format_uncompressed_des
> uByte bCopyProtect;
> } __packed;
>
> +/* Table 3-1: H.264 Payload Video Format Descriptor */
> +struct usb_video_format_h264_desc {
> + uByte bLength;
> + uByte bDescriptorType;
> + uByte bDescriptorSubtype;
> + uByte bFormatIndex;
> + uByte bNumFrameDescriptors;
> + uByte bDefaultFrameIndex;
> + uByte bMaxCodecConfigDelay;
> + uByte bmSupportedSliceModes;
> + uByte bmSupportedSyncFrameTypes;
> + uByte bResolutionScaling;
> + uByte _reserved1;
> + uByte bmSupportedRateControlModes;
> + uWord wMaxMBperSecOneResolutionNoScalability;
> + uWord wMaxMBperSecTwoResolutionsNoScalability;
> + uWord wMaxMBperSecOneResolutionTemporalQualityScalability;
> + uWord wMaxMBperSecTwoResolutionsTemporalQualityScalability;
> + uWord wMaxMBperSecThreeResolutionsTemporalQualityScalablity;
> + uWord wMaxMBperSecFourResolutionsTemporalQualityScalability;
> + uWord wMaxMBperSecOneResolutionsTemporalSpatialScalability;
> + uWord wMaxMBperSecTwoResolutionsTemporalSpatialScalability;
> + uWord wMaxMBperSecThreeResolutionsTemporalSpatialScalability;
> + uWord wMaxMBperSecFourResolutionsTemporalSpatialScalability;
> + uWord wMaxMBperSecOneResolutionFullScalability;
> + uWord wMaxMBperSecTwoResolutionsFullScalability;
> + uWord wMaxMBperSecThreeResolutionsFullScalability;
> + uWord wMaxMBperSecFourResolutionsFullScalability;
> +} __packed;
> +
> /*
> * Driver specific private definitions.
> */
> @@ -503,6 +569,31 @@ struct uvideo_format_desc {
> uByte bmInterlaceFlags;
> uByte bCopyProtect;
> } uc;
> +
> + /* h264 */
> + struct {
> + uByte bDefaultFrameIndex;
> + uByte bMaxCodecConfigDelay;
> + uByte bmSupportedSliceModes;
> + uByte bmSupportedSyncFrameTypes;
> + uByte bResolutionScaling;
> + uByte _reserved1;
> + uByte bmSupportedRateControlModes;
> + uDWord wMaxMBperSecOneResolutionNoScalability;
> + uDWord wMaxMBperSecTwoResolutionsNoScalability;
> + uDWord wMaxMBperSecOneResolutionTemporalQualityScalability;
> + uDWord wMaxMBperSecTwoResolutionsTemporalQualityScalability;
> + uDWord wMaxMBperSecThreeResolutionsTemporalQualityScalablity;
> + uDWord wMaxMBperSecFourResolutionsTemporalQualityScalability;
> + uDWord wMaxMBperSecOneResolutionsTemporalSpatialScalability;
> + uDWord wMaxMBperSecTwoResolutionsTemporalSpatialScalability;
> + uDWord wMaxMBperSecThreeResolutionsTemporalSpatialScalability;
> + uDWord wMaxMBperSecFourResolutionsTemporalSpatialScalability;
> + uDWord wMaxMBperSecOneResolutionFullScalability;
> + uDWord wMaxMBperSecTwoResolutionsFullScalability;
> + uDWord wMaxMBperSecThreeResolutionsFullScalability;
> + uDWord wMaxMBperSecFourResolutionsFullScalability;
> + } h264;
> } u;
> } __packed;
sys/uvideo: support H.264 format and frame