From: Kirill A. Korinsky 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 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? 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;