Hi Harri,
here is a slightly revised patch. Could you please test it with your
hardware ?
I'm a bit concerned about the MPEG format, which should be stream-based and
not frame-based. Beside, the GUID doesn't seem to be defined anywhere, and is
quite ambiguous. Could you please give me more information about the device
you are targetting ?
Best regards,
Laurent Pinchart
Index: uvc_driver.c
===================================================================
--- uvc_driver.c (revision 111)
+++ uvc_driver.c (working copy)
@@ -54,13 +54,47 @@
static struct uvc_format_desc uvc_fmts[] = {
{
+ .name = "YUV 4:2:2 (YUYV)",
.guid = UVC_GUID_FORMAT_YUY2,
.fcc = V4L2_PIX_FMT_YUYV,
+ .flags = 0,
},
{
+ .name = "YUV 4:2:0 (NV12)",
.guid = UVC_GUID_FORMAT_NV12,
.fcc = V4L2_PIX_FMT_NV12,
+ .flags = 0,
},
+ {
+ .name = "MJPEG",
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ .flags = UVC_FMT_FLAG_COMPRESSED,
+ },
+ {
+ .name = "YVU 4:2:0 (YV12)",
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ .flags = 0,
+ },
+ {
+ .name = "YUV 4:2:0 (I420)",
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ .flags = 0,
+ },
+ {
+ .name = "YUV 4:2:2 (UYVY)",
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ .flags = 0,
+ },
+ {
+ .name = "MPEG",
+ .guid = UVC_GUID_FORMAT_MPEG,
+ .fcc = V4L2_PIX_FMT_MPEG,
+ .flags = UVC_FMT_FLAG_COMPRESSED,
+ },
};
#if 0
@@ -104,17 +138,17 @@
return NULL;
}
-static __u32 uvc_guid_to_fcc(const __u8 guid[16])
+static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
{
unsigned int len = ARRAY_SIZE(uvc_fmts);
unsigned int i;
for (i = 0; i < len; ++i) {
if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
- return uvc_fmts[i].fcc;
+ return &uvc_fmts[i];
}
- return -1;
+ return NULL;
}
static __u32 uvc_colorspace(const __u8 primaries)
@@ -277,6 +311,7 @@
{
struct usb_interface *intf = streaming->intf;
struct usb_host_interface *alts = intf->cur_altsetting;
+ struct uvc_format_desc *fmtdesc;
struct uvc_frame *frame;
const unsigned char *start = buffer;
unsigned int interval;
@@ -288,6 +323,7 @@
switch (buffer[2]) {
case VS_FORMAT_UNCOMPRESSED:
+ case VS_FORMAT_FRAME_BASED:
if (buflen < 27) {
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
"interface %d FORMAT error\n",
@@ -296,11 +332,23 @@
return -EINVAL;
}
- strncpy(format->name, "Uncompressed", sizeof format->name);
- format->fcc = uvc_guid_to_fcc(&buffer[5]);
- format->flags = 0;
- format->bpp = 16;
- ftype = VS_FRAME_UNCOMPRESSED;
+ /* Find the format descriptor from its GUID. */
+ fmtdesc = uvc_format_by_guid(&buffer[5]);
+ if (fmtdesc == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d unknown frame-based GUID\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ strncpy(format->name, fmtdesc->name, sizeof format->name);
+ format->fcc = fmtdesc->fcc;
+ format->flags = fmtdesc->flags;
+ format->bpp = buffer[21];
+ if (buffer[2] == VS_FORMAT_UNCOMPRESSED)
+ ftype = VS_FRAME_UNCOMPRESSED;
+ else
+ ftype = VS_FRAME_FRAME_BASED;
break;
case VS_FORMAT_MJPEG:
@@ -362,7 +410,6 @@
break;
case VS_FORMAT_MPEG2TS:
- case VS_FORMAT_FRAME_BASED:
case VS_FORMAT_STREAM_BASED:
/* Not supported yet. */
default:
@@ -378,64 +425,70 @@
buflen -= buffer[0];
buffer += buffer[0];
+ /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
+ * based formats have frame descriptors.
+ */
while (buflen > 2 && buffer[2] == ftype) {
frame = &format->frame[format->nframes];
- switch (buffer[2]) {
- case VS_FRAME_UNCOMPRESSED:
- case VS_FRAME_MJPEG:
+ if (ftype != VS_FRAME_FRAME_BASED)
n = buflen > 25 ? buffer[25] : 0;
- n = n ? n : 3;
+ else
+ n = buflen > 21 ? buffer[21] : 0;
- if (buflen < 26 + 4*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
- "interface %d FRAME error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
+ n = n ? n : 3;
- frame->bFrameIndex = buffer[3];
- frame->bmCapabilities = buffer[4];
- frame->wWidth = le16_to_cpup((__le16*)&buffer[5]);
- frame->wHeight = le16_to_cpup((__le16*)&buffer[7]);
- frame->dwMinBitRate = le32_to_cpup((__le32*)&buffer[9]);
- frame->dwMaxBitRate = le32_to_cpup((__le32*)&buffer[13]);
- frame->dwMaxVideoFrameBufferSize = le32_to_cpup((__le32*)&buffer[17]);
+ if (buflen < 26 + 4*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d FRAME error\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ frame->bFrameIndex = buffer[3];
+ frame->bmCapabilities = buffer[4];
+ frame->wWidth = le16_to_cpup((__le16*)&buffer[5]);
+ frame->wHeight = le16_to_cpup((__le16*)&buffer[7]);
+ frame->dwMinBitRate = le32_to_cpup((__le32*)&buffer[9]);
+ frame->dwMaxBitRate = le32_to_cpup((__le32*)&buffer[13]);
+ if (ftype != VS_FRAME_FRAME_BASED) {
+ frame->dwMaxVideoFrameBufferSize =
+ le32_to_cpup((__le32*)&buffer[17]);
+ frame->dwDefaultFrameInterval =
+ le32_to_cpup((__le32*)&buffer[21]);
frame->bFrameIntervalType = buffer[25];
- frame->dwFrameInterval = *intervals;
+ } else {
+ frame->dwMaxVideoFrameBufferSize = 0;
+ frame->dwDefaultFrameInterval =
+ le32_to_cpup((__le32*)&buffer[17]);
+ frame->bFrameIntervalType = buffer[21];
+ }
+ frame->dwFrameInterval = *intervals;
- /* Some bogus devices report dwMinFrameInterval equal
- * to dwMaxFrameInterval and have dwFrameIntervalStep
- * set to zero. Setting all null intervals to 1 fixes
- * the problem and some other divisions by zero which
- * could happen.
- */
- for (i = 0; i < n; ++i) {
- interval = le32_to_cpup((__le32*)&buffer[26+4*i]);
- *(*intervals)++ = interval ? interval : 1;
- }
+ /* Some bogus devices report dwMinFrameInterval equal to
+ * dwMaxFrameInterval and have dwFrameIntervalStep set to
+ * zero. Setting all null intervals to 1 fixes the problem and
+ * some other divisions by zero which could happen.
+ */
+ for (i = 0; i < n; ++i) {
+ interval = le32_to_cpup((__le32*)&buffer[26+4*i]);
+ *(*intervals)++ = interval ? interval : 1;
+ }
- /* Make sure that the default frame interval stays
- * between the boundaries.
- */
- n -= frame->bFrameIntervalType ? 1 : 2;
- interval = le32_to_cpup((__le32*)&buffer[21]);
- if (interval < frame->dwFrameInterval[0])
- interval = frame->dwFrameInterval[0];
- else if (interval > frame->dwFrameInterval[n])
- interval = frame->dwFrameInterval[n];
+ /* Make sure that the default frame interval stays between
+ * the boundaries.
+ */
+ n -= frame->bFrameIntervalType ? 1 : 2;
+ frame->dwDefaultFrameInterval =
+ min(frame->dwFrameInterval[n],
+ max(frame->dwFrameInterval[0],
+ frame->dwDefaultFrameInterval));
- frame->dwDefaultFrameInterval = interval;
+ uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+ frame->wWidth, frame->wHeight,
+ 10000000/frame->dwDefaultFrameInterval,
+ (100000000/frame->dwDefaultFrameInterval)%10);
- uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
- frame->wWidth, frame->wHeight,
- 10000000/frame->dwDefaultFrameInterval,
- (100000000/frame->dwDefaultFrameInterval)%10);
-
- break;
- }
-
format->nframes++;
buflen -= buffer[0];
buffer += buffer[0];
@@ -563,6 +616,7 @@
switch (_buffer[2]) {
case VS_FORMAT_UNCOMPRESSED:
case VS_FORMAT_MJPEG:
+ case VS_FORMAT_FRAME_BASED:
nformats++;
break;
@@ -576,7 +630,6 @@
break;
case VS_FORMAT_MPEG2TS:
- case VS_FORMAT_FRAME_BASED:
case VS_FORMAT_STREAM_BASED:
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
"interface %d FORMAT %u is not supported.\n",
@@ -627,6 +680,7 @@
case VS_FORMAT_UNCOMPRESSED:
case VS_FORMAT_MJPEG:
case VS_FORMAT_DV:
+ case VS_FORMAT_FRAME_BASED:
format->frame = frame;
ret = uvc_parse_format(dev, streaming, format,
&interval, buffer, buflen);
Index: uvcvideo.h
===================================================================
--- uvcvideo.h (revision 110)
+++ uvcvideo.h (working copy)
@@ -218,6 +218,14 @@
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_NV12 {0x4e, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, \
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YV12 { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_I420 { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_UYVY { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_MPEG { 'M', 'P', 'E', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
/* ------------------------------------------------------------------------
@@ -332,8 +340,10 @@
};
struct uvc_format_desc {
+ char *name;
__u8 guid[16];
__u32 fcc;
+ __u32 flags;
};
/* The term 'entity' refers to both UVC units and UVC terminals.
_______________________________________________
Linux-uvc-devel mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel