Only one webcam can run - webcamd2 - problem with port number?

OctoPrint version : 1.3.12
OctoPi version : 0.17.0

webcamd2 gets the wrong port (webcamd2 activation failed)

ls -l /dev

show the correct symlinks, I have created:

lrwxrwxrwx 1 root root 6 Jan 17 23:25 VideoEndoskop -> video2
lrwxrwxrwx 1 root root 6 Jan 17 23:25 VideoLogitech -> video0

If i plugin both webcams:
VideoEndoskop is running quite well, the Logitech not.
service webcamd status
shows:

Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: www-folder-path......: ./www-octopi/
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: HTTP TCP port........: 8080
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: HTTP Listen Address..: (null)
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: username:password....: disabled
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: commands.............: disabled
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: starting input plugin input_uvc.so
Jan 17 23:25:28 octopi mjpg_streamer[452]: MJPG-streamer [452]: starting output plugin: output_http.so (ID: 00)
Jan 17 23:25:29 octopi webcamd[361]: Done bring up all configured video device
Jan 17 23:25:29 octopi webcamd[361]: Goodbye...
Jan 17 23:25:29 octopi systemd[1]: Started the OctoPi webcam daemon with the user specified config.

service webcamd2 status
shows:

● webcamd2.service - the OctoPi webcam daemon with the user specified config
Loaded: loaded (/etc/systemd/system/webcamd2.service; enabled; vendor preset: enabled)
Active: failed (Result: start-limit-hit) since Fri 2020-01-17 23:25:35 CET; 12h ago
Process: 593 ExecStart=/root/bin/webcamd2 (code=exited, status=0/SUCCESS)
Jan 17 23:25:35 octopi systemd[1]: webcamd2.service: Service RestartSec=100ms expired, scheduling restart.
Jan 17 23:25:35 octopi systemd[1]: webcamd2.service: Scheduled restart job, restart counter is at 5.
Jan 17 23:25:35 octopi systemd[1]: Stopped the OctoPi webcam daemon with the user specified config.
Jan 17 23:25:35 octopi systemd[1]: webcamd2.service: Start request repeated too quickly.
Jan 17 23:25:35 octopi systemd[1]: webcamd2.service: Failed with result 'start-limit-hit'.
Jan 17 23:25:35 octopi systemd[1]: Failed to start the OctoPi webcam daemon with the user specified config.

If I unplug the VideoEndoskop (assigned in /root/bin/webcamd)
REBOOT
the VideoLogitech (assigned in /root/bin/webcamd2) is running quite well.

service webcamd status:
shows:

● webcamd.service - the OctoPi webcam daemon with the user specified config
Loaded: loaded (/etc/systemd/system/webcamd.service; enabled; vendor preset: enabled)
Active: failed (Result: start-limit-hit) since Sat 2020-01-18 12:23:31 CET; 13min ago
Process: 580 ExecStart=/root/bin/webcamd (code=exited, status=0/SUCCESS)
Jan 18 12:23:30 octopi webcamd[580]: Goodbye...
Jan 18 12:23:30 octopi systemd[1]: webcamd.service: Succeeded.
Jan 18 12:23:30 octopi systemd[1]: Started the OctoPi webcam daemon with the user specified config.
Jan 18 12:23:31 octopi systemd[1]: webcamd.service: Service RestartSec=100ms expired, scheduling restart.
Jan 18 12:23:31 octopi systemd[1]: webcamd.service: Scheduled restart job, restart counter is at 5.
Jan 18 12:23:31 octopi systemd[1]: Stopped the OctoPi webcam daemon with the user specified config.
Jan 18 12:23:31 octopi systemd[1]: webcamd.service: Start request repeated too quickly.
Jan 18 12:23:31 octopi systemd[1]: webcamd.service: Failed with result 'start-limit-hit'.
Jan 18 12:23:31 octopi systemd[1]: Failed to start the OctoPi webcam daemon with the user specified config.

service webcamd2 status:
shows:

● webcamd2.service - the OctoPi webcam daemon with the user specified config
Loaded: loaded (/etc/systemd/system/webcamd2.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2020-01-18 12:23:25 CET; 13min ago
Process: 374 ExecStart=/root/bin/webcamd2 (code=exited, status=0/SUCCESS)
Main PID: 463 (mjpg_streamer)
Tasks: 3 (limit: 2077)
Memory: 2.0M
CGroup: /system.slice/webcamd2.service
└─463 ./mjpg_streamer -o output_http.so -w ./www-octopi -n -i input_uvc.so -d /dev/VideoLogitech -r 1280x720 -f 10
Jan 18 12:23:24 octopi webcamd2[374]: o: commands.............: disabled
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: HTTP TCP port........: 8080
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: HTTP Listen Address..: (null)
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: username:password....: disabled
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: commands.............: disabled
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: starting input plugin input_uvc.so
Jan 18 12:23:24 octopi mjpg_streamer[463]: MJPG-streamer [463]: starting output plugin: output_http.so (ID: 00)
Jan 18 12:23:25 octopi systemd[1]: Started the OctoPi webcam daemon with the user specified config.
Jan 18 12:23:25 octopi webcamd2[374]: Done bring up all configured video device
Jan 18 12:23:25 octopi webcamd2[374]: Goodbye...

I think the problem in my case is the port 8080, which is assigned to webcamd OR webcamd2 and assigning the 8080 to webcamd and webcamd2 will be a problem.
I have entered port 8081 in /root/bin/webcamd2 and /etc/haproxy/haproxy.cfg in the backend of webcam2.

Does anybody have a idea, how to solve the problem? Otherwise I am lost in space.

Thx Carsten

Keep it to one thread please...

1 Like

Reopened after clarification from OP and title adjustment.

direct after service webcamd2 restart
the status shows:

octoopi:~ $ service webcamd2 status
● webcamd2.service - the OctoPi webcam daemon with the user specified config
Loaded: loaded (/etc/systemd/system/webcamd2.service; enabled; vendor preset: enabled)
Active: activating (start) since Mon 2020-01-20 23:22:03 CET; 648ms ago
Cntrl PID: 1084 (webcamd2)
Tasks: 2 (limit: 2077)
Memory: 1.2M
CGroup: /system.slice/webcamd2.service
β”œβ”€1084 /bin/bash /root/bin/webcamd2
└─1109 sleep 1
Jan 20 23:22:03 octopi webcamd2[1084]: bind: Address already in use
Jan 20 23:22:03 octopi webcamd2[1084]: o: server_thread(): bind(8080) failed
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: www-folder-path......: ./www-octopi/
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: HTTP TCP port........: 8080
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: HTTP Listen Address..: (null)
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: username:password....: disabled
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: commands.............: disabled
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: starting input plugin input_uvc.so
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: starting output plugin: output_http.so (ID: 00)
Jan 20 23:22:03 octopi mjpg_streamer[1108]: MJPG-streamer [1108]: server_thread(): bind(8080) failed

and here you can see that port 8080 is not assignable. (because it is already assigned to webcamd service).
If nobody knows about webcamd ports, does anybody have a forum-address, where I can post this problem?

Thx - Carsten

Each webcam service would need to stream from its own port on tcp. Assuming that the first is on 8080 why not stream the second on 8081?

Yes, that I have tied. 8080 on webcamd and 8081 on webcamd2.
configured in /root/bin/webcamd2:

camera_usb_options="-d /dev/VideoLogitech -r 1280x720 -f 10"
camera_raspi_options="-fps 10"
camera_http_webroot="./www-octopi"
camera_http_options="-p 8081"

and in /etc/haproxy/haproxy.cfg:

backend webcam2
reqrep ^([^\ :])\ /webcam2/(.) \1\ /\2
server webcam1 127.0.0.1:8081
errorfile 503 /etc/haproxy/errors/503-no-webcam.http

is there another place to set 8081 for webcamd2? - Carsten

I'm looking on my OctoPi-imaged Raspberry Pi right now:

ls /etc/systemd/system/webcamd.service # Note the location

[Unit]
Description=the OctoPi webcam daemon with the user specified config

[Service]
WorkingDirectory=/root/bin
ExecStart=/root/bin/webcamd
Restart=always
Type=forking

[Install]
WantedBy=multi-user.target

In theory, you'd need to clone that in that folder as webcamd2.service and edit its ExecStart line so that it's unique from the original.

Here's my /root/bin/webcamd. As you can see, it's nothing like yours. It might be worth introducing a /boot/octopi2.txt file to configure a cloned version of all this.

#!/bin/bash

########################################################################
### DO NOT EDIT THIS FILE TO CHANGE THE CONFIG!!!                    ###
### ---------------------------------------------------------------- ###
### There is no need to edit this file for changing resolution,      ###
### frame rates or any other mjpg-streamer parameters. Please edit   ###
### /boot/octopi.txt instead - that's what it's there for! You can   ###
### even do this with your Pi powered down by directly accessing the ###
### file when using the SD card as thumb drive in your regular       ###
### computer.                                                        ###
########################################################################

MJPGSTREAMER_HOME=/home/pi/mjpg-streamer
MJPGSTREAMER_INPUT_USB="input_uvc.so"
MJPGSTREAMER_INPUT_RASPICAM="input_raspicam.so"

brokenfps_usb_devices=("046d:082b" "1908:2310" "0458:708c" "1e4e:0102" "0471:0311" "038f:6001")

config_dir="/boot/octopi.conf.d"

echo "Starting up webcamDaemon..."
echo ""

cfg_files=()
cfg_files+=/boot/octopi.txt
if [[ -d ${config_dir} ]]; then
  cfg_files+=( `ls ${config_dir}/*.txt` )
fi

array_camera_config=()
array_camera=()
array_camera_usb_options=()
array_camera_usb_device=()
array_camera_raspi_options=()
array_camera_http_webroot=()
array_camera_http_options=()
array_additional_brokenfps_usb_devices=()
array_camera_device=()
array_assigned_device=()

echo "--- Configuration: ----------------------------"
for cfg_file in ${cfg_files[@]}; do
  # init configuration - DO NOT EDIT, USE /boot/octopi.conf.d/*.txt INSTEAD!
  camera="auto"
  camera_usb_options="-r 640x480 -f 10"
  camera_raspi_options="-fps 10"
  camera_http_webroot="./www-octopi"
  camera_http_options="-n"
  additional_brokenfps_usb_devices=()

  if [[ -e ${cfg_file} ]]; then
      source "$cfg_file"
  fi
  usb_options="$camera_usb_options"

  # if webcam device is explicitly given in /boot/octopi.txt, save the path of the device
  # to a variable and remove its parameter from usb_options
  extracted_device=`echo $usb_options | sed 's@.*-d \(/dev/\(video[0-9]\+\|v4l/[^ ]*\)\).*@\1@'`
  if [ "$extracted_device" != "$usb_options" ]
  then
    # the camera options refer to a device, save it in a variable
    # replace video device parameter with empty string and strip extra whitespace
    usb_options=`echo $usb_options | sed 's/\-d \/dev\/\(video[0-9]\+\|v4l\/[^ ]*\)//g' | awk '$1=$1'`
  else
    extracted_device=""
  fi

  # echo configuration
  echo "cfg_file:      $cfg_file"
  echo "camera:        $camera"
  echo "usb options:   $camera_usb_options"
  echo "raspi options: $camera_raspi_options"
  echo "http options:  -w $camera_http_webroot $camera_http_options"
  echo ""
  echo "Explicitly USB device: $extracted_device"
  echo "-----------------------------------------------"
  echo ""

  array_camera_config+=( $cfg_file )
  array_camera+=( $camera )
  array_camera_usb_options+=("$usb_options")
  array_camera_usb_device+=("$extracted_device")
  array_camera_raspi_options+=("$camera_raspi_options")
  array_camera_http_webroot+=("$camera_http_webroot")
  array_camera_http_options+=("$camera_http_options")
  array_camera_brokenfps_usb_devices+=("${brokenfps_usb_devices[*]} ${additional_brokenfps_usb_devices[*]}")
  array_camera_device+=("")
done

# check if array contains a string
function containsString() {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

# cleans up when the script receives a SIGINT or SIGTERM
function cleanup() {
    # make sure that all child processed die when we die
    local pids=$(jobs -pr)
    [ -n "$pids" ] && kill $pids
    exit 0
}

# says goodbye when the script shuts down
function goodbye() {
    # say goodbye
    echo ""
    echo "Goodbye..."
    echo ""
}

# runs MJPG Streamer, using the provided input plugin + configuration
function runMjpgStreamer {
    input=$1
    pushd $MJPGSTREAMER_HOME > /dev/null 2>&1
        echo Running ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input"
        LD_LIBRARY_PATH=. ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input" &
        sleep 1 &
        sleep_pid=$!
        wait ${sleep_pid}
    popd > /dev/null 2>&1
}

# starts up the RasPiCam
function startRaspi {
    logger -s "Starting Raspberry Pi camera"
    runMjpgStreamer "$MJPGSTREAMER_INPUT_RASPICAM $camera_raspi_options"
}

# starts up the USB webcam
function startUsb {
    options="$usb_options"
    device="video0"

    # check for parameter and set the device if it is given as a parameter
    input=$1
    if [[ -n $input ]]; then
        device=`basename "$input"`
    fi

    # add video device into options
    options="$options -d /dev/$device"

    uevent_file="/sys/class/video4linux/$device/device/uevent"
    if [ -e $uevent_file ]; then
        # let's see what kind of webcam we have here, fetch vid and pid...
        product=`cat $uevent_file | grep PRODUCT | cut -d"=" -f2`
        vid=`echo $product | cut -d"/" -f1`
        pid=`echo $product | cut -d"/" -f2`
        vidpid=`printf "%04x:%04x" "0x$vid" "0x$pid"`

        # ... then look if it is in our list of known broken-fps-devices and if so remove
        # the -f parameter from the options (if it's in there, else that's just a no-op)
        for identifier in ${brokenfps_usb_devices[@]};
        do
            if [ "$vidpid" = "$identifier" ]; then
                echo
                echo "Camera model $vidpid is known to not work with -f parameter, stripping it out"
                echo
                options=`echo $options | sed -e "s/\(\s\+\|^\)-f\s\+[0-9]\+//g"`
            fi
        done
    fi

    logger -s "Starting USB webcam"
    runMjpgStreamer "$MJPGSTREAMER_INPUT_USB $options"
}

# make sure our cleanup function gets called when we receive SIGINT, SIGTERM
trap "cleanup" SIGINT SIGTERM
# say goodbye when we EXIT
trap "goodbye" EXIT

# we need this to prevent the later calls to vcgencmd from blocking
# I have no idea why, but that's how it is...
vcgencmd version > /dev/null 2>&1

# keep mjpg streamer running if some camera is attached
while true; do

  # get list of usb video devices into an array
  video_devices=($(find /dev -regextype sed -regex '\/dev/video[0-9]\+' | sort 2> /dev/null))

  # add list of raspi camera into an array
  if [ "`vcgencmd get_camera`" = "supported=1 detected=1" ]; then
    video_devices+=( "raspi" )
  fi

  echo "Found video devices:"
  printf '%s\n' "${video_devices[@]}"

  for scan_mode in "usb" "usb-auto" "raspi" "auto"; do
    camera=$scan_mode
    if [[ "usb-auto" == "$scan_mode" ]]; then
      camera="usb"
    fi
    for ((i=0;i<${#array_camera[@]};i++));  do
      if [[ -z ${array_camera_device[${i}]} ]] && [[ $camera == ${array_camera[${i}]} ]]; then
        camera_config="${array_camera_config[${i}]}"
        usb_options="${array_camera_usb_options[${i}]}"
        camera_usb_device="${array_camera_usb_device[${i}]}"
        camera_raspi_options="${array_camera_raspi_options[${i}]}"
        camera_http_webroot="${array_camera_http_webroot[${i}]}"
        camera_http_options="${array_camera_http_options[${i}]}"
        brokenfps_usb_devices="${array_camera_brokenfps_usb_devices[${i}]}"
        if [[ ${camera_usb_device} ]] && { [[ "usb" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
           # usb device is explicitly set in options
          usb_device_path=`readlink -f ${camera_usb_device}`
          if containsString "$usb_device_path" "${array_camera_device[@]}"; then
            if [[ "auto" != ${scan_mode} ]]; then
              array_camera_device[${i}]="alredy_in_use"
              echo "config file='$camera_config':Video device already in use."
              continue
            fi
          elif containsString "$usb_device_path" "${video_devices[@]}"; then
            array_camera_device[${i}]="$usb_device_path"
            # explicitly set usb device was found in video_devices array, start usb with the found device
            echo "config file='$camera_config':USB device was set in options and found in devices, start MJPG-streamer with the configured USB video device: $usb_device_path"
            startUsb "$usb_device_path"
            continue
          fi
        elif [[ -z ${camera_usb_device} ]] && { [[ "usb-auto" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
          for video_device in "${video_devices[@]}"; do
            if [[ "raspi" != "$video_device" ]]; then
              if containsString "$video_device" "${array_camera_device[@]}"; then
                : #already in use
              else
                array_camera_device[${i}]="$video_device"
                # device is not set explicitly in options, start usb with first found usb camera as the device
                echo "config file='$camera_config':USB device was not set in options, start MJPG-streamer with the first found video device: ${video_device}"
                startUsb "${video_device}"
                break
              fi
            fi
          done
          if [[ -n ${array_camera_device[${i}]} ]]; then
            continue
          fi
        fi
        if [[ "raspi" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; then
          video_device="raspi"
          if containsString "$video_device" "${array_camera_device[@]}"; then
            if [[ "auto" != ${scan_mode} ]]; then
              array_camera_device[${i}]="alredy_in_use"
              echo "config file='$camera_config':RasPiCam device already in use."
            fi
          elif containsString "$video_device" "${video_devices[@]}"; then
            array_camera_device[${i}]="$video_device"
            echo "config file='$camera_config':Start MJPG-streamer with video device: ${video_device}"
            startRaspi
            sleep 30 &
            sleep_pid=$!
            wait ${sleep_pid}
          fi
        fi
      fi
    done
  done
  array_assigned_device=( ${array_camera_device[*]} )
  if [[ ${#array_camera[@]} -eq ${#array_assigned_device[@]} ]]; then
    echo "Done bring up all configured video device"
    exit 0
  else
    echo "Scan again in two minutes"
    sleep 120 &
    sleep_pid=$!
    wait ${sleep_pid}
  fi
done

And then again, it's like... why bother? You're not the only person on here who's spending many hours trying to add multiple webcams to the same Pi computer. Each stream eats of lots of processing time and puts your print job's quality at risk.

Why not instead dedicate one Pi per extra webcam and call it a day? You just offload the work to another Pi Zero which can be moved to anywhere you need it to be.