#!/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=/opt/mjpg-streamer MJPGSTREAMER_INPUT_USB="input_uvc.so" MJPGSTREAMER_INPUT_RASPICAM="input_raspicam.so" brokenfps_usb_devices=("046d:082b" "1908:2310" "0458:708c" "0458:6006" "1e4e:0102" "0471:0311" "038f:6001" "046d:0804" "046d:0994" "0ac8:3450") 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 --listen 127.0.0.1" 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 set 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 } # waits for our child processes function awaitChildren() { local pids=$(jobs -pr) for pid in $pids; do wait $pid done } # 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 # There are problems with 0x000137ab firmware on VL805 (Raspberry Pi 4}). # Try to autodetect offending firmware and temporarily fix the issue # by changing power management mode echo "Checking for VL805 (Raspberry Pi 4)..." if [[ -f /usr/bin/vl805 ]]; then VL805_VERSION=$(/usr/bin/vl805) VL805_VERSION=${VL805_VERSION#*: } echo " - version 0x${VL805_VERSION} detected" case "$VL805_VERSION" in 00013701) echo " - nothing to be done. It shouldn't cause USB problems." ;; 000137ab) echo -e " - \e[31mThis version is known to cause problems with USB cameras.\e[39m" echo -e " You may want to downgrade to 0x0013701." echo -e " - [FIXING] Trying the setpci -s 01:00.0 0xD4.B=0x41 hack to mitigate the" echo -e " issue. It disables ASPM L1 on the VL805. Your board may (or may not) get" echo -e " slightly hotter. For details see:" echo -e " https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=244421" setpci -s 01:00.0 0xD4.B=0x41 ;; *) echo " - unknown firmware version. Doing nothing." ;; esac else echo " - It seems that you don't have VL805 (Raspberry Pi 4)." echo " There should be no problems with USB (a.k.a. select() timeout)" fi 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` if [[ -n "$vid" && -n "$pid" ]]; then 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 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 vcgencmd_regex="supported=1 detected=1.*" # Example output matching: supported=1 detected=1, libcamera interfaces=0 if [[ "`vcgencmd get_camera`" =~ $vcgencmd_regex ]]; 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}]="already_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, starting 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, starting 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}]="already_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':Starting 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 bringing up all configured video devices" awaitChildren # reset array_camera_device to empty array_camera_device=() for cam in ${array_camera[@]}; do array_camera_device+=("") done fi echo "Scanning again in two minutes" sleep 120 done