Hi Laurent,
are there any reason why uvc_video_decode_start do not do precise header
size checks? Are there many cameras with broken header size too?
I send you patch on what i work now to catch streams with fragmented
packets.. what do you think about it? Will you apply some thing like this?
Regards,
Alexey.
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 8244167..6fd1986 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -371,6 +371,46 @@ int uvc_commit_video(struct uvc_streaming *stream,
#define UVC_STREAM_EOF (1 << 1)
#define UVC_STREAM_FID (1 << 0)
+static int uvc_video_jpeg_soi(const __u8 *data, int len)
+{
+ /* to check jpeg header we need minimum 2 bytes after header. */
+ if (data[0] + 2 > len)
+ return 0;
+
+ if (data[data[0]] == 0xff && data[data[0] + 1] == 0xd8) {
+ uvc_trace(UVC_TRACE_FRAME, "jpeg_SOI signature found\n");
+ return 1;
+ } else
+ return 0;
+}
+
+static int uvc_video_jpeg_eoi(struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ struct uvc_video_queue *queue = &stream->queue;
+ __u8 *mem, *mem_min;
+ int diff = 0;
+
+ mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused - 1;
+ mem_min = queue->mem + buf->buf.m.offset;
+
+ /* some times webcam can put some zeros after eoi signatur. */
+ while (*mem == 0) {
+ mem--;
+ diff++;
+ }
+
+ if (*(mem-1) == 0xff && *mem == 0xd9) {
+ uvc_trace(UVC_TRACE_FRAME, "jpeg_EOI signature found\n");
+ if (diff) {
+ uvc_trace(UVC_TRACE_FRAME, "remove trash after EOI\n");
+ buf->buf.bytesused = buf->buf.bytesused - diff;
+ }
+ return 1;
+ } else
+ return 0;
+}
+
/* Video payload decoding is handled by uvc_video_decode_start(),
* uvc_video_decode_data() and uvc_video_decode_end().
*
@@ -409,16 +449,65 @@ int uvc_commit_video(struct uvc_streaming *stream,
static int uvc_video_decode_start(struct uvc_streaming *stream,
struct uvc_buffer *buf, const __u8 *data, int len)
{
- __u8 fid;
+ __u8 fid, calk, recover;
+ int size;
+ calk = 1;
+ recover = 0;
/* Sanity checks:
- * - packet must be at least 2 bytes long
- * - bHeaderLength value must be at least 2 bytes (see above)
- * - bHeaderLength value can't be larger than the packet size.
- */
- if (len < 2 || data[0] < 2 || data[0] > len)
+ * - if too smal for minimal header then it is trash */
+ if (len < UVC_HEADER_SIZE_MIN)
return -EINVAL;
+ /* lets do strict header check, it could be good filter, but can detect
+ * more bad but working uvc cams.
+ */
+ if (calk && size != data[0]) {
+ /* calculate size of header, minimum is 2 byte */
+ size = UVC_HEADER_SIZE_MIN;
+ /* PTS should be 4 byte */
+ if (data[1] & UVC_STREAM_PTS)
+ size += 4;
+ /* SCR should be 6 */
+ if (data[1] & UVC_STREAM_SCR)
+ size += 6;
+ /* all together should be 12 byte */
+ if (size != data[0]) {
+ uvc_trace(UVC_TRACE_FRAME, "Calculated header size "
+ "do not match with reported:%i/%i (%i). "
+ "Frame is corrupt or payload is fragmented.",
+ size, data[0], len);
+ /* so we got some chunk of garbrage,
+ * It is against uvc specification.
+ * There are some webcams aim to be uvc but sent
+ * fragmented packets.
+ * I assume every thing more then 12 bytes can be
+ * video data. */
+ if (len > UVC_HEADER_SIZE_MAX) {
+ buf->error = 1;
+ return 0;
+ } else
+ return -EINVAL;
+ }
+ /* this is less strict check, in case PTS and/or SCR not set, but
+ * have max header leth (12) */
+ } else if (!calk &&
+ (data[0] < UVC_HEADER_SIZE_MIN ||
+ data[0] > UVC_HEADER_SIZE_MAX ||
+ data[0] > len)) {
+ uvc_trace(UVC_TRACE_FRAME, "Header or frame is corrupt. "
+ "Or payload is fragmented.");
+ if (len > UVC_HEADER_SIZE_MAX) {
+ buf->error = 1;
+ return 0;
+ } else
+ return -EINVAL;
+ }
+
+ /* no need to process empty payload, exept it has EOF flag. */
+ if (data[0] == len && !(data[1] & UVC_STREAM_EOF))
+ return -ENODATA;
+
/* Skip payloads marked with the error bit ("error frames"). */
if (data[1] & UVC_STREAM_ERR) {
uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit "
@@ -453,6 +542,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
if (buf->state != UVC_BUF_STATE_ACTIVE) {
struct timespec ts;
+ if (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG) {
+ /* for mjpeg stream start new buffer only if we have
+ * valid SOI signature. */
+ if (uvc_video_jpeg_soi(data, len)) {
+ stream->last_fid = -1;
+ } else {
+ uvc_trace(UVC_TRACE_FRAME,
+ "Dropping payload no "
+ "jpeg_SOI signature found." );
+ return -ENODATA;
+ }
+ }
+
if (fid == stream->last_fid) {
uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
"sync).\n");
@@ -493,6 +595,16 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
if (fid != stream->last_fid && buf->buf.bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
"toggled).\n");
+ /* For mjpeg stream, if FID was togled but there is no SOI
+ * signature, then some thing bad was happened.
+ * We can ignore this FID, or mark buffer as corrupt. */
+ if (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG &&
+ !uvc_video_jpeg_soi(data, len)) {
+ uvc_trace(UVC_TRACE_FRAME, "but no jpeg_SOI found "
+ "continue\n");
+ return data[0];
+ }
+
buf->state = UVC_BUF_STATE_READY;
return -EAGAIN;
}
@@ -535,8 +647,17 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
if (data[0] == len)
uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
buf->state = UVC_BUF_STATE_READY;
- if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
- stream->last_fid ^= UVC_STREAM_FID;
+ /* ready or not, it is time to togle FID. */
+ stream->last_fid = -1;
+ if (!(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) &&
+ buf->buf.length > buf->buf.bytesused) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame is corrupt.\n");
+ buf->error = 1;
+ } else if (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG &&
+ !uvc_video_jpeg_eoi(stream, buf)) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame is corrupt.\n");
+ buf->error = 1;
+ }
}
}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index df32a43..726e628 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -215,6 +215,10 @@ struct uvc_xu_control {
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
#define UVC_FMT_FLAG_STREAM 0x00000002
+/* uvc header size limits */
+#define UVC_HEADER_SIZE_MAX 12
+#define UVC_HEADER_SIZE_MIN 2
+
/* ------------------------------------------------------------------------
* Structures.
*/
_______________________________________________
Linux-uvc-devel mailing list
Linux-uvc-devel@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel