Web Cam Resolution Revisited

Yesterday I was playing around with determining webcam resolution by reverse engineering how fswebcam worked, using the ioctl VIDIOC_TRY_FMT. I keep working that evening on my code and was thinking of filing a bug against v4l2-ctl or the driver until I made a discovery: having fswebcam take a picture at a specified resolution actually changes what v4l2-ctl returns, here’s an example that’s been truncated to keep it short:

mfisch@caprica:/tmp/luvcview-0.2.6$ v4l2-ctl -V
Width/Height : 1280/720

mfisch@caprica:/tmp/luvcview-0.2.6$ fswebcam -d /dev/video0 -r640x480 /tmp/foo.jpg
Writing JPEG image to '/tmp/foo.jpg'.

mfisch@caprica:/tmp/luvcview-0.2.6$ v4l2-ctl -V
Width/Height : 640/480

Hey look, v4l2-ctl changed it’s answer! So is this a bug? I dug into the v4l2 docs and it turns out that you can make it change it’s answer again by picking a different resolution with fswebcam. This has to do with how fswebcam works.

fswebcam first calls VIDIOC_TRY_FMT and that ioctl returns and sets some parameters on the image. For example, if you request an image that’s 10000×10000 it will return back a success, but set the width and height to something like 1280×720 (depending on your hardware).

Next fswebcam calls VIDIOC_S_FMT. Unlike VIDIOC_TRY_FMT, S_FMT actually sets what the driver’s format is. So, if you take a picture at 640×480, the driver will keep that internal format as default until you call it again with a larger value. This is all described in the v4l2 spec.

So, I was wrong, the driver is fine and is in fact behaving as the spec requires. So given this information, I figured that to find the true max, I could just start with a huge value, like 10000×10000 and see what value TRY_FMT came back with. That’s where I left it last night, until a colleague, Josh Poulson, pointed me to luvcview.

luvcview can list all the supported formats and even the time intervals between frames, it prints a long list, so here’s a truncated version:

Device information:
Device path: /dev/video0
{ pixelformat = 'YUYV', description = 'YUV 4:2:2 (YUYV)' }
{ discrete: width = 640, height = 480 }
Time interval between frame: 1/30, 1/20, 1/15, 1/10, 1/5,
{ discrete: width = 640, height = 400 }
Time interval between frame: 1/30, 1/20, 1/15, 1/10, 1/5,
{ discrete: width = 352, height = 288 }
Time interval between frame: 1/30, 1/20, 1/15, 1/10, 1/5,

And that’s what I needed. It turns out that there’s an experimental ioctl called VIDIOC_ENUM_FRAMESIZES that luvcview is using. This information highlights the good and bad parts of the web. It was easy for me to take existing tools and dive into the problem I was trying to solve with virtually no knowledge of v4l2, but I made some bad assumptions. But it’s also thanks to the web, and my partially incorrect blog post that I know so much more now.

Tagged , , , ,

3 thoughts on “Web Cam Resolution Revisited

  1. mcnesium says:

    so did you figure out how to get fswebcam to take a picture of the requested size? i kind of missed that point. my webcam can do 640×480 but fswebcam shrinks it down to 160×120

    mcnesium@fluse:~$ fswebcam -r 640×480 tmp/cam.jpg
    — Opening /dev/video0…
    Trying source module v4l2…
    /dev/video0 opened.
    No input was specified, using the first.
    Adjusting resolution from 640×480 to 160×120.
    — Capturing frame…
    Captured frame in 0.00 seconds.
    — Processing captured image…
    Writing JPEG image to ‘tmp/cam.jpg’.

    • Matt Fischer says:

      I don’t know unfortunately. What’s the output that shows your supported info?

      • mcnesium says:

        src_v4l2_set_pix_format,541: Device offers the following V4L2 pixel formats:
        src_v4l2_set_pix_format,554: 0: [0x30313953] ‘S910’ (S910)
        src_v4l2_set_pix_format,554: 1: [0x31384142] ‘BA81’ (BA81)

        those are the same as ffmpeg/avconv shows me, except that they are unsupported there:

        [video4linux2 @ 0x947f3a0] R : Unsupported : S910 : 160×120 320×240 640×480
        [video4linux2 @ 0x947f3a0] R : Unsupported : BA81 : 160×120

        so the small one BA81 is used obviously. do you know how i can switch to the other one?

Leave a Reply

Your email address will not be published. Required fields are marked *