Discussion:
[linux-uvc-devel] uvcvideo overallocates bandwidth for compressed (e.g. MJPG) video, proposed fix
reik red
2015-07-25 02:34:32 UTC
Permalink
I have a bandwidth problem with the uvcvideo driver that appears to be pretty common. I searched for "uvcvideo Unable
to start capture: No space left on device" and found quite a few people that had the same problem. The error message is
a bit unclear, but it really is about "bandwidth space on the USB device" and not about running out ot storage.

Now, I think I know what the underlying problem is so please bear with me while I explain.

The problem arises when people try tro run more than one USB webcam (logitech c270 in my case) on a shared 480Mbps worth
of USB bandwidth. A typical use case is when people plug two webcams into a PC or, as in my case, an openWRT router, and
then try to start two mjpg_streamer processes to stream the mjpg video to some other destination. The 2nd process will
fail with the aforementioned "No space left on device" error message.

I spend several evenings investigating the problem, and I think I have found out what the problem is:

In uvc_video.c, there is snippet of code, starting on line 122 in my copy, which goes like this:


if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
stream->intf->num_altsetting > 1) {
u32 interval;
u32 bandwidth;

interval = (ctrl->dwFrameInterval > 100000)
? ctrl->dwFrameInterval
: frame->dwFrameInterval[0];

/* Compute a bandwidth estimation by multiplying the frame
* size by the number of video frames per second, divide the
* result by the number of USB frames (or micro-frames for
* high-speed devices) per second and add the UVC header size
* (assumed to be 12 bytes long).
*/
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
if (stream->dev->udev->speed == USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;

/* The bandwidth estimate is too low for many cameras. Don't use
* maximum packet sizes lower than 1024 bytes to try and work
* around the problem. According to measurements done on two
* different camera models, the value is high enough to get most
* resolutions working while not preventing two simultaneous
* VGA streams at 15 fps.
*/
bandwidth = max_t(u32, bandwidth, 1024);

ctrl->dwMaxPayloadTransferSize = bandwidth;
}

Now, the above code is supposed to fix bandwidth problems when you use the quirk=128 module setting (causes the
UVC_QUIRK_FIX_BANDWIDTH flag in the code), as in

insmod uvcvideo quirk=128

However, upon reading the code above, I discovered that the code does not at all account for UVC_FMT_FLAG_COMPRESSED,
which is saying that the video is compressed and will therefore require smaller bandwidth than (width * height *
bits-per_pixel * framerate), by some reduction factor, call it "jpgcompr" or "comprfac", which I think should be another
module parameter with some conservative default value, say 5.

Additionally there is the hack "bandwidth = max_t(u32, bandwidth, 1024);" which floors the bandwidth at 1024B/frame
where a frame is 125us or about 60000b == 7500B in the case if USB at 480 Mbps. This should not be necessary. One could
adjust it down by changing comprfac from 5 to 1 if need be.

I have observed using openWRT instrumentation that my c270 at 1280x960 resolution uses less than 9Mbps on the average at
5fps in MJPG mode, but the above code allocates something like 150Mbps to it, making it impossible to have a 2nd cam
running because (google it) USB tops out around 190 Mbps effective rate even if the raw spec is 480Mbps.

So how about it? I think the corrected code could look something like the following, with some accompanying declarations
and defaults in uvc_module.c.


if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
stream->intf->num_altsetting > 1) {
u32 interval;
u32 bandwidth;

interval = (ctrl->dwFrameInterval > 100000)
? ctrl->dwFrameInterval
: frame->dwFrameInterval[0];

/* Compute a bandwidth estimation by multiplying the frame
* size by the number of video frames per second, divide the
* result by the number of USB frames (or micro-frames for
* high-speed devices) per second and add the UVC header size
* (assumed to be 12 bytes long).
*/
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
if (stream->dev->udev->speed == USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;

/* PROPOSED MODIFICATION: jpgcompr is the expected reduction in bandwidth
via compression. Should have a default value, as well
as be able to specify it arbitrarily as a module
parameter. Suggest jpgcompr=5 as default (a very
conservative value for compression). Even 10x is
usually achieveable with little trouble.*/
bandwidth /= jpegcompr;

/* ditch the floor value on bandwidth */
/* bandwidth = max_t(u32, bandwidth, 1024); */

ctrl->dwMaxPayloadTransferSize = bandwidth;
}

Finally, I am not a kernel hacker, so I am hoping someone who is interested might be willing to follow the outline above
and create some usable code. I also have the additional challenge of cross-compiling the module to openWRT on a mips
box to worry about ;).

Hope to get some feedback on this.






------------------------------------------------------------------------------
Oleksij Rempel
2015-07-25 05:03:23 UTC
Permalink
Post by reik red
I have a bandwidth problem with the uvcvideo driver that appears to be pretty common. I searched for "uvcvideo Unable
to start capture: No space left on device" and found quite a few people that had the same problem. The error message is
a bit unclear, but it really is about "bandwidth space on the USB device" and not about running out ot storage.
Now, I think I know what the underlying problem is so please bear with me while I explain.
The problem arises when people try tro run more than one USB webcam (logitech c270 in my case) on a shared 480Mbps worth
of USB bandwidth. A typical use case is when people plug two webcams into a PC or, as in my case, an openWRT router, and
then try to start two mjpg_streamer processes to stream the mjpg video to some other destination. The 2nd process will
fail with the aforementioned "No space left on device" error message.
if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
stream->intf->num_altsetting > 1) {
u32 interval;
u32 bandwidth;
interval = (ctrl->dwFrameInterval > 100000)
? ctrl->dwFrameInterval
: frame->dwFrameInterval[0];
/* Compute a bandwidth estimation by multiplying the frame
* size by the number of video frames per second, divide the
* result by the number of USB frames (or micro-frames for
* high-speed devices) per second and add the UVC header size
* (assumed to be 12 bytes long).
*/
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
if (stream->dev->udev->speed == USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;
/* The bandwidth estimate is too low for many cameras. Don't use
* maximum packet sizes lower than 1024 bytes to try and work
* around the problem. According to measurements done on two
* different camera models, the value is high enough to get most
* resolutions working while not preventing two simultaneous
* VGA streams at 15 fps.
*/
bandwidth = max_t(u32, bandwidth, 1024);
ctrl->dwMaxPayloadTransferSize = bandwidth;
}
Now, the above code is supposed to fix bandwidth problems when you use the quirk=128 module setting (causes the
UVC_QUIRK_FIX_BANDWIDTH flag in the code), as in
insmod uvcvideo quirk=128
However, upon reading the code above, I discovered that the code does not at all account for UVC_FMT_FLAG_COMPRESSED,
which is saying that the video is compressed and will therefore require smaller bandwidth than (width * height *
bits-per_pixel * framerate), by some reduction factor, call it "jpgcompr" or "comprfac", which I think should be another
module parameter with some conservative default value, say 5.
Additionally there is the hack "bandwidth = max_t(u32, bandwidth, 1024);" which floors the bandwidth at 1024B/frame
where a frame is 125us or about 60000b == 7500B in the case if USB at 480 Mbps. This should not be necessary. One could
adjust it down by changing comprfac from 5 to 1 if need be.
I have observed using openWRT instrumentation that my c270 at 1280x960 resolution uses less than 9Mbps on the average at
5fps in MJPG mode, but the above code allocates something like 150Mbps to it, making it impossible to have a 2nd cam
running because (google it) USB tops out around 190 Mbps effective rate even if the raw spec is 480Mbps.
So how about it? I think the corrected code could look something like the following, with some accompanying declarations
and defaults in uvc_module.c.
if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
stream->intf->num_altsetting > 1) {
u32 interval;
u32 bandwidth;
interval = (ctrl->dwFrameInterval > 100000)
? ctrl->dwFrameInterval
: frame->dwFrameInterval[0];
/* Compute a bandwidth estimation by multiplying the frame
* size by the number of video frames per second, divide the
* result by the number of USB frames (or micro-frames for
* high-speed devices) per second and add the UVC header size
* (assumed to be 12 bytes long).
*/
bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
bandwidth *= 10000000 / interval + 1;
bandwidth /= 1000;
if (stream->dev->udev->speed == USB_SPEED_HIGH)
bandwidth /= 8;
bandwidth += 12;
/* PROPOSED MODIFICATION: jpgcompr is the expected reduction in bandwidth
via compression. Should have a default value, as well
as be able to specify it arbitrarily as a module
parameter. Suggest jpgcompr=5 as default (a very
conservative value for compression). Even 10x is
usually achieveable with little trouble.*/
bandwidth /= jpegcompr;
/* ditch the floor value on bandwidth */
/* bandwidth = max_t(u32, bandwidth, 1024); */
ctrl->dwMaxPayloadTransferSize = bandwidth;
}
Finally, I am not a kernel hacker, so I am hoping someone who is interested might be willing to follow the outline above
and create some usable code. I also have the additional challenge of cross-compiling the module to openWRT on a mips
box to worry about ;).
Hope to get some feedback on this.
Well, thank you for your try, but if you would spend more time on
googleing you will find other patches which could "fix" the problem. The
thing is, each cam use different compression algorithm and depending on
algorithm and noise level, compressed frame can be as big as
uncompressed. So, i assume only proper solution for this issue is to use
bulk mode for compressed stream, which depends on HW.
--
Regards,
Oleksij
Paul Fertser
2015-07-26 18:39:10 UTC
Permalink
Hello,
Post by Oleksij Rempel
Post by reik red
I have a bandwidth problem with the uvcvideo driver that appears
to be pretty common. I searched for "uvcvideo Unable to start
capture: No space left on device" and found quite a few people
that had the same problem. The error message is a bit unclear, but
it really is about "bandwidth space on the USB > device" and not
about running out ot storage.
...
Post by Oleksij Rempel
Well, thank you for your try, but if you would spend more time on
googleing you will find other patches which could "fix" the problem. The
thing is, each cam use different compression algorithm and depending on
algorithm and noise level, compressed frame can be as big as
uncompressed. So, i assume only proper solution for this issue is to use
bulk mode for compressed stream, which depends on HW.
Probably worth adding a module parameter to override this check then
to avoid unnecessarily limiting the users who actually have enough
bandwidth? I'd say that when this check fires a KERN_WARN message
should be output suggesting method to override it.
--
Be free, use free (http://www.gnu.org/philosophy/free-sw.html) software!
mailto:***@gmail.com

------------------------------------------------------------------------------
Oleksij Rempel
2015-07-26 18:47:35 UTC
Permalink
Post by Paul Fertser
Hello,
Post by Oleksij Rempel
Post by reik red
I have a bandwidth problem with the uvcvideo driver that appears
to be pretty common. I searched for "uvcvideo Unable to start
capture: No space left on device" and found quite a few people
that had the same problem. The error message is a bit unclear, but
it really is about "bandwidth space on the USB > device" and not
about running out ot storage.
...
Post by Oleksij Rempel
Well, thank you for your try, but if you would spend more time on
googleing you will find other patches which could "fix" the problem. The
thing is, each cam use different compression algorithm and depending on
algorithm and noise level, compressed frame can be as big as
uncompressed. So, i assume only proper solution for this issue is to use
bulk mode for compressed stream, which depends on HW.
Probably worth adding a module parameter to override this check then
to avoid unnecessarily limiting the users who actually have enough
bandwidth? I'd say that when this check fires a KERN_WARN message
should be output suggesting method to override it.
This patch i wrote and published under other name:
http://permalink.gmane.org/gmane.linux.drivers.uvc.devel/5863
Not sure if it still working and i'm not sure if i would do it in same
way today. If some one has time please try it and update it if needed.
--
Regards,
Oleksij
reik red
2015-07-27 21:20:25 UTC
Permalink
------------------------------------------------------------------------------
Loading...