SensorsIot / IOTstack

Docker stack for getting started on IOT on the Raspberry PI
GNU General Public License v3.0
1.44k stars 304 forks source link

Udev Rules and Streaming Multiple Webcams Not Working #621

Closed BlueWings172 closed 1 year ago

BlueWings172 commented 1 year ago

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 runls /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 lsusband 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 /devbefore 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!

Paraphraser commented 1 year ago

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:

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.

BlueWings172 commented 1 year ago

@Paraphraser

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 runningv4l2-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. image

image

Now that steaming issue solved, I have to design a holder for my endoscope!