diff mbox series

[1/2] media: uvcvideo: Ensure all probed info is returned to v4l2

Message ID 20200823012134.3813457-1-agoode@google.com (mailing list archive)
State New, archived
Headers show
Series [1/2] media: uvcvideo: Ensure all probed info is returned to v4l2 | expand

Commit Message

Adam Goode Aug. 23, 2020, 1:21 a.m. UTC
bFrameIndex and bFormatIndex can be negotiated by the camera during
probing, resulting in the camera choosing a different format than
expected. v4l2 can already accommodate such changes, but the code was
not updating the proper fields.

Without such a change, v4l2 would potentially interpret the payload
incorrectly, causing corrupted output. This was happening on the
Elgato HD60 S+, which currently always renegotiates to format 1.

As an aside, the Elgato firmware is buggy and should not be renegotating,
but it is still a valid thing for the camera to do. Both macOS and Windows
will properly probe and read uncorrupted images from this camera.

With this change, both qv4l2 and chromium can now read uncorrupted video
from the Elgato HD60 S+.

Signed-off-by: Adam Goode <agoode@google.com>
---
 drivers/media/usb/uvc/uvc_v4l2.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

Comments

Laurent Pinchart Aug. 23, 2020, 2:37 p.m. UTC | #1
Hi Adam,

Thank you for the patch.

On Sat, Aug 22, 2020 at 09:21:33PM -0400, Adam Goode wrote:
> bFrameIndex and bFormatIndex can be negotiated by the camera during
> probing, resulting in the camera choosing a different format than
> expected. v4l2 can already accommodate such changes, but the code was
> not updating the proper fields.
> 
> Without such a change, v4l2 would potentially interpret the payload
> incorrectly, causing corrupted output. This was happening on the
> Elgato HD60 S+, which currently always renegotiates to format 1.
> 
> As an aside, the Elgato firmware is buggy and should not be renegotating,
> but it is still a valid thing for the camera to do. Both macOS and Windows
> will properly probe and read uncorrupted images from this camera.
> 
> With this change, both qv4l2 and chromium can now read uncorrupted video
> from the Elgato HD60 S+.

Good catch. I've seen my share of buggy cameras, just not this
particular bug I suppose :-)

> Signed-off-by: Adam Goode <agoode@google.com>
> ---
>  drivers/media/usb/uvc/uvc_v4l2.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 0335e69b70ab..7f14096cb44d 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -247,11 +247,37 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
>  	if (ret < 0)
>  		goto done;
>  
> +	/* After the probe, update fmt with the values returned from
> +	 * negotiation with the device.
> +	 */
> +	for (i = 0; i < stream->nformats; ++i) {
> +		if (probe->bFormatIndex == stream->format[i].index) {
> +			format = &stream->format[i];
> +			break;
> +		}
> +	}
> +	if (i == stream->nformats) {
> +		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u.\n",
> +			  probe->bFormatIndex);
> +		return -EINVAL;
> +	}
> +	for (i = 0; i < format->nframes; ++i) {
> +		if (probe->bFrameIndex == format->frame[i].bFrameIndex) {
> +			frame = &format->frame[i];
> +			break;
> +		}
> +	}
> +	if (i == format->nframes) {
> +		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u.\n",
> +			  probe->bFrameIndex);
> +		return -EINVAL;
> +	}

This looks good to me. Blank lines between the different blocks would be
good to let the code breathe a little bit :-) Apart from that,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

There's no need to resubmit the patch for such a trivial change, unless
you object, I'll add the blank lines locally.

I may submit an additional patch on top of this to share the above code
with the identical implementation in uvc_fixup_video_ctrl().

>  	fmt->fmt.pix.width = frame->wWidth;
>  	fmt->fmt.pix.height = frame->wHeight;
>  	fmt->fmt.pix.field = V4L2_FIELD_NONE;
>  	fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
>  	fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
> +	fmt->fmt.pix.pixelformat = format->fcc;
>  	fmt->fmt.pix.colorspace = format->colorspace;
>  
>  	if (uvc_format != NULL)
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 0335e69b70ab..7f14096cb44d 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -247,11 +247,37 @@  static int uvc_v4l2_try_format(struct uvc_streaming *stream,
 	if (ret < 0)
 		goto done;
 
+	/* After the probe, update fmt with the values returned from
+	 * negotiation with the device.
+	 */
+	for (i = 0; i < stream->nformats; ++i) {
+		if (probe->bFormatIndex == stream->format[i].index) {
+			format = &stream->format[i];
+			break;
+		}
+	}
+	if (i == stream->nformats) {
+		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u.\n",
+			  probe->bFormatIndex);
+		return -EINVAL;
+	}
+	for (i = 0; i < format->nframes; ++i) {
+		if (probe->bFrameIndex == format->frame[i].bFrameIndex) {
+			frame = &format->frame[i];
+			break;
+		}
+	}
+	if (i == format->nframes) {
+		uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u.\n",
+			  probe->bFrameIndex);
+		return -EINVAL;
+	}
 	fmt->fmt.pix.width = frame->wWidth;
 	fmt->fmt.pix.height = frame->wHeight;
 	fmt->fmt.pix.field = V4L2_FIELD_NONE;
 	fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
 	fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
+	fmt->fmt.pix.pixelformat = format->fcc;
 	fmt->fmt.pix.colorspace = format->colorspace;
 
 	if (uvc_format != NULL)