Using multiple USB webcams in Linux
Ding ding! Managed to figure this one out with some help from the nice people in #v4l on freenode.
Long story short: v4l2-ctl is the best tool for debugging USB camera issues. Read all the available commands and the man page, it'll be fun I promise. Using v4l2-ctl I discovered one of my cameras did not support any compressed video modes. You can check what modes your cameras support by running the following command:
v4l2-ctl -d /dev/video0 --list-formats
Which should output something like this.
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : MJPEG
Index : 1
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUV 4:2:2 (YUYV)
If the only pixel format returned is "YUYV", "IUYV", "I420", or "GBRG" you'll only be able to run one camera per USB controller* since those formats are uncompressed. Using multiple webcams which support MJPEG or some other form of compression will work fine.
If you use OpenCV like me, don't worry if the default pixel format isn't compressed as it appears OpenCV defaults to using compression anyway.
**Unless you're happy with 320x240 resolution or lower.*
The answer is to use the uvcvideo modifications written by SwDevRefugee, and described above. He and I have worked together to get the mod'ed code compiled for OpenWrt, with success. The version I am running it on is OpenWRT DESIGNATED DRIVER (Bleeding Edge, r48130), on a tplink wdr3600 router:
RESULT: I can have 3*c270 (logitech) running simulataneously at 1280x960 and 15fps in MJPG format, through a usb 2.0 hub. I do not have a fourth c270 to hook up, sorry.
I can also have 2*c270 and 1*GEMBIRD 640*480*15fps with YUV format, but adding a 2nd GEMBIRD leads to the dreaded "Unable to start capture: No space left on device" (space==bandwidth here, as you know well:)). Note that GEMBIRD (1908:2311) == http://www.penguin.cz/~utx/hardware/USB_Camera_AX2311/ .
CPU usage with 3*c270 is fairly reasonable on a wdr3600:
Mem: 50600K used, 75444K free, 320K shrd, 3436K buff, 8800K cached
CPU: 16% usr 27% sys 0% nic 45% idle 0% io 0% irq 10% sirq
Load average: 1.20 0.85 0.44 4/60 2546
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
2240 1679 root S 15348 12% 17% mjpg_streamer --input input_uvc.so --
2505 1679 root S 15368 12% 11% mjpg_streamer --input input_uvc.so --
2239 1679 root S 15532 12% 11% mjpg_streamer --input input_uvc.so --
If the community gives some reputation and support, I think SwDevRefugee is willing to get the code into uvc-linux.
I looked at the uvcvideo driver and the quirks=128 module parameter is ignored if the stream is mjpeg compressed.
My webcams of choice have been the Logitech C500 and the Logitech C270, and I found that the image produced by the C500 at 1280x1024 is 100kbytes and the image produced by the C270 at 1280x960 is 200kbytes.
If I run the C270 at 10fps then the bitrate required is 10x200000x8 = 16Mbit/s. In Ubuntu 14.04 the uvcdriver module always allocates 196Mbits/s regardless of the frame rate. For the C500 it is a little better behaved, but is still a bandwidth hog.
I have modified the uvcvideo driver so that I can provide a "compression" factor to the driver through the V4L2 interface. It is a "little hacky" in that I used the priv attribute in the struct v4l2_pix_format to specify the value. In the driver it calculates the size of the uncompressed image and then divides by the compression factor to work out what USB bandwidth to use.
By default I use a compression factor of 10 which allows a large margin for if the camera encounters a particularly hard image to compress. The C270 running at 1280x960 and 10fps now uses 41Mbit/s and I can easily run 4 cameras on one bus.
If anyone is interested in this feature then I will attempt to get the uvcvideo maintainers to consider the "compression" factor concept.