Udev Rules and Streaming Multiple Webcams Not Working
BlueWings172 opened this issue · 2 comments
Hello
I trying to use 2 webcams with Octoprint but they keep swapping video0 and video1 so I decided to use udev rules to create symlinks in addition to using 2 streaming containers (Ustreamer) and I had it working for a while until i rebooted the Pi !!
Not sure what I did wrong and this is driving me crazy and Ustreamer is showing No Signal on the browser for both webcams.
If I don't use the symlinks in docker-compose YAML, both steams works.
Here is my rule with priority 99:
SUBSYSTEM=="video4linux", ENV{ID_BUS}=="usb", ATTRS{idVendor}=="090c", ATTRS{idProduct}=="f37d", SYMLINK+="endoscope"
The dev/endoscope does show when I run ls /dev
and when I run udevadm info /dev/endoscope
I get the correct info of the webcam
Here is my docker-compose YAML file:
ustreamer:
image: mkuf/ustreamer:latest
restart: unless-stopped
ports:
- 8091:8080
devices:
- "/dev/endoscope"
command: --host=0.0.0.0 --port=8080 --slowdown --device=/dev/endoscope --resolution=1080x720 --format=MJPEG --desired-fps=20
when I run docker-compose logs ustreamer
, I get repeated rows of the below:
iotstack-ustreamer-1 | -- INFO [579.531 stream] -- Device fd=8 opened
iotstack-ustreamer-1 | -- INFO [579.531 stream] -- Using input channel: 0
iotstack-ustreamer-1 | -- ERROR [579.531 stream] -- Can't set input channel
iotstack-ustreamer-1 | -- INFO [579.531 stream] -- Device fd=8 closed
iotstack-ustreamer-1 | -- INFO [579.531 stream] -- Sleeping 1 seconds before new stream init ...
That said, if I modify my docker-compose YAML to include /dev/video0
instead of /dev/endoscope
then it works.
I know that the webcams work perfectly because i've used them before and they show when I run lsusb
and I can video their stream from VLC with video0 and video2. Ustreamer constrainers do their job if I don't use symlinks in YAML and mention /dev/video0.
I found a way that I make this work again but only until the next reboot. This might indicate where the problem lies. Here it is:
1- run this commandsudo udevadm control --reload-rules && sudo udevadm trigger
(note the webcams symlinks are already visible with ls /dev
before running this command)
2- restart the container with
It seems to me that the symlinks are not "visible" to the ustreamer containers when the pi boots up. I tried to lower the priority number of the udev rule but below about 60, the symlinks do not appear under /dev.
I would appreciate any help.
thanks!
I don't claim to know a whole lot about the how/when/why of device enumeration but I suspect you're right that this is the nub of the problem. I have also never used ustreamer. In short, I don't have a lot of relevant experience so please take this with a few grains of salt.
You said you were using two streaming containers but you've only included the YAML for one of them, and also only one udev rule. Why? All other things being equal, I would expect to see:
-
two udev rules where the discriminators (idVendor+idProduct) were clearly distinct between the two rules.
the two udev rules can be in the same file
-
two distinct SYMLINK names, by which I mean "endoscope" for one camera and a name that isn't "video0" or "video1" for the second camera.
-
the yaml for two containers, one using "endoscope", the other using the second name.
The fact that I can only see one of anything makes me suspect you're assuming you can get one camera out of the way with "endoscope", leaving the other camera free to take "video0". If I've guessed correctly, then I don't think it works that way. I'm pretty sure the cameras will still be video0 and video1 (in whatever order the OS decides at the time) and one of those will also be "endoscope". I think you need to use the same symlink pattern for both cameras so each one gets its own predictable non-videoN name, in addition to the unpredictable videoN name assigned by the OS.
Also, I've never been a big fan of implied mappings in compose files, by which I mean:
devices:
- "/dev/endoscope"
If it were me, I'd be standardising on video0 inside each container:
devices:
- "/dev/endoscope:/dev/video0"
then, in the other container:
devices:
- "/dev/someOtherCameraHere:/dev/video0"
The compose example on DockerHub appears to imply (internal) port 8080, host 0.0.0.0 (all interfaces) and video0 are the defaults so you can probably get away with just:
command: --slowdown --resolution=1080x720 --format=MJPEG --desired-fps=20
I hope that all makes sense. Please let me know if you discover the solution because I've been thinking about getting a second camera. Right now I only have a standard Raspberry Pi "ribbon camera" sitting atop the Pi, with the Pi mounted on a tripod pointing at the printer. It's OK but something closer to the hot-end would be better so you pointing me at ustreamer comes at the perfect time.
Just as a comment, I have never had to run udevadm
to reload udev rules. When I was experimenting with the rules described in octoprint-docker: when your 3D printer turns on and off, they always took effect immediately. I don't know whether this is true on all Linux systems but it definitely seems to be true on the RPi.
I also don't think the priority matters too much. I'm not even sure it is a true priority. I think it's just the lexical ordering within the directory. I've been using multiples of 11 (88-
then 77-
) for no reason other than 99-
was already there and it seemed like a good idea at the time.
I doubt any of this matters either way in terms of solving your problem but I thought I'd mention it.
I do indeed have 2 udev rules and 2 YAML entries but I included 1 of each only to simplify my post since both rules and both YAML entries are identical (except where they shouldn't).
I have amended the YAML entry to include - "/dev/endoscope:/dev/video0"
as suggested but that was not enough to solve the issue. Also I have changed the command line to exclude default values but that made both containers crash (I found that host is mandatory while port and video0 can be removed).
At the end, my ustreamer containers continued to behave randomly after each restart. Most of the times, only one container will be streaming (can be either one), sometimes both work fine, sometimes both are not streaming and show No Signal.
But after a week of banging my head on the wall, I'm glad to report that I found the issue (very silly one) and I fixed it.
Problem:
Since my containers randomly work or not after each Pi reboot, I decided to compare the output of udevadm info /dev/endoscope
when a container fails to stream and when it succeeds and after pasting them side by side on excel, I found that for the container to stream successfully, index0 has to be assigned to device video0. In other times where the container fails, it was index1 that was assigned to video0 for the first webcam and video2 for the second webcam.
Solution:
Just specify index in the udev rule by adding ATTR{index}=="0"
After reboot both containers are streaming fine and I have tried to reboot over 7 times without issues.
From what I can gather a webcam would have several indexes and only index0 carries video. To see what index numbers are assigned to each webcam I used the command ls /dev/v4l/by-id/ and in my case the output is:
pi@raspberrypi:~ $ ls /dev/v4l/by-id/
usb-Generic_USB2.0_PC_CAMERA-video-index0
usb-Generic_USB2.0_PC_CAMERA-video-index1
usb-Microsoft_Microsoft®_LifeCam_HD-3000-video-index0
usb-Microsoft_Microsoft®_LifeCam_HD-3000-video-index1
Also the each webcam will be assigned several virtual devices such as video0, video1, and media4. These can be viewed by running v4l2-ctl --list-devices
. The output for my webcams is as follows:
USB2.0 PC CAMERA: USB2.0 PC CAM (usb-0000:01:00.0-1.1):
/dev/video2
/dev/video3
/dev/media5
Microsoft® LifeCam HD-3000: Mi (usb-0000:01:00.0-1.2):
/dev/video0
/dev/video1
/dev/media4
Only the videoN with the lower number for each device carries video. so for my webcams to work the index0 for each webcam needs to be mapped with video device with lower number (video0 and video2) and then link them to corresponding symlink.
**Webcam1: index0 >> video2 >> symlink endoscope
Webcam2: index0 >> video0 >> symlink hd3000**
When I was having issues, it was index1 of each webcam that was mapped to video2 and video0. This was an easy fix by just specifying the index number in the udev rule.
To check which indexN and which videoN is linked to a specific symlink, I use the command udevadm info /dev/hd3000
(hd3000 is the symlink I created for my Microsoft HD3000 webcam). The first lines of output are:
P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2/1-1.2:1.0/video4linux/video0
**N: video0**
L: 0
S: v4l/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.2:1.0-video-**index0**
**S: hd3000**
S: v4l/by-id/**usb-Microsoft_Microsoft®_LifeCam_HD-3000-video-index0**
The udev rules that worked for me are:
SUBSYSTEM=="video4linux", ENV{ID_BUS}=="usb", ATTRS{idVendor}=="045e", ATTRS{idProduct}=="0779", ATTR{index}=="0", SYMLINK+="hd3000"
SUBSYSTEM=="video4linux", ENV{ID_BUS}=="usb", ATTRS{idVendor}=="1908", ATTRS{idProduct}=="2311", ATTR{index}=="0", SYMLINK+="endoscope"
YAML entries for my ustreamer containers:
ustreamer_hd3000:
container_name: hd3000-stream
image: mkuf/ustreamer:latest
restart: unless-stopped
ports:
- 8090:8080
devices:
- "/dev/hd3000:/dev/video0"
command: --host=0.0.0.0 --slowdown --resolution=1280x720 --format=MJPEG --desired-fps=30
ustreamer_endoscope:
container_name: endoscope-stream
image: mkuf/ustreamer:latest
restart: unless-stopped
ports:
- 8091:8080
devices:
- "/dev/endoscope:/dev/video0"
command: --host=0.0.0.0 --slowdown --resolution=1280x720 --format=YUYV --desired-fps=30
I found this command very helpful to determine capabilities of my webcams in terms of resolution and fps as well as info.
v4l2-ctl -d /dev/video0 --list-formats-ext
More very useful v4l2 commands here
Also this page includes information on ustreamer options that can be included in the yaml configuration.
As a beginner this was not easy to understand and figure out, that's why I'm including as much information as I can that might benefit someone else.
Now that everything is working fine, i'm quite happy with ustreamer. I feel it is much faster that the default streamer Octoprint uses and with the right configuration, I guess I can add several webcams in literally seconds.
With Sidebar Webcam plugin for Octorpint I can switch between cameras from the main page as well rotate cameras if needed.
Now that steaming issue solved, I have to design a holder for my endoscope!