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

Reply via email to