From: Marcus Glocker Subject: Re: sys/uvideo: support H.264 format and frame To: "Kirill A. Korinsky" Cc: tech@openbsd.org Date: Sun, 3 Aug 2025 10:03:17 +0200 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 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;