Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: sys/uvideo: support H.264 format and frame
To:
tech@openbsd.org, mglocker@openbsd.org
Date:
Sat, 02 Aug 2025 13:18:19 +0200

Download raw body.

Thread
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?

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;