Download raw body.
sys/uvideo: support H.264 format and frame
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.
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 1 Aug 2025 22:10:52 -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,48 @@ 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)
+ */
+#define UVIDEO_FRAME_MIN_LEN 26
+ struct {
+ uByte bmCapabilities;
+ uWord wWidth;
+ uWord wHeight;
+ uDWord dwMinBitRate;
+ uDWord dwMaxBitRate;
+ uDWord dwMaxVideoFrameBufferSize;
+ uDWord dwDefaultFrameInterval;
+ uByte bFrameIntervalType;
+ } d;
+
+ /* Table 3-2: H.264 Payload Video Frame Descriptor */
+#define UVIDEO_FRAME_H264_MIN_LEN 44
+ 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;
+ } u;
/* uDWord ivals[]; frame intervals, length varies */
} __packed;
@@ -473,6 +504,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 +564,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