#!/bin/bash #### Webcamd Core Application. #### webcamd - A webcam Service for multiple Cams and Stream Services. #### #### written by Stephan Wendel aka KwadFan #### Copyright 2021 #### https://github.com/mainsail-crew/crowsnest #### #### This File is distributed under GPLv3 #### #### Version 3 ### Disable shellcheck Errors # shellcheck disable=SC2012,SC2206 # Exit upon Errors set -e # Base Path BASE_CN_PATH="$(dirname "$(readlink -f "${0}")")" ## Import Librarys source "${BASE_CN_PATH}/libs/core.sh" source "${BASE_CN_PATH}/libs/configparser.sh" source "${BASE_CN_PATH}/libs/hwhandler.sh" source "${BASE_CN_PATH}/libs/logging.sh" source "${BASE_CN_PATH}/libs/messages.sh" source "${BASE_CN_PATH}/libs/watchdog.sh" ## Start Stream Service # sleep to prevent cpu cycle spikes function construct_streamer { local stream_server cams cams=($(configured_cams)) log_msg "Try to start configured Cams / Services..." for (( i=0; i<"${#cams[@]}"; i++ )); do stream_server="$(get_param "cam ${cams[$i]}" streamer 2> /dev/null)" if [ "${stream_server}" == "ustreamer" ]; then run_ustreamer "${cams[$i]}" & sleep 8 & sleep_pid="$!" wait "${sleep_pid}" elif [ "${stream_server}" == "rtsp" ]; then run_rtsp "${cams[$i]}" & sleep 8 & sleep_pid="$!" wait "${sleep_pid}" else log_msg "ERROR: Missing 'streamer' parameter in [cam ${cams[$i]}]. Skipping." fi done log_msg "... Done!" } function run_ustreamer { local cam_section ustreamer_bin device port resolution fps custom local raspicam start_param wwwroot cam_section="${1}" ustreamer_bin="$(whereis ustreamer | awk '{print $2}')" device="$(get_param "cam ${cam_section}" device)" port=$(get_param "cam ${cam_section}" port) resolution=$(get_param "cam ${cam_section}" resolution) fps=$(get_param "cam ${cam_section}" max_fps) custom="$(get_param "cam ${cam_section}" custom_flags 2> /dev/null)" raspicam="$(v4l2-ctl --list-devices | grep -A1 -e 'mmal' | \ awk 'NR==2 {print $1}')" check_section "${cam_section}" wwwroot="$(dirname $(readlink -qe $(whereis webcamd)))/ustreamer-www" #Raspicam Workaround if [ "${device}" == "${raspicam}" ]; then start_param=( --host 127.0.0.1 -p "${port}" -m MJPEG --device-timeout=5 --buffers=3 -r "${resolution}" -f "${fps}" --allow-origin=\* --static "${wwwroot}" ) else start_param=( -d "${device}" -r "${resolution}" -f "${fps}" --host 127.0.0.1 -p "${port}" --allow-origin=\* --device-timeout=2 --static "${wwwroot}" ) fi # Custom Flag Handling if [ -n "${custom}" ]; then start_param=(${start_param[@]} "${custom}" ) fi log_msg "Starting ustreamer with Device ${device} ..." echo "Parameters: ${start_param[*]}" | \ log_output "ustreamer [cam ${cam_section}]" # Ustreamer is designed to run even if the device is not ready or readable. # I dont like that! ustreamer has to exit if Cam isnt there. if [ -e "${device}" ]; then "${ustreamer_bin}" ${start_param[*]} 2>&1 | \ log_output "ustreamer [cam ${cam_section}]" else log_msg "ERROR: Start of ustreamer [cam ${cam_section}] failed!" fi } function run_rtsp { local cam_section rtsp_bin device port resolution fps custom local raspicam start_param cam_section="${1}" rtsp_bin="$(whereis v4l2rtspserver | awk '{print $2}')" device="$(get_param "cam ${cam_section}" device)" port=$(get_param "cam ${cam_section}" port) resolution=$(get_param "cam ${cam_section}" resolution) fps=$(get_param "cam ${cam_section}" max_fps) custom="$(get_param "cam ${cam_section}" custom_flags 2> /dev/null)" check_section "${cam_section}" split_res="$(echo "${resolution}" | \ awk -F 'x' '{print "-W "$1 " -H "$2}')" start_param=( -I 0.0.0.0 -P "${port}" "${split_res}" -F "${fps}" \ "${device}" ) # Custom Flag Handling if [ -n "${custom}" ]; then start_param=(${start_param[@]} "${custom}" ) fi log_msg "Starting v4l2rtspserver with Device ${device} ..." echo "Parameters: ${start_param[*]}" | \ log_output "v4l2rtspserver [cam ${cam_section}]" "${rtsp_bin}" ${start_param[*]} 2>&1 | \ log_output "v4l2rtspserver [cam ${cam_section}]" log_msg "ERROR: Start of v4l2rtspserver [cam ${cam_section}] failed!" } #### MAIN ## Args given? if [ "$#" -eq 0 ]; then missing_args_msg exit 1 fi ## Parse Args while getopts ":Vhc:" arg; do case "${arg}" in v ) echo -e "\nwebcamd Version: $(self_version)\n" exit 0 ;; h ) help_msg exit 0 ;; c ) check_cfg "${OPTARG}" WEBCAMD_CFG="${OPTARG}" ;; \?) wrong_args_msg exit 1 ;; esac done develop init_log_entry initial_check construct_streamer ## Loop and Watchdog ## In this case watchdog acts more like a "cable defect detector" ## The User gets a message if Device is lost. clean_watchdog while true ; do webcamd_watchdog sleep 120 & sleep_pid="$!" wait "${sleep_pid}" done