reik red
2015-07-25 02:34:32 UTC
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.
------------------------------------------------------------------------------
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.
------------------------------------------------------------------------------