Download raw body.
sys/uvideo: support Frame Based format and frame
tech@,
here the last bit which requires to fully support Elgato Facecam Pro.
Frame Based format and streams.
Tested on
- Elgato Facecam Pro
- Jabra PanaCast 20
- Azurewave, USB camera
No regression discovered.
Ok?
Index: sys/dev/usb/uvideo.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uvideo.c,v
diff -u -p -r1.259 uvideo.c
--- sys/dev/usb/uvideo.c 3 Aug 2025 08:39:00 -0000 1.259
+++ sys/dev/usb/uvideo.c 3 Aug 2025 09:10:59 -0000
@@ -132,6 +132,8 @@ 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_frame_based(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 *,
@@ -139,9 +141,9 @@ usbd_status uvideo_vs_parse_desc_format_
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 *,
+usbd_status uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc *,
const usb_descriptor_t *);
-usbd_status uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *,
+usbd_status uvideo_vs_parse_desc_frame_max_rate(struct uvideo_softc *,
const usb_descriptor_t *);
uint32_t uvideo_vc_parse_max_packet_size(struct uvideo_softc *,
usb_endpoint_descriptor_t *);
@@ -216,10 +218,14 @@ void uvideo_dump_desc_format_uncompress
const usb_descriptor_t *);
void uvideo_dump_desc_format_h264(struct uvideo_softc *,
const usb_descriptor_t *);
+void uvideo_dump_desc_format_frame_based(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_frame_based_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 *,
@@ -746,7 +752,7 @@ uvideo_vc_parse_desc(struct uvideo_softc
break;
case UDESCSUB_VC_PROCESSING_UNIT:
/* XXX do correct length calculation */
- if (desc->bLength < UVIDEO_FRAME_MIN_LEN)
+ if (desc->bLength < UVIDEO_FRAME_MIN_LEN(desc))
(void)uvideo_vc_parse_desc_pu(sc, desc);
break;
@@ -1064,6 +1070,12 @@ uvideo_vs_parse_desc_format(struct uvide
sc, desc);
}
break;
+ case UDESCSUB_VS_FORMAT_FRAME_BASED:
+ if (desc->bLength == 28) {
+ (void)uvideo_vs_parse_desc_format_frame_based(
+ sc, desc);
+ }
+ break;
case UDESCSUB_VS_FORMAT_H264:
case UDESCSUB_VS_FORMAT_H264_SIMULCAST:
if (desc->bLength == 52) {
@@ -1174,7 +1186,7 @@ uvideo_vs_parse_desc_format_h264(struct
d = (struct usb_video_format_h264_desc *)(uint8_t *)desc;
if (d->bNumFrameDescriptors == 0) {
- printf("%s: no H.264 frame descriptors available!\n",
+ printf("%s: no H264 frame descriptors available!\n",
DEVNAME(sc));
return (USBD_INVAL);
}
@@ -1197,7 +1209,65 @@ uvideo_vs_parse_desc_format_h264(struct
sc->sc_fmtgrp[sc->sc_fmtgrp_idx].pixelformat = V4L2_PIX_FMT_H264;
if (sc->sc_fmtgrp_cur == NULL)
- /* set H.264 format */
+ /* set H264 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_frame_based(struct uvideo_softc *sc,
+ const usb_descriptor_t *desc)
+{
+ struct usb_video_format_frame_based_desc *d;
+ int i, j, nent;
+
+ d = (struct usb_video_format_frame_based_desc *)(uint8_t *)desc;
+
+ if (d->bNumFrameDescriptors == 0) {
+ printf("%s: no Frame Based 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;
+ }
+
+ i = sc->sc_fmtgrp_idx;
+
+ /* map GUID to pixel format if a matching entry is found */
+ for (j = 0, nent = nitems(uvideo_map_fmts); j < nent; j++) {
+ if (!memcmp(sc->sc_fmtgrp[i].format->u.uc.guidFormat,
+ uvideo_map_fmts[j].guidFormat, 16)) {
+ sc->sc_fmtgrp[i].pixelformat =
+ uvideo_map_fmts[j].pixelformat;
+ break;
+ }
+ }
+ /* default to using GUID start as the pixel format */
+ if (j == nent)
+ memcpy(&sc->sc_fmtgrp[i].pixelformat,
+ sc->sc_fmtgrp[i].format->u.uc.guidFormat,
+ sizeof(uint32_t));
+
+ if (sc->sc_fmtgrp_cur == NULL)
+ /* set Frame Based format */
sc->sc_fmtgrp_cur = &sc->sc_fmtgrp[sc->sc_fmtgrp_idx];
sc->sc_fmtgrp_idx++;
@@ -1290,17 +1360,18 @@ uvideo_vs_parse_desc_frame(struct uvideo
if (desc->bDescriptorType == UDESC_IFACE_ASSOC)
break;
if (desc->bDescriptorType == UDESC_CS_INTERFACE &&
- desc->bLength > UVIDEO_FRAME_MIN_LEN &&
+ desc->bLength > UVIDEO_FRAME_MIN_LEN(desc) &&
(desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_MJPEG ||
desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED)) {
- error = uvideo_vs_parse_desc_frame_sub(sc, desc);
+ error = uvideo_vs_parse_desc_frame_buffer_size(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);
+ desc->bLength > UVIDEO_FRAME_MIN_LEN(desc) &&
+ (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ||
+ desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_FRAME_BASED)) {
+ error = uvideo_vs_parse_desc_frame_max_rate(sc, desc);
if (error != USBD_NORMAL_COMPLETION)
return (error);
}
@@ -1311,7 +1382,7 @@ uvideo_vs_parse_desc_frame(struct uvideo
}
usbd_status
-uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *sc,
+uvideo_vs_parse_desc_frame_buffer_size(struct uvideo_softc *sc,
const usb_descriptor_t *desc)
{
struct usb_video_frame_desc *fd =
@@ -1343,14 +1414,14 @@ 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->u.d.wWidth) * UGETW(fd->u.d.wHeight) *
+ fbuf_size = UGETW(fd->u.uc.wWidth) * UGETW(fd->u.uc.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->u.d.wWidth), UGETW(fd->u.d.wHeight),
+ fbuf_size, UGETW(fd->u.uc.wWidth), UGETW(fd->u.uc.wHeight),
sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel);
} else
- fbuf_size = UGETDW(fd->u.d.dwMaxVideoFrameBufferSize);
+ fbuf_size = UGETDW(fd->u.uc.dwMaxVideoFrameBufferSize);
/* store max value */
if (fbuf_size > sc->sc_max_fbuf_size)
@@ -1369,20 +1440,22 @@ uvideo_vs_parse_desc_frame_sub(struct uv
}
usbd_status
-uvideo_vs_parse_desc_frame_h264(struct uvideo_softc *sc,
+uvideo_vs_parse_desc_frame_max_rate(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;
+ int i, fmtidx, frame_num, length, nivals;
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));
+ printf("%s: too many %s frame descriptors found!\n",
+ DEVNAME(sc),
+ desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ?
+ "H264" : "FRAME BASED");
return (USBD_INVAL);
}
sc->sc_fmtgrp[fmtidx].frame[frame_num] = fd;
@@ -1392,17 +1465,19 @@ uvideo_vs_parse_desc_frame_h264(struct u
sc->sc_fmtgrp[fmtidx].frame_cur = fd;
/*
- * H.264 frame hasn't got dwMaxVideoFrameBufferSize, instead
- * compute requiers buffer via dwMaxBitRate and dwFrameInterval.
+ * Frame Based and H264 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;
+ p = (uint8_t *)desc + UVIDEO_FRAME_MIN_LEN(fd);
+ length = fd->bLength - UVIDEO_FRAME_MIN_LEN(fd);
+
+ nivals = UVIDEO_FRAME_NUM_INTERVALS(fd);
- for (i = 0; i < fd->u.h264.bNumFrameIntervals; i++) {
+ for (i = 0; i < nivals; i++) {
if (length <= 0) {
printf("frame descriptor ended early\n");
break;
@@ -1414,7 +1489,7 @@ uvideo_vs_parse_desc_frame_h264(struct u
length -= sizeof(uDWord);
}
- fbuf_size = UGETDW(fd->u.h264.dwMaxBitRate) * frame_ival;
+ fbuf_size = UGETDW(UVIDEO_FRAME_FIELD(fd, dwMaxBitRate)) * frame_ival;
fbuf_size /= 8 * 10000000;
/* store max value */
@@ -1706,13 +1781,8 @@ uvideo_find_res(struct uvideo_softc *sc,
for (i = 0; i < sc->sc_fmtgrp[idx].frame_num; i++) {
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);
- }
+ w = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
+ h = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
size_is = w * h;
if (size_is > size_want)
diff = size_is - size_want;
@@ -1764,20 +1834,12 @@ uvideo_vs_negotiation(struct uvideo_soft
pc->bFrameIndex = fmtgrp->frame_cur->bFrameIndex;
/* dwFrameInterval: 30fps=333333, 15fps=666666, 10fps=1000000 */
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);
+ frame_ival = UGETDW(UVIDEO_FRAME_FIELD(frame, 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;
- }
+ len = UVIDEO_FRAME_MIN_LEN(frame);
+ nivals = UVIDEO_FRAME_NUM_INTERVALS(frame);
p = (uint8_t *)fmtgrp->frame_cur;
p += len;
ival_bytes = frame->bLength - len;
@@ -1880,12 +1942,12 @@ uvideo_vs_negotiation(struct uvideo_soft
*/
if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
USETDW(pc->dwMaxVideoFrameSize,
- UGETW(frame->u.d.wWidth) * UGETW(frame->u.d.wHeight) *
+ UGETW(frame->u.uc.wWidth) * UGETW(frame->u.uc.wHeight) *
fmtgrp->format->u.uc.bBitsPerPixel / NBBY);
DPRINTF(1, "fixed dwMaxVideoFrameSize=%d, "
"width=%d height=%d bpp=%d\n",
UGETDW(pc->dwMaxVideoFrameSize),
- UGETW(frame->u.d.wWidth), UGETW(frame->u.d.wHeight),
+ UGETW(frame->u.uc.wWidth), UGETW(frame->u.uc.wHeight),
fmtgrp->format->u.uc.bBitsPerPixel);
} else {
/*
@@ -1898,7 +1960,7 @@ uvideo_vs_negotiation(struct uvideo_soft
DPRINTF(1, "%s: dwMaxVideoFrameSize == 0, fixed\n",
DEVNAME(sc));
USETDW(pc->dwMaxVideoFrameSize,
- UGETDW(frame->u.d.dwMaxVideoFrameBufferSize));
+ UGETDW(frame->u.uc.dwMaxVideoFrameBufferSize));
}
}
@@ -2807,7 +2869,7 @@ uvideo_dump_desc_all(struct uvideo_softc
case UDESCSUB_VC_PROCESSING_UNIT:
printf("bDescriptorSubtype=0x%02x",
desc->bDescriptorSubtype);
- if (desc->bLength > UVIDEO_FRAME_MIN_LEN) {
+ if (desc->bLength > UVIDEO_FRAME_MIN_LEN(desc)) {
printf(" (UDESCSUB_VS_FRAME_"
"UNCOMPRESSED)\n");
uvideo_dump_desc_frame(sc, desc);
@@ -2836,11 +2898,29 @@ uvideo_dump_desc_all(struct uvideo_softc
printf("bDescriptorSubtype=0x%02x",
desc->bDescriptorSubtype);
printf(" (UDESCSUB_VS_FRAME_MJPEG)\n");
- if (desc->bLength > UVIDEO_FRAME_MIN_LEN) {
+ if (desc->bLength > UVIDEO_FRAME_MIN_LEN(desc)) {
printf("|\n");
uvideo_dump_desc_frame(sc, desc);
}
break;
+ case UDESCSUB_VS_FORMAT_FRAME_BASED:
+ printf("bDescriptorSubtype=0x%02x",
+ desc->bDescriptorSubtype);
+ printf(" (UDESCSUB_VS_FORMAT_FRAME_BASED)\n");
+ if (desc->bLength == 28) {
+ printf("|\n");
+ uvideo_dump_desc_format_frame_based(sc, desc);
+ }
+ break;
+ case UDESCSUB_VS_FRAME_FRAME_BASED:
+ printf("bDescriptorSubtype=0x%02x",
+ desc->bDescriptorSubtype);
+ printf(" (UDESCSUB_VS_FRAME_FRAME_BASED)\n");
+ if (desc->bLength > UVIDEO_FRAME_MIN_LEN(desc)) {
+ printf("|\n");
+ uvideo_dump_desc_frame_based_frame(sc, desc);
+ }
+ break;
case UDESCSUB_VS_FORMAT_H264:
printf("bDescriptorSubtype=0x%02x",
desc->bDescriptorSubtype);
@@ -2854,7 +2934,7 @@ uvideo_dump_desc_all(struct uvideo_softc
printf("bDescriptorSubtype=0x%02x",
desc->bDescriptorSubtype);
printf(" (UDESCSUB_VS_FRAME_H264)\n");
- if (desc->bLength > UVIDEO_FRAME_H264_MIN_LEN) {
+ if (desc->bLength > UVIDEO_FRAME_MIN_LEN(desc)) {
printf("|\n");
uvideo_dump_desc_h264_frame(sc, desc);
}
@@ -3192,23 +3272,22 @@ 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->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("bmCapabilities=0x%02x\n", d->u.uc.bmCapabilities);
+ printf("wWidth=%d\n", UGETW(d->u.uc.wWidth));
+ printf("wHeight=%d\n", UGETW(d->u.uc.wHeight));
+ printf("dwMinBitRate=%d\n", UGETDW(d->u.uc.dwMinBitRate));
+ printf("dwMaxBitRate=%d\n", UGETDW(d->u.uc.dwMaxBitRate));
printf("dwMaxVideoFrameBufferSize=%d\n",
- UGETDW(d->u.d.dwMaxVideoFrameBufferSize));
+ UGETDW(d->u.uc.dwMaxVideoFrameBufferSize));
printf("dwDefaultFrameInterval=%d\n",
- UGETDW(d->u.d.dwDefaultFrameInterval));
- printf("bFrameIntervalType=0x%02x\n", d->u.d.bFrameIntervalType);
+ UGETDW(d->u.uc.dwDefaultFrameInterval));
+ printf("bFrameIntervalType=0x%02x\n", d->u.uc.bFrameIntervalType);
- p = (uint8_t *)d;
- p += UVIDEO_FRAME_MIN_LEN;
+ p = (uint8_t *)d + UVIDEO_FRAME_MIN_LEN(d);
- if (!d->u.d.bFrameIntervalType) {
+ if (!d->u.uc.bFrameIntervalType) {
/* continuous */
- if (d->bLength < UVIDEO_FRAME_MIN_LEN +
+ if (d->bLength < UVIDEO_FRAME_MIN_LEN(d) +
(sizeof(uDWord) * 3)) {
printf("invalid frame descriptor length\n");
} else {
@@ -3221,8 +3300,8 @@ uvideo_dump_desc_frame(struct uvideo_sof
}
} else {
/* discrete */
- length = d->bLength - UVIDEO_FRAME_MIN_LEN;
- for (i = 0; i < d->u.d.bFrameIntervalType; i++) {
+ length = d->bLength - UVIDEO_FRAME_MIN_LEN(d);
+ for (i = 0; i < d->u.uc.bFrameIntervalType; i++) {
if (length <= 0) {
printf("frame descriptor ended early\n");
break;
@@ -3257,6 +3336,83 @@ uvideo_dump_desc_format_uncompressed(str
}
void
+uvideo_dump_desc_format_frame_based(struct uvideo_softc *sc,
+ const usb_descriptor_t *desc)
+{
+ struct usb_video_format_frame_based_desc *d;
+
+ d = (struct usb_video_format_frame_based_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("guidFormat=%s\n", d->guidFormat);
+ printf("bBitsPerPixel=0x%02x\n", d->bBitsPerPixel);
+ printf("bDefaultFrameIndex=0x%02x\n", d->bDefaultFrameIndex);
+ printf("bAspectRatioX=0x%02x\n", d->bAspectRatioX);
+ printf("bAspectRatioY=0x%02x\n", d->bAspectRatioY);
+ printf("bmInterlaceFlags=0x%02x\n", d->bmInterlaceFlags);
+ printf("bCopyProtect=0x%02x\n", d->bCopyProtect);
+ printf("bVariableSize=0x%02x\n", d->bVariableSize);
+}
+
+void
+uvideo_dump_desc_frame_based_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("bmCapabilities=0x%02x\n", d->u.fb.bmCapabilities);
+ printf("wWidth=%d\n", UGETW(d->u.fb.wWidth));
+ printf("wHeight=%d\n", UGETW(d->u.fb.wHeight));
+ printf("dwMinBitRate=%d\n", UGETDW(d->u.fb.dwMinBitRate));
+ printf("dwMaxBitRate=%d\n", UGETDW(d->u.fb.dwMaxBitRate));
+ printf("dwDefaultFrameInterval=%d\n",
+ UGETDW(d->u.fb.dwDefaultFrameInterval));
+ printf("bFrameIntervalType=0x%02x\n", d->u.fb.bFrameIntervalType);
+ printf("dwBytesPerLine=%d\n",
+ UGETDW(d->u.fb.dwBytesPerLine));
+
+ p = (uint8_t *)d + UVIDEO_FRAME_MIN_LEN(d);
+
+ if (!d->u.uc.bFrameIntervalType) {
+ /* continuous */
+ if (d->bLength < UVIDEO_FRAME_MIN_LEN(d) +
+ (sizeof(uDWord) * 3)) {
+ printf("invalid frame descriptor length\n");
+ } else {
+ printf("dwMinFrameInterval = %d\n", UGETDW(p));
+ p += sizeof(uDWord);
+ printf("dwMaxFrameInterval = %d\n", UGETDW(p));
+ p += sizeof(uDWord);
+ printf("dwFrameIntervalStep = %d\n", UGETDW(p));
+ p += sizeof(uDWord);
+ }
+ } else {
+ /* discrete */
+ length = d->bLength - UVIDEO_FRAME_MIN_LEN(d);
+ for (i = 0; i < d->u.uc.bFrameIntervalType; 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_format_h264(struct uvideo_softc *sc,
const usb_descriptor_t *desc)
{
@@ -3343,10 +3499,9 @@ uvideo_dump_desc_h264_frame(struct uvide
printf("bNumFrameIntervals=0x%02x\n",
d->u.h264.bNumFrameIntervals);
- p = (uint8_t *)d;
- p += UVIDEO_FRAME_H264_MIN_LEN;;
+ p = (uint8_t *)d + UVIDEO_FRAME_MIN_LEN(d);
- length = d->bLength - UVIDEO_FRAME_H264_MIN_LEN;
+ length = d->bLength - UVIDEO_FRAME_MIN_LEN(d);
for (i = 0; i < d->u.h264.bNumFrameIntervals; i++) {
if (length <= 0) {
printf("frame descriptor ended early\n");
@@ -3469,10 +3624,21 @@ uvideo_enum_fmt(void *v, struct v4l2_fmt
sizeof(fmtdesc->description));
bzero(fmtdesc->reserved, sizeof(fmtdesc->reserved));
break;
+ case UDESCSUB_VS_FORMAT_FRAME_BASED:
+ if (sc->sc_fmtgrp[idx].format->u.fb.bVariableSize)
+ fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+ else
+ fmtdesc->flags = 0;
+ fmtdesc->pixelformat = sc->sc_fmtgrp[idx].pixelformat;
+ (void)strlcpy(fmtdesc->description,
+ (char *) &fmtdesc->pixelformat,
+ 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",
+ (void)strlcpy(fmtdesc->description, "H264",
sizeof(fmtdesc->description));
fmtdesc->pixelformat = V4L2_PIX_FMT_H264;
bzero(fmtdesc->reserved, sizeof(fmtdesc->reserved));
@@ -3510,13 +3676,8 @@ uvideo_enum_fsizes(void *v, struct v4l2_
fsizes->type = V4L2_FRMSIZE_TYPE_DISCRETE;
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);
- }
+ fsizes->discrete.width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
+ fsizes->discrete.height = UGETW(UVIDEO_FRAME_FIELD(frame, wHeight));
return (0);
}
@@ -3541,17 +3702,8 @@ uvideo_enum_fivals(void *v, struct v4l2_
return (EINVAL);
for (idx = 0; idx < fmtgrp->frame_num; idx++) {
- 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;
- }
+ width = UGETW(UVIDEO_FRAME_FIELD(fmtgrp->frame[idx], wWidth));
+ height = UGETW(UVIDEO_FRAME_FIELD(fmtgrp->frame[idx], wHeight));
if (width == fivals->width && height == fivals->height) {
frame = fmtgrp->frame[idx];
@@ -3562,14 +3714,9 @@ uvideo_enum_fivals(void *v, struct v4l2_
return (EINVAL);
/* byte-wise pointer to start of frame intervals */
- p = (uint8_t *)frame;
- if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264)
- p += UVIDEO_FRAME_H264_MIN_LEN;
- else
- p += UVIDEO_FRAME_MIN_LEN;
+ p = (uint8_t *)frame + UVIDEO_FRAME_MIN_LEN(frame);
- if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264 ||
- frame->u.d.bFrameIntervalType == 0) {
+ if (UVIDEO_FRAME_NUM_INTERVALS(frame) == 0) {
if (fivals->index != 0)
return (EINVAL);
fivals->type = V4L2_FRMIVAL_TYPE_STEPWISE;
@@ -3583,7 +3730,7 @@ uvideo_enum_fivals(void *v, struct v4l2_
fivals->stepwise.step.denominator = 10000000;
p += sizeof(uDWord);
} else {
- if (fivals->index >= frame->u.d.bFrameIntervalType)
+ if (fivals->index >= UVIDEO_FRAME_NUM_INTERVALS(frame))
return (EINVAL);
p += sizeof(uDWord) * fivals->index;
if (p > frame->bLength + (uint8_t *)frame) {
@@ -3678,14 +3825,8 @@ uvideo_g_fmt(void *v, struct v4l2_format
fmt->fmt.pix.pixelformat = sc->sc_fmtgrp_cur->pixelformat;
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.width = UGETW(UVIDEO_FRAME_FIELD(frame, wWidth));
+ fmt->fmt.pix.height = UGETW(UVIDEO_FRAME_FIELD(frame, 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: /cvs/src/sys/dev/usb/uvideo.h,v
diff -u -p -r1.66 uvideo.h
--- sys/dev/usb/uvideo.h 3 Aug 2025 08:39:01 -0000 1.66
+++ sys/dev/usb/uvideo.h 3 Aug 2025 09:10:59 -0000
@@ -459,10 +459,20 @@ struct usb_video_frame_desc {
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))
+ } uc;
+
+ /*
+ * Table 3-2 Frame Based Payload Video Frame Descriptors */
+ struct {
+ uByte bmCapabilities;
+ uWord wWidth;
+ uWord wHeight;
+ uDWord dwMinBitRate;
+ uDWord dwMaxBitRate;
+ uDWord dwDefaultFrameInterval;
+ uByte bFrameIntervalType;
+ uDWord dwBytesPerLine;
+ } fb;
/* Table 3-2: H.264 Payload Video Frame Descriptor */
struct {
@@ -482,11 +492,38 @@ struct usb_video_frame_desc {
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;
+
+#define UVIDEO_FRAME_MIN_LEN(frm) \
+ (offsetof(struct usb_video_frame_desc, u) + \
+ ( \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) ? \
+ sizeof(((struct usb_video_frame_desc *)0)->u.h264) : \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_FRAME_BASED) ? \
+ sizeof(((struct usb_video_frame_desc *)0)->u.fb) : \
+ sizeof(((struct usb_video_frame_desc *)0)->u.uc) \
+ ) \
+ )
+
+#define UVIDEO_FRAME_FIELD(frm, field) \
+ ( \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) ? \
+ (frm)->u.h264.field : \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_FRAME_BASED) ? \
+ (frm)->u.fb.field : \
+ (frm)->u.uc.field \
+ )
+
+#define UVIDEO_FRAME_NUM_INTERVALS(frm) \
+ ( \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_H264) ? \
+ (frm)->u.h264.bNumFrameIntervals : \
+ ((frm)->bDescriptorSubtype == UDESCSUB_VS_FRAME_FRAME_BASED) ? \
+ (frm)->u.fb.bFrameIntervalType : \
+ (frm)->u.uc.bFrameIntervalType \
+ )
+
/* uDWord ivals[]; frame intervals, length varies */
} __packed;
@@ -539,6 +576,23 @@ struct usb_video_format_h264_desc {
uWord wMaxMBperSecFourResolutionsFullScalability;
} __packed;
+/* Table 3-1: Frame Based Payload Video Format Descriptor */
+struct usb_video_format_frame_based_desc {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bFormatIndex;
+ uByte bNumFrameDescriptors;
+ uByte guidFormat[16];
+ uByte bBitsPerPixel;
+ uByte bDefaultFrameIndex;
+ uByte bAspectRatioX;
+ uByte bAspectRatioY;
+ uByte bmInterlaceFlags;
+ uByte bCopyProtect;
+ uByte bVariableSize;
+} __packed;
+
/*
* Driver specific private definitions.
*/
@@ -569,6 +623,18 @@ struct uvideo_format_desc {
uByte bmInterlaceFlags;
uByte bCopyProtect;
} uc;
+
+ /* frame based */
+ struct {
+ uByte guidFormat[16];
+ uByte bBitsPerPixel;
+ uByte bDefaultFrameIndex;
+ uByte bAspectRatioX;
+ uByte bAspectRatioY;
+ uByte bmInterlaceFlags;
+ uByte bCopyProtect;
+ uByte bVariableSize;
+ } fb;
/* h264 */
struct {
sys/uvideo: support Frame Based format and frame