diff mbox series

[3/3] media: uvcvideo: Implement dual stream quirk to fix loss of usb packets

Message ID 20241108142310.19794-4-isaac.scott@ideasonboard.com
State New
Headers show
Series Fix Sonix Technology MJPEG streams | expand

Commit Message

Isaac Scott Nov. 8, 2024, 2:23 p.m. UTC
Some cameras, such as the Sonix Technology Co. 292A exhibit issues when
running two parallel streams, causing USB packets to be dropped when an
H.264 stream posts a keyframe while an MJPEG stream is running
simultaneously. This occasionally causes the driver to erroneously
output two consecutive JPEG images as a single frame.

To fix this, we inspect the buffer, and trigger a new frame when we
find an SOI, inverting the FID to make sure no frames are erroneously
dropped.

Signed-off-by: Isaac Scott <isaac.scott@ideasonboard.com>
---
 drivers/media/usb/uvc/uvc_video.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 2fb9f2b59afc..f754109f5e96 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1211,6 +1211,30 @@  static int uvc_video_decode_start(struct uvc_streaming *stream,
 		return -EAGAIN;
 	}
 
+	/*
+	 * Some cameras, such as the Sonix Technology Co. 292A exhibit issues
+	 * when running two parallel streams, causing USB packets to be dropped
+	 * when an H.264 stream posts a keyframe while an MJPEG stream is
+	 * running simultaneously. This occasionally causes the driver to
+	 * erroneously output two consecutive JPEG images as a single frame.
+	 *
+	 * Check the buffer for a new SOI on JPEG streams and complete the
+	 * preceding buffer using EAGAIN, and invert the FID to make sure the
+	 * erroneous frame is not dropped.
+	 */
+	if ((stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF) &&
+	    (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG ||
+	     stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) {
+		const u8 *packet = data + header_len;
+
+		if ((packet[0] == 0xff && packet[1] == 0xd8) && buf->bytesused != 0) {
+			buf->state = UVC_BUF_STATE_READY;
+			buf->error = 1;
+			stream->last_fid ^= UVC_STREAM_FID;
+			return -EAGAIN;
+		}
+	}
+
 	stream->last_fid = fid;
 
 	return header_len;