feat!: adds camera-streamer to crowsnest
This introduces camera-streamer as streamer option via `mode: multi` camera-streamer is a feature packed stream service. It is capable to deliver mjpg/snapshots/webrtc and rtsp Limited to raspberry pi sbc's for now. This should also resolv Feature request #51 Feature request #37 Fixes #83 Closes #85 Fixes #89 BREAKING CHANGES: Dropping support for Debian Buster based images and kernels older than 5.15y Dropping RTSP support due aler9/simple-rtsp-server Dropping usage of ffmpeg No support anymore for Raspicam V1 (EOL) Signed-off-by: Stephan Wendel <me@stephanwe.de> Signed-off-by: Stephan Wendel <me@stephanwe.de>
This commit is contained in:
parent
20ed6a8b58
commit
95c1dca13f
4
.gitignore
vendored
4
.gitignore
vendored
@ -19,6 +19,10 @@ test*
|
||||
# tmp file workaround
|
||||
lost*
|
||||
|
||||
# ignore bin paths
|
||||
bin/ustreamer
|
||||
bin/camera-streamer
|
||||
|
||||
# Ignore rtsp-simple-server binary
|
||||
bin/rtsp-simple-server/rtsp*
|
||||
bin/rtsp-simple-server/*.yml
|
||||
|
10
Makefile
10
Makefile
@ -7,7 +7,7 @@
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
|
||||
.PHONY: build buildclean config help install unsinstall
|
||||
.PHONY: build buildclean config help install unsinstall update
|
||||
|
||||
# Setup
|
||||
USER = $(shell whoami)
|
||||
@ -31,6 +31,7 @@ help:
|
||||
@echo " build builds binaries"
|
||||
@echo " buildclean cleans binaries (for recompile)"
|
||||
@echo " clean Removes Installer config"
|
||||
@echo " update Pulls latest updates from repository"
|
||||
@echo ""
|
||||
|
||||
install:
|
||||
@ -40,10 +41,10 @@ uninstall:
|
||||
@bash -c 'tools/uninstall.sh'
|
||||
|
||||
build:
|
||||
$(MAKE) -C $(BIN_FOLDER)
|
||||
bash -c 'bin/build.sh --build'
|
||||
|
||||
buildclean:
|
||||
$(MAKE) -C $(BIN_FOLDER) clean
|
||||
bash -c 'bin/build.sh --clean'
|
||||
|
||||
clean:
|
||||
@if [ -f tools/.config ]; then rm -f tools/.config; fi
|
||||
@ -52,6 +53,9 @@ clean:
|
||||
config:
|
||||
@bash -c 'tools/configure.sh'
|
||||
|
||||
update:
|
||||
@git fetch && git pull
|
||||
|
||||
report:
|
||||
@if [ -f ~/report.txt ]; then rm -f ~/report.txt; fi
|
||||
@bash -c 'tools/dev-helper.sh -a >> ~/report.txt'
|
||||
|
76
bin/Makefile
76
bin/Makefile
@ -1,76 +0,0 @@
|
||||
#### crowsnest - A webcam Service for multiple Cams and Stream Services.
|
||||
####
|
||||
#### Written by Stephan Wendel aka KwadFan <me@stephanwe.de>
|
||||
#### Copyright 2021
|
||||
#### https://github.com/mainsail-crew/crowsnest
|
||||
####
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
#### This Makefile is intended to streamline the build of depending binaries.
|
||||
|
||||
#### DO NOT MODIFY AT ALL! ####
|
||||
|
||||
# Setup
|
||||
.PHONY: all clean ustreamer rtsp webrtc update
|
||||
|
||||
# Paths
|
||||
USTREAMER_PATH = ustreamer
|
||||
USTREAMER_REPO = https://github.com/pikvm/ustreamer.git
|
||||
USTREAMER_OMX_BRANCH = 61ab2a8
|
||||
ARCH = $(shell uname -m)
|
||||
RTSP_PATH = rtsp-simple-server
|
||||
RTSP_ARCH = $(shell uname -m | cut -c '-5' | sed 's/x86_6/amd64/;s/aarch/arm64v8/')
|
||||
RTSP_VERSION = $(shell cat $(RTSP_PATH)/version)
|
||||
DL_RTSP = https://github.com/aler9/rtsp-simple-server/releases/download/$(RTSP_VERSION)/
|
||||
RTSP_ARCHIVE = rtsp-simple-server_$(RTSP_VERSION)_linux_$(RTSP_ARCH).tar.gz
|
||||
|
||||
# Custom Flags
|
||||
MAKEFLAGS += -j$(shell nproc)
|
||||
|
||||
# OpenMAX IL Support
|
||||
OMX_SUPPORT = $(shell [ -d /opt/vc/include ] && echo 1 || echo 0)
|
||||
|
||||
# Ustreamer cloned?
|
||||
USTREAMER_EXIST = $(shell [ -d ustreamer ] > /dev/null && echo 1 || echo 0)
|
||||
|
||||
all:
|
||||
ifeq ($(USTREAMER_EXIST), 0)
|
||||
$(info INFO: ustreamer not found, cloning repository.)
|
||||
$(shell git clone $(USTREAMER_REPO) ustreamer)
|
||||
else
|
||||
$(info INFO: ustreamer found.)
|
||||
endif
|
||||
$(MAKE) ustreamer-bin
|
||||
$(MAKE) rtsp
|
||||
|
||||
# Build ustreamer
|
||||
ustreamer-bin:
|
||||
ifeq ($(OMX_SUPPORT), 1)
|
||||
$(info Compiling ustreamer with OMX Support.)
|
||||
$(info Changening to commit '$(USTREAMER_OMX_BRANCH)' )
|
||||
$(shell cd ustreamer; git reset -q --hard $(USTREAMER_OMX_BRANCH) \
|
||||
; cd ..)
|
||||
WITH_OMX=1 $(MAKE) -C $(USTREAMER_PATH)
|
||||
else
|
||||
$(info Compiling ustreamer without OMX Support.)
|
||||
$(MAKE) -C $(USTREAMER_PATH)
|
||||
endif
|
||||
|
||||
# Download rtsp-simple-server
|
||||
rtsp:
|
||||
$(info Download $(RTSP_ARCHIVE) from $(DL_RTSP))
|
||||
$(shell curl -JLo $(RTSP_PATH)/$(RTSP_ARCHIVE) $(DL_RTSP)$(RTSP_ARCHIVE))
|
||||
$(shell tar -C $(RTSP_PATH) -xf $(RTSP_PATH)/$(RTSP_ARCHIVE))
|
||||
$(shell rm -f $(RTSP_PATH)/$(RTSP_ARCHIVE))
|
||||
$(info Finished.)
|
||||
# Dirty Hack needed to prevent "make: Nothing to be done for 'rtsp'." message
|
||||
@echo > /dev/null
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(USTREAMER_PATH) clean
|
||||
$(info Clean rtsp-simple-server ...)
|
||||
$(shell rm -rf $(RTSP_PATH)/rtsp*)
|
||||
|
||||
update:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
221
bin/build.sh
Executable file
221
bin/build.sh
Executable file
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#### crowsnest - A webcam Service for multiple Cams and Stream Services.
|
||||
####
|
||||
#### Written by Stephan Wendel aka KwadFan <me@stephanwe.de>
|
||||
#### Copyright 2021 - till today
|
||||
#### https://github.com/mainsail-crew/crowsnest
|
||||
####
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
|
||||
# shellcheck enable=require-variable-braces
|
||||
|
||||
### Disable SC2317 due Trap usage
|
||||
# shellcheck disable=SC2317
|
||||
|
||||
# Exit on errors
|
||||
set -Ee
|
||||
|
||||
# Debug
|
||||
# set -x
|
||||
|
||||
# Global vars
|
||||
# Base Path
|
||||
BASE_CN_BIN_PATH="$(dirname "$(readlink -f "${0}")")"
|
||||
|
||||
# Clone Flags
|
||||
CLONE_FLAGS=(--depth=1 --single-branch)
|
||||
|
||||
# Ustreamer repo
|
||||
USTREAMER_PATH="ustreamer"
|
||||
if [[ -z "${CROWSNEST_USTREAMER_REPO_SHIP}" ]]; then
|
||||
CROWSNEST_USTREAMER_REPO_SHIP="https://github.com/pikvm/ustreamer.git"
|
||||
fi
|
||||
if [[ -z "${CROWSNEST_USTREAMER_REPO_BRANCH}" ]]; then
|
||||
CROWSNEST_USTREAMER_REPO_BRANCH="master"
|
||||
fi
|
||||
|
||||
# Camera-streamer repo
|
||||
CSTREAMER_PATH="camera-streamer"
|
||||
if [[ -z "${CROWSNEST_CAMERA_STREAMER_REPO_SHIP}" ]]; then
|
||||
CROWSNEST_CAMERA_STREAMER_REPO_SHIP="https://github.com/ayufan-research/camera-streamer.git"
|
||||
fi
|
||||
if [[ -z "${CROWSNEST_CAMERA_STREAMER_REPO_BRANCH}" ]]; then
|
||||
CROWSNEST_CAMERA_STREAMER_REPO_BRANCH="master"
|
||||
fi
|
||||
|
||||
|
||||
# Paths of repos
|
||||
ALL_PATHS=(
|
||||
"${BASE_CN_BIN_PATH}"/"${USTREAMER_PATH}"
|
||||
"${BASE_CN_BIN_PATH}"/"${CSTREAMER_PATH}"
|
||||
)
|
||||
|
||||
# Helper messages
|
||||
show_help() {
|
||||
printf "Usage %s [options]\n" "$(basename "${0}")"
|
||||
printf "\t-h or --help\t\tShows this help message\n"
|
||||
printf "\t-b or --build\t\tBuild Apps\n"
|
||||
printf "\t-c or --clean\t\tClean Apps\n"
|
||||
printf "\t-d or --delete\t\tDelete cloned Apps\n"
|
||||
printf "\t-r or --reclone\t\tClone Apps again\n\n"
|
||||
}
|
||||
|
||||
## Helper funcs
|
||||
### Check if device is Raspberry Pi
|
||||
is_raspberry_pi() {
|
||||
if [[ -f /proc/device-tree/model ]] &&
|
||||
grep -q "Raspberry" /proc/device-tree/model; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
### Get avail mem
|
||||
get_avail_mem() {
|
||||
grep "MemTotal" /proc/meminfo | awk '{print $2}'
|
||||
}
|
||||
|
||||
## MAIN funcs
|
||||
### Delete repo folder
|
||||
delete_apps() {
|
||||
for path in "${ALL_PATHS[@]}"; do
|
||||
if [[ ! -d "${path}" ]]; then
|
||||
printf "'%s' does not exist! Delete skipped ...\n" "${path}"
|
||||
fi
|
||||
if [[ -d "${path}" ]]; then
|
||||
printf "Deleting '%s' ... \n" "${path}"
|
||||
rm -rf "${path}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
### Clone ustreamer
|
||||
clone_ustreamer() {
|
||||
if [[ -d "${BASE_CN_BIN_PATH}"/"${USTREAMER_PATH}" ]]; then
|
||||
printf "%s already exist ... [SKIPPED]\n" "${USTREAMER_PATH}"
|
||||
return
|
||||
fi
|
||||
git clone "${CROWSNEST_USTREAMER_REPO_SHIP}" \
|
||||
-b "${CROWSNEST_USTREAMER_REPO_BRANCH}" \
|
||||
"${BASE_CN_BIN_PATH}"/"${USTREAMER_PATH}" \
|
||||
"${CLONE_FLAGS[@]}"
|
||||
}
|
||||
|
||||
### Clone camera-streamer
|
||||
clone_cstreamer() {
|
||||
## Special handling because only supported on Raspberry Pi
|
||||
[[ -n "${CROWSNEST_UNATTENDED}" ]] || CROWSNEST_UNATTENDED="0"
|
||||
if [[ "$(is_raspberry_pi)" = "0" ]] && [[ "${CROWSNEST_UNATTENDED}" = "0" ]]; then
|
||||
printf "WARN: Cloning camera-streamer skipped! Device is not supported!"
|
||||
return
|
||||
fi
|
||||
if [[ -d "${BASE_CN_BIN_PATH}"/"${CSTREAMER_PATH}" ]]; then
|
||||
printf "%s already exist ... [SKIPPED]\n" "${CSTREAMER_PATH}"
|
||||
return
|
||||
fi
|
||||
git clone "${CROWSNEST_CAMERA_STREAMER_REPO_SHIP}" \
|
||||
-b "${CROWSNEST_CAMERA_STREAMER_REPO_BRANCH}" \
|
||||
"${BASE_CN_BIN_PATH}"/"${CSTREAMER_PATH}" \
|
||||
"${CLONE_FLAGS[@]}" --recursive
|
||||
}
|
||||
|
||||
### Clone Apps
|
||||
clone_apps() {
|
||||
local apps
|
||||
apps="ustreamer cstreamer"
|
||||
for app in ${apps}; do
|
||||
clone_"${app}"
|
||||
done
|
||||
}
|
||||
|
||||
### Run 'make clean' in cloned folders
|
||||
clean_apps() {
|
||||
for app in "${ALL_PATHS[@]}"; do
|
||||
printf "\nRunning 'make clean' in %s ... \n" "${app}"
|
||||
pushd "${app}" &> /dev/null || exit 1
|
||||
make clean
|
||||
popd &> /dev/null || exit 1
|
||||
done
|
||||
printf "\nRunning 'make clean' ... [DONE]\n"
|
||||
}
|
||||
|
||||
build_apps() {
|
||||
## Determine Ramsize and export MAKEFLAG
|
||||
if [[ "$(get_avail_mem)" -le 524288 ]]; then
|
||||
USE_PROCS=-j1
|
||||
fi
|
||||
if [[ "$(get_avail_mem)" -le 1048576 ]]; then
|
||||
USE_PROCS=-j2
|
||||
fi
|
||||
if [[ "$(get_avail_mem)" -gt 1048576 ]]; then
|
||||
USE_PROCS=-j4
|
||||
fi
|
||||
for path in "${ALL_PATHS[@]}"; do
|
||||
if [[ ! -d "${path}" ]]; then
|
||||
printf "'%s' does not exist! Build skipped ... [WARN]\n" "${path}"
|
||||
fi
|
||||
if [[ -d "${path}" ]]; then
|
||||
printf "Build '%s' using ${USE_PROCS##-j} Cores ... \n" "${path##*/}"
|
||||
pushd "${path}" &> /dev/null || exit 1
|
||||
make "${USE_PROCS}"
|
||||
popd &> /dev/null || exit 1
|
||||
printf "Build '%s' ... [SUCCESS]\n" "${path##*/}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
## MAIN FUNC
|
||||
main() {
|
||||
## Error exit if no args given, show help
|
||||
if [[ $# -eq "0" ]]; then
|
||||
printf "ERROR: No options given ...\n"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
## Error exit if too many args given
|
||||
if [[ $# -gt "1" ]]; then
|
||||
printf "ERROR: Too many options given ...\n"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
## Get opts
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-b|--build)
|
||||
build_apps
|
||||
break
|
||||
;;
|
||||
-c|--clean)
|
||||
clean_apps
|
||||
break
|
||||
;;
|
||||
-d|--delete)
|
||||
delete_apps
|
||||
break
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
break
|
||||
;;
|
||||
-r|--reclone)
|
||||
delete_apps
|
||||
clone_apps
|
||||
break
|
||||
;;
|
||||
*)
|
||||
printf "Unknown option: %s" "${1}"
|
||||
show_help
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
#### MAIN
|
||||
main "${@}"
|
||||
exit 0
|
||||
|
||||
#### EOF
|
@ -1 +0,0 @@
|
||||
v0.20.2
|
32
crowsnest
32
crowsnest
@ -20,18 +20,18 @@ set -Ee
|
||||
BASE_CN_PATH="$(dirname "$(readlink -f "${0}")")"
|
||||
|
||||
## Import Librarys
|
||||
# shellcheck source-path=SCRIPTDIR/libs/
|
||||
source "${BASE_CN_PATH}/libs/configparser.sh"
|
||||
source "${BASE_CN_PATH}/libs/core.sh"
|
||||
source "${BASE_CN_PATH}/libs/hwhandler.sh"
|
||||
source "${BASE_CN_PATH}/libs/init_stream.sh"
|
||||
source "${BASE_CN_PATH}/libs/logging.sh"
|
||||
source "${BASE_CN_PATH}/libs/messages.sh"
|
||||
source "${BASE_CN_PATH}/libs/rtspsimple.sh"
|
||||
source "${BASE_CN_PATH}/libs/ustreamer.sh"
|
||||
source "${BASE_CN_PATH}/libs/v4l2_control.sh"
|
||||
source "${BASE_CN_PATH}/libs/versioncontrol.sh"
|
||||
source "${BASE_CN_PATH}/libs/watchdog.sh"
|
||||
# shellcheck source-path=SCRIPTDIR/../libs/
|
||||
. "${BASE_CN_PATH}/libs/camera-streamer.sh"
|
||||
. "${BASE_CN_PATH}/libs/configparser.sh"
|
||||
. "${BASE_CN_PATH}/libs/core.sh"
|
||||
. "${BASE_CN_PATH}/libs/hwhandler.sh"
|
||||
. "${BASE_CN_PATH}/libs/init_stream.sh"
|
||||
. "${BASE_CN_PATH}/libs/logging.sh"
|
||||
. "${BASE_CN_PATH}/libs/messages.sh"
|
||||
. "${BASE_CN_PATH}/libs/ustreamer.sh"
|
||||
. "${BASE_CN_PATH}/libs/v4l2_control.sh"
|
||||
. "${BASE_CN_PATH}/libs/versioncontrol.sh"
|
||||
. "${BASE_CN_PATH}/libs/watchdog.sh"
|
||||
|
||||
#### MAIN
|
||||
## Args given?
|
||||
@ -41,7 +41,7 @@ if [ "$#" -eq 0 ]; then
|
||||
fi
|
||||
|
||||
## Parse Args
|
||||
while getopts ":vhc:" arg; do
|
||||
while getopts ":vhc:d" arg; do
|
||||
case "${arg}" in
|
||||
v )
|
||||
echo -e "\ncrowsnest Version: $(self_version)\n"
|
||||
@ -55,6 +55,9 @@ while getopts ":vhc:" arg; do
|
||||
check_cfg "${OPTARG}"
|
||||
export CROWSNEST_CFG="${OPTARG}"
|
||||
;;
|
||||
d )
|
||||
set -x
|
||||
;;
|
||||
\?)
|
||||
wrong_args_msg
|
||||
exit 1
|
||||
@ -64,10 +67,7 @@ done
|
||||
|
||||
init_logging
|
||||
initial_check
|
||||
v4l2_control
|
||||
blockyfix
|
||||
construct_streamer
|
||||
brokenfocus
|
||||
|
||||
## Loop and Watchdog
|
||||
## In this case watchdog acts more like a "cable defect detector"
|
||||
|
@ -10,15 +10,14 @@
|
||||
# shellcheck disable=all
|
||||
|
||||
# crowsnest repo
|
||||
[[ -n "$CROWSNEST_REPO_SHIP" ]] || CROWSNEST_REPO_SHIP=https://github.com/mainsail-crew/crowsnest.git
|
||||
[[ -n "$CROWSNEST_REPO_BRANCH" ]] || CROWSNEST_REPO_BRANCH=master
|
||||
[[ -n "$CROWSNEST_REPO_SHIP" ]] || CROWSNEST_REPO_SHIP="https://github.com/mainsail-crew/crowsnest.git"
|
||||
[[ -n "$CROWSNEST_REPO_BRANCH" ]] || CROWSNEST_REPO_BRANCH="master"
|
||||
|
||||
# crowsnest setup
|
||||
[[ -n "$CROWSNEST_DEFAULT_CONF" ]] || CROWSNEST_DEFAULT_CONF="resources/crowsnest.conf"
|
||||
[[ -n "$CROWSNEST_CONFIG_PATH" ]] || CROWSNEST_CONFIG_PATH="/home/${BASE_USER}/printer_data/config"
|
||||
[[ -n "$CROWSNEST_LOG_PATH" ]] || CROWSNEST_LOG_PATH="/home/${BASE_USER}/printer_data/logs"
|
||||
[[ -n "$CROWSNEST_ENV_PATH" ]] || CROWSNEST_ENV_PATH="/home/${BASE_USER}/printer_data/systemd"
|
||||
[[ -n "$CROWSNEST_RASPICAMFIX" ]] || CROWSNEST_RASPICAMFIX="1"
|
||||
[[ -n "$CROWSNEST_ADD_CROWSNEST_MOONRAKER" ]] || CROWSNEST_ADD_CROWSNEST_MOONRAKER="1"
|
||||
[[ -n "$CROWSNEST_MOONRAKER_CONF_PATH" ]] || CROWSNEST_MOONRAKER_CONF_PATH="/home/${BASE_USER}/printer_data/config/moonraker.conf"
|
||||
|
||||
@ -27,6 +26,10 @@
|
||||
[[ -n "$CROWSNEST_USTREAMER_REPO_SHIP" ]] || CROWSNEST_USTREAMER_REPO_SHIP="https://github.com/pikvm/ustreamer.git"
|
||||
[[ -n "$CROWSNEST_USTREAMER_REPO_BRANCH" ]] || CROWSNEST_USTREAMER_REPO_BRANCH="master"
|
||||
|
||||
# camera-streamer
|
||||
[[ -n "$CROWSNEST_CAMERA_STREAMER_REPO_SHIP" ]] || CROWSNEST_CAMERA_STREAMER_REPO_SHIP="https://github.com/ayufan/camera-streamer.git"
|
||||
[[ -n "$CROWSNEST_CAMERA_STREAMER_REPO_BRANCH" ]] || CROWSNEST_CAMERA_STREAMER_REPO_BRANCH="master"
|
||||
|
||||
###########################################################################
|
||||
### DO NOT EDIT BELOW THIS LINE, UNLESS YOU KNOW EXACTLY WHAT HAPPENDS! ###
|
||||
###########################################################################
|
||||
|
117
libs/camera-streamer.sh
Executable file
117
libs/camera-streamer.sh
Executable file
@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
|
||||
#### camera-streamer library
|
||||
|
||||
#### crowsnest - A webcam Service for multiple Cams and Stream Services.
|
||||
####
|
||||
#### Written by Stephan Wendel aka KwadFan <me@stephanwe.de>
|
||||
#### Copyright 2021 - 2022
|
||||
#### https://github.com/mainsail-crew/crowsnest
|
||||
####
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
|
||||
# shellcheck enable=require-variable-braces
|
||||
|
||||
# Exit upon Errors
|
||||
set -Ee
|
||||
|
||||
function run_multi() {
|
||||
local cams
|
||||
cams="${1}"
|
||||
for instance in ${cams} ; do
|
||||
run_ayucamstream "${instance}" &
|
||||
done
|
||||
}
|
||||
|
||||
function run_ayucamstream() {
|
||||
local cam_sec ust_bin dev pt res rtsp rtsp_pt fps cstm start_param
|
||||
local v4l2ctl
|
||||
cam_sec="${1}"
|
||||
ust_bin="${BASE_CN_PATH}/bin/camera-streamer/camera-streamer"
|
||||
dev="$(get_param "cam ${cam_sec}" device)"
|
||||
pt=$(get_param "cam ${cam_sec}" port)
|
||||
res=$(get_param "cam ${cam_sec}" resolution)
|
||||
fps=$(get_param "cam ${cam_sec}" max_fps)
|
||||
rtsp=$(get_param "cam ${cam_sec}" enable_rtsp)
|
||||
rtsp_pt=$(get_param "cam ${cam_sec}" rtsp_port)
|
||||
cstm="$(get_param "cam ${cam_sec}" custom_flags 2> /dev/null)"
|
||||
## construct start parameter
|
||||
# set http port
|
||||
#
|
||||
start_param=( --http-port="${pt}" )
|
||||
|
||||
# Set device
|
||||
start_param+=( --camera-path="${dev}" )
|
||||
|
||||
# Detect libcamera device and add start param accordingly
|
||||
if [[ "${dev}" =~ "/base/soc" ]]; then
|
||||
start_param+=( --camera-type=libcamera )
|
||||
start_param+=( --camera-format=YUYV )
|
||||
fi
|
||||
|
||||
if [[ "${dev}" =~ "/dev/video" ]] ||
|
||||
[[ "${dev}" =~ "/dev/v4l/" ]]; then
|
||||
start_param+=( --camera-type=v4l2 )
|
||||
fi
|
||||
|
||||
# Use MJPEG Hardware encoder if possible
|
||||
if [ "$(detect_mjpeg "${cam_sec}")" = "1" ] &&
|
||||
[[ ! "${dev}" =~ "/base/soc" ]]; then
|
||||
start_param+=( --camera-format=MJPG )
|
||||
fi
|
||||
|
||||
# Set resolution
|
||||
get_height_val() {
|
||||
(sed 's/#.*//' | cut -d'x' -f2) <<< "${res}"
|
||||
}
|
||||
get_width_val() {
|
||||
(sed 's/#.*//' | cut -d'x' -f1) <<< "${res}"
|
||||
}
|
||||
|
||||
# Set snapshot heigth to 1080p by default
|
||||
start_param+=( --camera-snapshot.height=1080 )
|
||||
|
||||
start_param+=( --camera-width="$(get_width_val)" )
|
||||
start_param+=( --camera-height="$(get_height_val)" )
|
||||
|
||||
# Set FPS
|
||||
start_param+=( --camera-fps="${fps}" )
|
||||
|
||||
# Enable rtsp, if set true
|
||||
if [[ -n "${rtsp}" ]] && [[ "${rtsp}" == "true" ]]; then
|
||||
# ensure a port is set
|
||||
start_param+=( --rtsp-port="${rtsp_pt:-8554}" )
|
||||
fi
|
||||
|
||||
# Enable camera-auto_reconnect by default
|
||||
start_param+=( --camera-auto_reconnect=1 )
|
||||
|
||||
# Custom Flag Handling (append to defaults)
|
||||
if [[ -n "${cstm}" ]]; then
|
||||
start_param+=( "${cstm}" )
|
||||
fi
|
||||
|
||||
# v4l2 option handling
|
||||
v4l2ctl="$(get_param "cam ${cam_sec}" v4l2ctl)"
|
||||
if [ -n "${v4l2ctl}" ]; then
|
||||
IFS="," read -ra opt < <(echo "${v4l2ctl}" | tr -d " "); unset IFS
|
||||
log_msg "V4L2 Control: Handling done by camera-streamer ..."
|
||||
log_msg "V4L2 Control: Trying to set: ${v4l2ctl}"
|
||||
# loop through options
|
||||
for param in "${opt[@]}"; do
|
||||
start_param+=( --camera-options="${param}" )
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Log start_param
|
||||
log_msg "Starting camera-streamer with Device ${dev} ..."
|
||||
echo "Parameters: ${start_param[*]}" | \
|
||||
log_output "camera-streamer [cam ${cam_sec}]"
|
||||
# Start camera-streamer
|
||||
echo "${start_param[*]}" | xargs "${ust_bin}" 2>&1 | \
|
||||
log_output "camera-streamer [cam ${cam_sec}]"
|
||||
# Should not be seen else failed.
|
||||
log_msg "ERROR: Start of camera-streamer [cam ${cam_sec}] failed!"
|
||||
}
|
@ -26,6 +26,7 @@ function get_param {
|
||||
param="${2}"
|
||||
crudini --get "${cfg}" "${section}" "${param}" 2> /dev/null | \
|
||||
sed 's/\#.*//;s/[[:space:]]*$//'
|
||||
return
|
||||
}
|
||||
|
||||
# Check for existing file
|
||||
@ -34,6 +35,8 @@ function check_cfg {
|
||||
if [ ! -r "${1}" ]; then
|
||||
log_msg "ERROR: No Configuration File found. Exiting!"
|
||||
exit 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
@ -46,12 +49,14 @@ function configured_cams {
|
||||
cams+=("${i}")
|
||||
done
|
||||
echo "${cams[@]}"
|
||||
return
|
||||
}
|
||||
|
||||
# Checks [cam <nameornumber>] if all needed configuration sections are present
|
||||
# call check_section <nameornumber> ex.: check_section foobar
|
||||
function check_section {
|
||||
local section exist param must_exist missing
|
||||
local section exist param
|
||||
local -a must_exist missing
|
||||
section="cam ${1}"
|
||||
# Ignore missing custom flags
|
||||
exist="$(crudini --existing=param --get "${CROWSNEST_CFG}" "${section}" \
|
||||
@ -67,15 +72,22 @@ function check_section {
|
||||
fi
|
||||
done
|
||||
must_exist=(mode port device resolution max_fps)
|
||||
missing="$(echo "${param[@]}" "${must_exist[@]}" | \
|
||||
tr ' ' '\n' | sort | uniq -u)"
|
||||
for i in "${missing[@]}"; do
|
||||
if [ -n "${i}" ]; then
|
||||
log_msg "ERROR: Parameter ${missing} not found in \
|
||||
Section [${section}]. Start skipped!"
|
||||
else
|
||||
log_msg "INFO: Configuration of Section [${section}] looks good. \
|
||||
Continue..."
|
||||
missing=()
|
||||
for i in "${must_exist[@]}"; do
|
||||
if [[ -z "$(get_param "${section}" "${i}")" ]]; then
|
||||
missing+=("${i}")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${#missing[@]}" != "0" ]]; then
|
||||
for param in "${missing[@]}"; do
|
||||
log_msg "ERROR: Parameter ${param} not found in Section [${section}]."
|
||||
done
|
||||
log_msg "ERROR: Please check your configuration!"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${#missing[@]}" == "0" ]]; then
|
||||
log_msg "INFO: Configuration of Section [${section}] looks good. Continue ..."
|
||||
fi
|
||||
return
|
||||
}
|
||||
|
40
libs/core.sh
40
libs/core.sh
@ -66,7 +66,7 @@ function shutdown {
|
||||
function check_dep {
|
||||
local dep
|
||||
dep="$(whereis "${1}" | awk '{print $2}')"
|
||||
if [ -z "${dep}" ]; then
|
||||
if [[ -z "${dep}" ]]; then
|
||||
log_msg "Dependency: '${1}' not found. Exiting!"
|
||||
exit 1
|
||||
else
|
||||
@ -75,30 +75,25 @@ function check_dep {
|
||||
}
|
||||
|
||||
function check_apps {
|
||||
local paths
|
||||
paths=(
|
||||
bin/ustreamer/ustreamer
|
||||
bin/rtsp-simple-server/rtsp-simple-server
|
||||
)
|
||||
for chk in "${paths[@]}"; do
|
||||
if [ -x "${BASE_CN_PATH}/${chk}" ]; then
|
||||
log_msg "Dependency: '$(echo "${chk}" | cut -d '/' -f3)' found in ${chk}."
|
||||
local cstreamer ustreamer
|
||||
ustreamer="bin/ustreamer/ustreamer"
|
||||
cstreamer="bin/camera-streamer/camera-streamer"
|
||||
|
||||
if [[ -x "${BASE_CN_PATH}/${ustreamer}" ]]; then
|
||||
log_msg "Dependency: '${ustreamer##*/}' found in ${ustreamer}."
|
||||
else
|
||||
log_msg "Dependency: '$(echo "${chk}" | cut -d '/' -f3)' not found. Exiting!"
|
||||
log_msg "Dependency: '${ustreamer##*/}' not found. Exiting!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# checks availability of OpenMax IL feature on host and in apps.
|
||||
# 0 = false / 1 = true
|
||||
function check_omx {
|
||||
if [ -d "/opt/vc/include" ] &&
|
||||
[ ! "$(ffmpeg -hide_banner -buildconf | grep -c 'omx')" = "0" ] &&
|
||||
[ "$("${BASE_CN_PATH}"/bin/ustreamer/ustreamer --features | grep -c '\+ WITH_OMX')" = "1" ]; then
|
||||
echo "1"
|
||||
## Avoid dependency check if non rpi sbc
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
if [[ -x "${BASE_CN_PATH}/${cstreamer}" ]]; then
|
||||
log_msg "Dependency: '${cstreamer##*/}' found in ${cstreamer}."
|
||||
else
|
||||
echo "0"
|
||||
log_msg "Dependency: '${cstreamer##*/}' not found. Exiting!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@ -110,9 +105,7 @@ function initial_check {
|
||||
log_msg "INFO: Checking Dependencys"
|
||||
check_dep "crudini"
|
||||
check_dep "find"
|
||||
check_dep "logger"
|
||||
check_dep "xargs"
|
||||
check_dep "ffmpeg"
|
||||
check_apps
|
||||
versioncontrol
|
||||
# print cfg if ! "${CROWSNEST_LOG_LEVEL}": quiet
|
||||
@ -121,8 +114,7 @@ function initial_check {
|
||||
print_cfg
|
||||
fi
|
||||
fi
|
||||
# in systemd show always config file
|
||||
logger -t crowsnest -f "${CROWSNEST_CFG}"
|
||||
log_msg "INFO: Detect available Devices"
|
||||
print_cams
|
||||
return
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ function detect_avail_cams {
|
||||
count="$(echo "${avail}" | wc -l)"
|
||||
if [[ -d "/dev/v4l/by-id/" ]] &&
|
||||
[[ -n "${avail}" ]]; then
|
||||
log_msg "INFO: Found ${count} available camera(s)"
|
||||
log_msg "INFO: Found ${count} available v4l2 (UVC) camera(s)"
|
||||
echo "${avail}" | while read -r v4l; do
|
||||
realpath=$(readlink -e "${v4l}")
|
||||
log_msg "${v4l} -> ${realpath}"
|
||||
@ -37,74 +37,69 @@ function detect_avail_cams {
|
||||
fi
|
||||
}
|
||||
|
||||
function detect_avail_csi {
|
||||
local avail count realpath
|
||||
avail="$(find /dev/v4l/by-path/ -iname "*csi*index0" 2> /dev/null)"
|
||||
count="$(echo "${avail}" | wc -l)"
|
||||
if [[ -d "/dev/v4l/by-path/" ]] &&
|
||||
[[ -n "${avail}" ]]; then
|
||||
log_msg "INFO: Found ${count} available csi device(s)"
|
||||
echo "${avail}" | while read -r csi; do
|
||||
realpath=$(readlink -e "${csi}")
|
||||
log_msg "${csi} -> ${realpath}"
|
||||
done
|
||||
else
|
||||
log_msg "INFO: No usable CSI Devices found."
|
||||
fi
|
||||
}
|
||||
|
||||
# Used for "verbose" and "debug" logging in logging.sh
|
||||
## Used for "verbose" and "debug" logging in logging.sh
|
||||
function list_cam_formats {
|
||||
local device formats
|
||||
local device prefix
|
||||
device="${1}"
|
||||
formats="$(v4l2-ctl -d "${device}" --list-formats-ext | sed '1,3d')"
|
||||
prefix="$(date +'[%D %T]') crowsnest:"
|
||||
log_msg "Supported Formats:"
|
||||
echo "${formats}" | while read -r i; do
|
||||
log_msg "\t\t${i}"
|
||||
done
|
||||
while read -r i; do
|
||||
printf "%s\t\t%s\n" "${prefix}" "${i}" >> "${CROWSNEST_LOG_PATH}"
|
||||
done < <(v4l2-ctl -d "${device}" --list-formats-ext | sed '1,3d')
|
||||
}
|
||||
|
||||
function list_cam_v4l2ctrls {
|
||||
local device ctrls
|
||||
local device prefix
|
||||
device="${1}"
|
||||
ctrls="$(v4l2-ctl -d "${device}" --list-ctrls-menus)"
|
||||
prefix="$(date +'[%D %T]') crowsnest:"
|
||||
log_msg "Supported Controls:"
|
||||
echo "${ctrls}" | while read -r i; do
|
||||
log_msg "\t\t${i}"
|
||||
done
|
||||
while read -r i; do
|
||||
printf "%s\t\t%s\n" "${prefix}" "${i}" >> "${CROWSNEST_LOG_PATH}"
|
||||
done < <(v4l2-ctl -d "${device}" --list-ctrls-menus)
|
||||
}
|
||||
|
||||
# Determine connected "raspicam" device
|
||||
function detect_raspicam {
|
||||
## Determine connected libcamera (CSI) device
|
||||
function detect_libcamera {
|
||||
local avail
|
||||
if [[ -f /proc/device-tree/model ]] &&
|
||||
grep -q "Raspberry" /proc/device-tree/model; then
|
||||
avail="$(vcgencmd get_camera | awk -F '=' '{ print $3 }' | cut -d',' -f1)"
|
||||
else
|
||||
avail="0"
|
||||
vcgencmd get_camera | grep -c "libcamera interfaces=1" || true
|
||||
fi
|
||||
echo "${avail}"
|
||||
}
|
||||
|
||||
function dev_is_raspicam {
|
||||
v4l2-ctl --list-devices | grep -A1 -e 'mmal' | \
|
||||
awk 'NR==2 {print $1}'
|
||||
## Spit /base/soc path for libcamera device
|
||||
function get_libcamera_path {
|
||||
if [[ -f /proc/device-tree/model ]] &&
|
||||
[[ -x "$(command -v libcamera-hello)" ]]; then
|
||||
libcamera-hello --list-cameras | sed '1,2d' \
|
||||
| grep "\(/base/*\)" | cut -d"(" -f2 | tr -d '$)'
|
||||
fi
|
||||
}
|
||||
|
||||
# Determine if cam has H.264 Hardware encoder
|
||||
# call detect_h264 <nameornumber> ex.: detect_h264 foobar
|
||||
# returns 1 = true / 0 = false ( numbers are strings! not int!)
|
||||
## Determine if cam has H.264 Hardware encoder
|
||||
## call detect_h264 <nameornumber> ex.: detect_h264 foobar
|
||||
## returns 1 = true / 0 = false ( numbers are strings! not int!)
|
||||
function detect_h264 {
|
||||
local dev
|
||||
dev="$(get_param "cam ${1}" device)"
|
||||
v4l2-ctl -d "${dev}" --list-formats-ext | grep -c "[hH]264"
|
||||
}
|
||||
|
||||
# Determine if cam has MJPEG Hardware encoder
|
||||
# call detect_mjpeg <nameornumber> ex.: detect_mjpeg foobar
|
||||
# returns 1 = true / 0 = false ( numbers are strings! not int!)
|
||||
## Determine if cam has MJPEG Hardware encoder
|
||||
## call detect_mjpeg <nameornumber> ex.: detect_mjpeg foobar
|
||||
## returns 1 = true / 0 = false ( numbers are strings! not int!)
|
||||
function detect_mjpeg {
|
||||
local dev
|
||||
dev="$(get_param "cam ${1}" device)"
|
||||
v4l2-ctl -d "${dev}" --list-formats-ext | grep -c "Motion-JPEG, compressed"
|
||||
}
|
||||
|
||||
## Check if device is raspberry sbc
|
||||
is_raspberry_pi() {
|
||||
if [[ -f /proc/device-tree/model ]] &&
|
||||
grep -q "Raspberry" /proc/device-tree/model; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
@ -26,12 +26,18 @@ function construct_streamer {
|
||||
mode="$(get_param "cam ${cams}" mode)"
|
||||
check_section "${cams}"
|
||||
case ${mode} in
|
||||
[mM]ulti)
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
MULTI_INSTANCES+=( "${cams}" )
|
||||
else
|
||||
log_msg "WARN: Mode 'multi' is not supported on your device!"
|
||||
log_msg "WARN: Falling back to Mode 'mjpg'"
|
||||
MJPG_INSTANCES+=( "${cams}" )
|
||||
fi
|
||||
;;
|
||||
mjpg | mjpeg)
|
||||
MJPG_INSTANCES+=( "${cams}" )
|
||||
;;
|
||||
rtsp)
|
||||
RTSP_INSTANCES+=( "${cams}" )
|
||||
;;
|
||||
?|*)
|
||||
unknown_mode_msg
|
||||
MJPG_INSTANCES+=( "${cams}" )
|
||||
@ -39,12 +45,12 @@ function construct_streamer {
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ "${#MULTI_INSTANCES[@]}" != "0" ]; then
|
||||
run_multi "${MULTI_INSTANCES[*]}"
|
||||
fi
|
||||
if [ "${#MJPG_INSTANCES[@]}" != "0" ]; then
|
||||
run_mjpg "${MJPG_INSTANCES[*]}"
|
||||
fi
|
||||
if [ "${#RTSP_INSTANCES[@]}" != "0" ]; then
|
||||
run_rtsp "${RTSP_INSTANCES[*]}"
|
||||
fi
|
||||
sleep 2 & sleep_pid="$!" ; wait "${sleep_pid}"
|
||||
log_msg " ... Done!"
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ function log_msg {
|
||||
local msg prefix
|
||||
msg="${1}"
|
||||
prefix="$(date +'[%D %T]') crowsnest:"
|
||||
echo -e "${prefix} ${msg}" | tr -s ' ' | tee -a "${CROWSNEST_LOG_PATH}" 2>&1
|
||||
echo -e "${msg}" | logger -t crowsnest
|
||||
printf "%s %s\n" "${prefix}" "${msg}" >> "${CROWSNEST_LOG_PATH}"
|
||||
printf "%s\n" "${msg}"
|
||||
}
|
||||
|
||||
#call '| log_output "<prefix>"'
|
||||
@ -73,49 +73,36 @@ function log_output {
|
||||
if [[ "${CROWSNEST_LOG_LEVEL}" = "debug" ]]; then
|
||||
log_msg "${prefix}: ${line}"
|
||||
fi
|
||||
if [[ -n "${line}" ]]; then
|
||||
# needed to prettify ustreamers output
|
||||
echo "${line//^--/ustreamer}" | logger -t crowsnest
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function print_cfg {
|
||||
local prefix
|
||||
prefix="\t\t"
|
||||
prefix="$(date +'[%D %T]') crowsnest:"
|
||||
log_msg "INFO: Print Configfile: '${CROWSNEST_CFG}'"
|
||||
(sed '/^#.*/d;/./,$!d' | cut -d'#' -f1) < "${CROWSNEST_CFG}" | \
|
||||
while read -r line; do
|
||||
log_msg "${prefix}${line}"
|
||||
printf "%s\t\t%s\n" "${prefix}" "${line}" >> "${CROWSNEST_LOG_PATH}"
|
||||
printf "\t\t%s\n" "${line}"
|
||||
done
|
||||
}
|
||||
|
||||
function print_cams {
|
||||
local csi raspicam total v4l
|
||||
local total v4l
|
||||
v4l="$(find /dev/v4l/by-id/ -iname "*index0" 2> /dev/null | wc -l)"
|
||||
csi="$(find /dev/v4l/by-path/ -iname "*csi*index0" 2> /dev/null | wc -l)"
|
||||
total="$((v4l+$(detect_raspicam)+csi))"
|
||||
if [[ "${total}" -eq 0 ]]; then
|
||||
total="$((v4l+($(detect_libcamera))))"
|
||||
if [ "${total}" -eq 0 ]; then
|
||||
log_msg "ERROR: No usable Devices Found. Stopping $(basename "${0}")."
|
||||
exit 1
|
||||
else
|
||||
log_msg "INFO: Found ${total} total available Device(s)"
|
||||
fi
|
||||
if [[ "$(detect_raspicam)" -ne 0 ]]; then
|
||||
raspicam="$(v4l2-ctl --list-devices | grep -A1 -e 'mmal' | \
|
||||
awk 'NR==2 {print $1}')"
|
||||
log_msg "Detected 'Raspicam' Device -> ${raspicam}"
|
||||
if [[ ! "${CROWSNEST_LOG_LEVEL}" = "quiet" ]]; then
|
||||
list_cam_formats "${raspicam}"
|
||||
list_cam_v4l2ctrls "${raspicam}"
|
||||
fi
|
||||
if [[ "$(detect_libcamera)" -ne 0 ]]; then
|
||||
log_msg "Detected 'libcamera' device -> $(get_libcamera_path)"
|
||||
fi
|
||||
if [[ -d "/dev/v4l/by-id/" ]]; then
|
||||
detect_avail_cams
|
||||
fi
|
||||
if [[ -d "/dev/v4l/by-path" ]]; then
|
||||
detect_avail_csi
|
||||
fi
|
||||
}
|
||||
|
||||
function print_host {
|
||||
@ -156,12 +143,3 @@ function print_host {
|
||||
log_msg "Host Info: Diskspace (avail. / total): ${disksize}"
|
||||
fi
|
||||
}
|
||||
|
||||
function debug_msg {
|
||||
local prefix
|
||||
prefix="Develop -- DEBUG:"
|
||||
while read -r msg; do
|
||||
log_msg "${prefix} ${msg}"
|
||||
echo -e "${msg}" | logger -t crowsnest
|
||||
done <<< "${1}"
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ function help_msg {
|
||||
|
||||
function deprecated_msg_1 {
|
||||
log_msg "Parameter 'streamer' is deprecated!"
|
||||
log_msg "Please use mode: [ mjpg | rtsp ]"
|
||||
log_msg "Please use mode: [ mjpg | multi ]"
|
||||
log_msg "ERROR: Please update your crowsnest.conf! Stopped."
|
||||
}
|
||||
|
||||
@ -48,15 +48,6 @@ function unknown_mode_msg {
|
||||
log_msg "WARN: Using 'mode: mjpg' as fallback!"
|
||||
}
|
||||
|
||||
function provides_omx_msg {
|
||||
if [ "$(check_omx)" -eq 1 ]; then
|
||||
log_msg "INFO: System provides OpenMAX IL features."
|
||||
else
|
||||
log_msg "WARN: System does not provide OpenMAX IL features."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
## v4l2_control lib
|
||||
function detected_broken_dev_msg {
|
||||
log_msg "WARN: Detected 'brokenfocus' device."
|
||||
|
@ -1,80 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#### ustreamer library
|
||||
|
||||
#### crowsnest - A webcam Service for multiple Cams and Stream Services.
|
||||
####
|
||||
#### Written by Stephan Wendel aka KwadFan <me@stephanwe.de>
|
||||
#### Copyright 2021
|
||||
#### https://github.com/mainsail-crew/crowsnest
|
||||
####
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
|
||||
# shellcheck enable=require-variable-braces
|
||||
|
||||
# Exit upon Errors
|
||||
set -Ee
|
||||
|
||||
run_rtsp() {
|
||||
local cams
|
||||
cams="${1}"
|
||||
if [[ -z "$(pidof rtsp-simple-server)" ]]; then
|
||||
run_rtsp_srv &
|
||||
fi
|
||||
for instance in ${cams} ; do
|
||||
run_ffmpeg "${instance}" &
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
run_ffmpeg() {
|
||||
local cam_section ffmpeg_bin start_param
|
||||
cam_section="${1}"
|
||||
ffmpeg_bin="$(whereis ffmpeg | awk '{print $2}')"
|
||||
dev="$(get_param "cam ${cam_section}" device)"
|
||||
# Construct start_param
|
||||
start_param=( -nostdin -hide_banner -f video4linux2 )
|
||||
if [[ "$(detect_h264 "${cam_section}")" = "1" ]]; then
|
||||
start_param+=( -input_format h264 -pix_fmt h264 )
|
||||
else
|
||||
start_param+=( -input_format yuyv422 )
|
||||
fi
|
||||
start_param+=(
|
||||
-video_size "$(get_param "cam ${cam_section}" resolution)"
|
||||
-framerate "$(get_param "cam ${cam_section}" max_fps)"
|
||||
-i "${dev}"
|
||||
)
|
||||
if [[ "$(detect_h264 "${cam_section}")" -eq 0 ]] &&
|
||||
[[ "$(check_omx)" -eq 1 ]]; then
|
||||
start_param+=( -c:v h264_omx -b:v 8M )
|
||||
else
|
||||
start_param+=( -c:v copy )
|
||||
fi
|
||||
start_param+=(-f rtsp -rtsp_transport tcp rtsp://localhost:8554/"${cam_section}" )
|
||||
# Log start_param
|
||||
log_msg "Starting ffmpeg (rtsp stream source) with Device ${dev} ..."
|
||||
echo "Parameters: ${start_param[*]}" | \
|
||||
log_output "ffmpeg (rtsp stream source) [cam ${cam_section}]"
|
||||
# Start ffmpeg
|
||||
echo "${start_param[*]}" | xargs "${ffmpeg_bin}" 2>&1 | \
|
||||
log_output "ffmpeg (rtsp stream source) [cam ${cam_section}]"
|
||||
# Should not be seen else failed.
|
||||
log_msg "ERROR: Start of ffmpeg (rtsp stream source) [cam ${cam_section}] failed!"
|
||||
}
|
||||
|
||||
run_rtsp_srv() {
|
||||
local rtsp_bin config
|
||||
rtsp_bin="${BASE_CN_PATH}/bin/rtsp-simple-server/rtsp-simple-server"
|
||||
config="${BASE_CN_PATH}/resources/crowsnest-rtsp.yml"
|
||||
log_msg "Starting rtsp-simple-server with config ${config} ..."
|
||||
echo "Config file: ${config}" | \
|
||||
log_output "rtsp-simple-server [INFO]"
|
||||
# Start rtsp-simple-server
|
||||
# Have to use this dirty bash hack to get output to logfile.
|
||||
"${rtsp_bin}" "${config}" &> >(log_output "rtsp-simple-server")
|
||||
# Should not be seen else failed.
|
||||
log_msg "ERROR: Start of rtsp-simple failed!"
|
||||
echo "Config file: ${config}" | \
|
||||
log_output "rtsp-simple-server [ERROR]"
|
||||
}
|
@ -18,10 +18,13 @@ set -Ee
|
||||
|
||||
run_mjpg() {
|
||||
local cams
|
||||
v4l2_control
|
||||
cams="${1}"
|
||||
for instance in ${cams} ; do
|
||||
run_ustreamer "${instance}" &
|
||||
done
|
||||
brokenfocus
|
||||
return
|
||||
}
|
||||
|
||||
run_ustreamer() {
|
||||
@ -29,9 +32,9 @@ run_ustreamer() {
|
||||
cam_sec="${1}"
|
||||
ust_bin="${BASE_CN_PATH}/bin/ustreamer/ustreamer"
|
||||
dev="$(get_param "cam ${cam_sec}" device)"
|
||||
pt=$(get_param "cam ${cam_sec}" port)
|
||||
res=$(get_param "cam ${cam_sec}" resolution)
|
||||
fps=$(get_param "cam ${cam_sec}" max_fps)
|
||||
pt="$(get_param "cam ${cam_sec}" port)"
|
||||
res="$(get_param "cam ${cam_sec}" resolution)"
|
||||
fps="$(get_param "cam ${cam_sec}" max_fps)"
|
||||
cstm="$(get_param "cam ${cam_sec}" custom_flags 2> /dev/null)"
|
||||
noprx="$(get_param "crowsnest" no_proxy 2> /dev/null)"
|
||||
# construct start parameter
|
||||
@ -41,17 +44,15 @@ run_ustreamer() {
|
||||
else
|
||||
start_param=( --host 127.0.0.1 -p "${pt}" )
|
||||
fi
|
||||
#Raspicam Workaround
|
||||
if [[ "${dev}" = "$(dev_is_raspicam)" ]]; then
|
||||
start_param+=( -m MJPEG --device-timeout=5 --buffers=3 )
|
||||
else
|
||||
start_param+=( -d "${dev}" --device-timeout=2 )
|
||||
|
||||
# Use MJPEG Hardware encoder if possible
|
||||
if [ "$(detect_mjpeg "${cam_sec}")" = "1" ]; then
|
||||
start_param+=( -m MJPEG --encoder=HW )
|
||||
fi
|
||||
fi
|
||||
|
||||
# set max framerate
|
||||
start_param+=( -r "${res}" -f "${fps}" )
|
||||
|
||||
# webroot & allow crossdomain requests
|
||||
start_param+=( --allow-origin=\* --static "${BASE_CN_PATH}/ustreamer-www" )
|
||||
# Custom Flag Handling (append to defaults)
|
||||
|
@ -36,7 +36,7 @@ function v4l2_control {
|
||||
v4c_log_msg "Device: [cam ${cam}]"
|
||||
v4c_log_msg "Options: ${v4l2ctl}"
|
||||
# Split options to array
|
||||
IFS=',' read -ra opt < <(echo "${v4l2ctl}"); unset IFS
|
||||
IFS="," read -ra opt < <(echo "${v4l2ctl}" | tr -d " "); unset IFS
|
||||
# loop through options
|
||||
for param in "${opt[@]}"; do
|
||||
# parameter available for device
|
||||
@ -136,30 +136,3 @@ function brokenfocus {
|
||||
main
|
||||
|
||||
}
|
||||
|
||||
# This function is to set bitrate on raspicams.
|
||||
# If raspicams set to variable bitrate, they tend to show
|
||||
# a "block-like" view after reboots
|
||||
# To prevent that blockyfix should apply constant bitrate befor start of ustreamer
|
||||
# See https://github.com/mainsail-crew/crowsnest/issues/33
|
||||
function blockyfix {
|
||||
local dev v4l2ctl
|
||||
|
||||
# call set_bitrate <device>
|
||||
function set_bitrate {
|
||||
v4l2-ctl -d "${1}" -c video_bitrate_mode=1 2> /dev/null
|
||||
v4l2-ctl -d "${1}" -c video_bitrate=15000000 2> /dev/null
|
||||
}
|
||||
|
||||
for cam in $(configured_cams); do
|
||||
dev="$(get_param "cam ${cam}" device)"
|
||||
v4l2ctl="$(get_param "cam ${cam}" v4l2ctl)"
|
||||
if [ "${dev}" = "$(dev_is_raspicam)" ]; then
|
||||
if [ -z "${v4l2ctl}" ] ||
|
||||
[ "$(grep -c "video_bitrate" <<< "${v4l2ctl}")" == "0" ]; then
|
||||
set_bitrate "${dev}"
|
||||
blockyfix_msg_1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
@ -18,15 +18,15 @@
|
||||
# Exit upon Errors
|
||||
set -Ee
|
||||
|
||||
function versioncontrol {
|
||||
versioncontrol() {
|
||||
|
||||
function vc_log_msg {
|
||||
vc_log_msg() {
|
||||
log_msg "Version Control: ${1}"
|
||||
}
|
||||
|
||||
function get_ustreamer_version {
|
||||
get_ustreamer_version() {
|
||||
local cur_ver avail_ver
|
||||
pushd "${BASE_CN_PATH}"/bin/ustreamer || exit 1
|
||||
pushd "${BASE_CN_PATH}"/bin/ustreamer &> /dev/null || exit 1
|
||||
avail_ver="$(git describe --tags --always)"
|
||||
cur_ver="v$("${PWD}"/ustreamer -v)"
|
||||
if [[ "${cur_ver}" == "${avail_ver}" ]]; then
|
||||
@ -35,24 +35,28 @@ function versioncontrol {
|
||||
if [[ "${cur_ver}" != "${avail_ver}" ]]; then
|
||||
vc_log_msg "ustreamer new version available: ${avail_ver} (${cur_ver})."
|
||||
fi
|
||||
popd || exit 1
|
||||
popd &> /dev/null || exit 1
|
||||
}
|
||||
|
||||
function get_rtsp_version {
|
||||
|
||||
# Camera Streamer has no version Output yet
|
||||
get_ayucamstream_version() {
|
||||
local cur_ver avail_ver
|
||||
pushd "${BASE_CN_PATH}"/bin/rtsp-simple-server || exit 1
|
||||
avail_ver="$(cat version)"
|
||||
cur_ver="$("${PWD}"/rtsp-simple-server --version)"
|
||||
if [[ "${cur_ver}" == "${avail_ver}" ]]; then
|
||||
vc_log_msg "rtsp-simple-server is up to date. (${cur_ver})"
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
pushd "${BASE_CN_PATH}"/bin/camera-streamer &> /dev/null || exit 1
|
||||
avail_ver="($(git describe --tags --always))"
|
||||
cur_ver="$("${PWD}"/camera-streamer --version | tr -d " ")"
|
||||
if [ "${cur_ver}" == "${avail_ver}" ]; then
|
||||
vc_log_msg "camera-streamer is up to date. (${cur_ver})"
|
||||
fi
|
||||
if [ "${cur_ver}" != "${avail_ver}" ]; then
|
||||
vc_log_msg "rtsp-simple-server new version available: ${avail_ver} (${cur_ver})."
|
||||
vc_log_msg "camera-streamer new version available: ${avail_ver} (${cur_ver})."
|
||||
fi
|
||||
popd &> /dev/null || exit 1
|
||||
fi
|
||||
popd || exit 1
|
||||
}
|
||||
|
||||
function get_ffmpeg_version {
|
||||
get_ffmpeg_version() {
|
||||
local cur_ver avail_ver
|
||||
avail_ver="$(dpkg-query -W ffmpeg | awk -F':' '{print $2}')"
|
||||
cur_ver="$(ffmpeg -version | awk 'NR==1 {print $3}')"
|
||||
@ -68,7 +72,9 @@ function versioncontrol {
|
||||
function main {
|
||||
if [[ "${CROWSNEST_LOG_LEVEL}" != "quiet" ]]; then
|
||||
get_ustreamer_version
|
||||
get_rtsp_version
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
get_ayucamstream_version
|
||||
fi
|
||||
get_ffmpeg_version
|
||||
fi
|
||||
}
|
||||
|
@ -64,10 +64,12 @@ function crowsnest_watchdog {
|
||||
for i in $(get_conf_devices); do
|
||||
cc="$(crudini --get "${CROWSNEST_CFG}" "cam ${i}" "device" \
|
||||
| awk '{print $1}')"
|
||||
if [ "$(available "${cc}")" -ne 0 ] && [ "$(is_lost "${cc}")" -ne 0 ]; then
|
||||
if [[ ! "${cc}" =~ "/base/soc" ]] &&
|
||||
[[ "$(available "${cc}")" -ne 0 ]] && [[ "$(is_lost "${cc}")" -ne 0 ]]; then
|
||||
log_msg "WATCHDOG: Lost Device: '${cc}'"
|
||||
lost_dev "${cc}"
|
||||
elif [ "$(is_lost "${cc}")" -eq 0 ] && [ "$(available "${cc}")" -eq 0 ]; then
|
||||
elif [[ ! "${cc}" =~ "/base/soc" ]] &&
|
||||
[[ "$(is_lost "${cc}")" -eq 0 ]] && [[ "$(available "${cc}")" -eq 0 ]]; then
|
||||
log_msg "WATCHDOG: Device '${cc}' returned."
|
||||
returned_dev "${cc}"
|
||||
fi
|
||||
|
@ -1,12 +0,0 @@
|
||||
#### crowsnest - A webcam Service for multiple Cams and Stream Services.
|
||||
####
|
||||
#### Written by Stephan Wendel aka KwadFan <me@stephanwe.de>
|
||||
#### Copyright 2021
|
||||
#### https://github.com/mainsail-crew/crowsnest
|
||||
####
|
||||
#### This File is distributed under GPLv3
|
||||
####
|
||||
|
||||
# See https://github.com/pikvm/ustreamer#raspberry-pi-camera-example
|
||||
|
||||
options bcm2835-v4l2 max_video_width=2592 max_video_height=1944
|
@ -1,280 +0,0 @@
|
||||
###############################################
|
||||
# General parameters
|
||||
|
||||
# Sets the verbosity of the program; available values are "error", "warn", "info", "debug".
|
||||
logLevel: info
|
||||
# Destinations of log messages; available values are "stdout", "file" and "syslog".
|
||||
logDestinations: [stdout]
|
||||
# If "file" is in logDestinations, this is the file which will receive the logs.
|
||||
logFile: rtsp-simple-server.log
|
||||
|
||||
# Timeout of read operations.
|
||||
readTimeout: 10s
|
||||
# Timeout of write operations.
|
||||
writeTimeout: 10s
|
||||
# Number of read buffers.
|
||||
# A higher number allows a wider throughput, a lower number allows to save RAM.
|
||||
readBufferCount: 512
|
||||
|
||||
# HTTP URL to perform external authentication.
|
||||
# Every time a user wants to authenticate, the server calls this URL
|
||||
# with the POST method and a body containing:
|
||||
# {
|
||||
# "ip": "ip",
|
||||
# "user": "user",
|
||||
# "password": "password",
|
||||
# "path": "path",
|
||||
# "action": "read|publish"
|
||||
# "query": "url's raw query"
|
||||
# }
|
||||
# If the response code is 20x, authentication is accepted, otherwise
|
||||
# it is discarded.
|
||||
externalAuthenticationURL:
|
||||
|
||||
# Enable the HTTP API.
|
||||
api: no
|
||||
# Address of the API listener.
|
||||
apiAddress: 127.0.0.1:9997
|
||||
|
||||
# Enable Prometheus-compatible metrics.
|
||||
metrics: no
|
||||
# Address of the metrics listener.
|
||||
metricsAddress: 127.0.0.1:9998
|
||||
|
||||
# Enable pprof-compatible endpoint to monitor performances.
|
||||
pprof: no
|
||||
# Address of the pprof listener.
|
||||
pprofAddress: 127.0.0.1:9999
|
||||
|
||||
# Command to run when a client connects to the server.
|
||||
# This is terminated with SIGINT when a client disconnects from the server.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PORT: server port
|
||||
runOnConnect:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnConnectRestart: no
|
||||
|
||||
###############################################
|
||||
# RTSP parameters
|
||||
|
||||
# Disable support for the RTSP protocol.
|
||||
rtspDisable: no
|
||||
# List of enabled RTSP transport protocols.
|
||||
# UDP is the most performant, but doesn't work when there's a NAT/firewall between
|
||||
# server and clients, and doesn't support encryption.
|
||||
# UDP-multicast allows to save bandwidth when clients are all in the same LAN.
|
||||
# TCP is the most versatile, and does support encryption.
|
||||
# The handshake is always performed with TCP.
|
||||
protocols: [udp, multicast, tcp]
|
||||
# Encrypt handshake and TCP streams with TLS (RTSPS).
|
||||
# Available values are "no", "strict", "optional".
|
||||
encryption: "no"
|
||||
# Address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional".
|
||||
rtspAddress: :8554
|
||||
# Address of the TCP/TLS/RTSPS listener. This is needed only when encryption is "strict" or "optional".
|
||||
rtspsAddress: :8322
|
||||
# Address of the UDP/RTP listener. This is needed only when "udp" is in protocols.
|
||||
rtpAddress: :8000
|
||||
# Address of the UDP/RTCP listener. This is needed only when "udp" is in protocols.
|
||||
rtcpAddress: :8001
|
||||
# IP range of all UDP-multicast listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastIPRange: 224.1.0.0/16
|
||||
# Port of all UDP-multicast/RTP listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastRTPPort: 8002
|
||||
# Port of all UDP-multicast/RTCP listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastRTCPPort: 8003
|
||||
# Path to the server key. This is needed only when encryption is "strict" or "optional".
|
||||
# This can be generated with:
|
||||
# openssl genrsa -out server.key 2048
|
||||
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||
serverKey: server.key
|
||||
# Path to the server certificate. This is needed only when encryption is "strict" or "optional".
|
||||
serverCert: server.crt
|
||||
# Authentication methods.
|
||||
authMethods: [basic, digest]
|
||||
|
||||
###############################################
|
||||
# RTMP parameters
|
||||
|
||||
# Disable support for the RTMP protocol.
|
||||
rtmpDisable: no
|
||||
# Address of the RTMP listener.
|
||||
rtmpAddress: :1935
|
||||
|
||||
###############################################
|
||||
# HLS parameters
|
||||
|
||||
# Disable support for the HLS protocol.
|
||||
hlsDisable: no
|
||||
# Address of the HLS listener.
|
||||
hlsAddress: :8888
|
||||
# By default, HLS is generated only when requested by a user.
|
||||
# This option allows to generate it always, avoiding the delay between request and generation.
|
||||
hlsAlwaysRemux: no
|
||||
# Variant of the HLS protocol to use. Available options are:
|
||||
# * mpegts - uses MPEG-TS segments, for maximum compatibility.
|
||||
# * fmp4 - uses fragmented MP4 segments, more efficient.
|
||||
# * lowLatency - uses Low-Latency HLS.
|
||||
hlsVariant: mpegts
|
||||
# Number of HLS segments to keep on the server.
|
||||
# Segments allow to seek through the stream.
|
||||
# Their number doesn't influence latency.
|
||||
hlsSegmentCount: 7
|
||||
# Minimum duration of each segment.
|
||||
# A player usually puts 3 segments in a buffer before reproducing the stream.
|
||||
# The final segment duration is also influenced by the interval between IDR frames,
|
||||
# since the server changes the duration in order to include at least one IDR frame
|
||||
# in each segment.
|
||||
hlsSegmentDuration: 1s
|
||||
# Minimum duration of each part.
|
||||
# A player usually puts 3 parts in a buffer before reproducing the stream.
|
||||
# Parts are used in Low-Latency HLS in place of segments.
|
||||
# Part duration is influenced by the distance between video/audio samples
|
||||
# and is adjusted in order to produce segments with a similar duration.
|
||||
hlsPartDuration: 200ms
|
||||
# Maximum size of each segment.
|
||||
# This prevents RAM exhaustion.
|
||||
hlsSegmentMaxSize: 50M
|
||||
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
|
||||
# This allows to play the HLS stream from an external website.
|
||||
hlsAllowOrigin: "*"
|
||||
# Enable TLS/HTTPS on the HLS server.
|
||||
# This is required for Low-Latency HLS.
|
||||
hlsEncryption: no
|
||||
# Path to the server key. This is needed only when encryption is yes.
|
||||
# This can be generated with:
|
||||
# openssl genrsa -out server.key 2048
|
||||
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||
hlsServerKey: server.key
|
||||
# Path to the server certificate.
|
||||
hlsServerCert: server.crt
|
||||
|
||||
###############################################
|
||||
# Path parameters
|
||||
|
||||
# These settings are path-dependent, and the map key is the name of the path.
|
||||
# It's possible to use regular expressions by using a tilde as prefix.
|
||||
# For example, "~^(test1|test2)$" will match both "test1" and "test2".
|
||||
# For example, "~^prefix" will match all paths that start with "prefix".
|
||||
# The settings under the path "all" are applied to all paths that do not match
|
||||
# another entry.
|
||||
paths:
|
||||
all:
|
||||
# Source of the stream. This can be:
|
||||
# * publisher -> the stream is published by a RTSP or RTMP client
|
||||
# * rtsp://existing-url -> the stream is pulled from another RTSP server / camera
|
||||
# * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS
|
||||
# * rtmp://existing-url -> the stream is pulled from another RTMP server
|
||||
# * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server
|
||||
# * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server with HTTPS
|
||||
# * redirect -> the stream is provided by another path or server
|
||||
source: publisher
|
||||
|
||||
# If the source is an RTSP or RTSPS URL, this is the protocol that will be used to
|
||||
# pull the stream. available values are "automatic", "udp", "multicast", "tcp".
|
||||
sourceProtocol: automatic
|
||||
|
||||
# Tf the source is an RTSP or RTSPS URL, this allows to support sources that
|
||||
# don't provide server ports or use random server ports. This is a security issue
|
||||
# and must be used only when interacting with sources that require it.
|
||||
sourceAnyPortEnable: no
|
||||
|
||||
# If the source is a RTSPS or HTTPS URL, and the source certificate is self-signed
|
||||
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||
# validate it anyway. It can be obtained by running:
|
||||
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||
sourceFingerprint:
|
||||
|
||||
# If the source is an RTSP or RTMP URL, it will be pulled only when at least
|
||||
# one reader is connected, saving bandwidth.
|
||||
sourceOnDemand: no
|
||||
# If sourceOnDemand is "yes", readers will be put on hold until the source is
|
||||
# ready or until this amount of time has passed.
|
||||
sourceOnDemandStartTimeout: 10s
|
||||
# If sourceOnDemand is "yes", the source will be closed when there are no
|
||||
# readers connected and this amount of time has passed.
|
||||
sourceOnDemandCloseAfter: 10s
|
||||
|
||||
# If the source is "redirect", this is the RTSP URL which clients will be
|
||||
# redirected to.
|
||||
sourceRedirect:
|
||||
|
||||
# If the source is "publisher" and a client is publishing, do not allow another
|
||||
# client to disconnect the former and publish in its place.
|
||||
disablePublisherOverride: no
|
||||
|
||||
# If the source is "publisher" and no one is publishing, redirect readers to this
|
||||
# path. It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
|
||||
fallback:
|
||||
|
||||
# Username required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
publishUser:
|
||||
# Password required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
publishPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to publish.
|
||||
publishIPs: []
|
||||
|
||||
# Username required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
readUser:
|
||||
# password required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
readPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to read.
|
||||
readIPs: []
|
||||
|
||||
# Command to run when this path is initialized.
|
||||
# This can be used to publish a stream and keep it always opened.
|
||||
# This is terminated with SIGINT when the program closes.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnInit:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnInitRestart: no
|
||||
|
||||
# Command to run when this path is requested.
|
||||
# This can be used to publish a stream on demand.
|
||||
# This is terminated with SIGINT when the path is not requested anymore.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnDemand:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnDemandRestart: no
|
||||
# Readers will be put on hold until the runOnDemand command starts publishing
|
||||
# or until this amount of time has passed.
|
||||
runOnDemandStartTimeout: 10s
|
||||
# The command will be closed when there are no
|
||||
# readers connected and this amount of time has passed.
|
||||
runOnDemandCloseAfter: 10s
|
||||
|
||||
# Command to run when the stream is ready to be read, whether it is
|
||||
# published by a client or pulled from a server / camera.
|
||||
# This is terminated with SIGINT when the stream is not ready anymore.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnReady:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnReadyRestart: no
|
||||
|
||||
# Command to run when a clients starts reading.
|
||||
# This is terminated with SIGINT when a client stops reading.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnRead:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnReadRestart: no
|
@ -18,6 +18,9 @@
|
||||
#### Port 8083 equals /webcam4/?action=[stream/snapshot] #####
|
||||
#### #####
|
||||
#####################################################################
|
||||
#### RTSP Stream URL: ( if enabled and supported ) #####
|
||||
#### rtsp://<ip>:<rtsp_port>/stream.h264 #####
|
||||
#####################################################################
|
||||
|
||||
|
||||
[crowsnest]
|
||||
@ -26,8 +29,10 @@ log_level: verbose # Valid Options are quiet/verbose/debug
|
||||
delete_log: false # Deletes log on every restart, if set to true
|
||||
|
||||
[cam 1]
|
||||
mode: mjpg # mjpg/rtsp
|
||||
port: 8080 # Port
|
||||
mode: mjpg # mjpg/multi - Multi uses webrtc, mjpg and snapshots at the same time
|
||||
enable_rtsp: false # If multi is used, this enables also usage of an rtsp server
|
||||
rtsp_port: 8554 # Set different ports for each device!
|
||||
port: 8080 # HTTP/MJPG Stream/Snapshot Port
|
||||
device: /dev/video0 # See Log for available ...
|
||||
resolution: 640x480 # widthxheight format
|
||||
max_fps: 15 # If Hardware Supports this it will be forced, otherwise ignored/coerced.
|
||||
|
@ -24,6 +24,8 @@ CN_CONFIG_ENVPATH="${CN_CONFIG_ROOTPATH}/systemd"
|
||||
CN_MOONRAKER_CONFIG_PATH="${CN_CONFIG_CONFIGPATH}/moonraker.conf"
|
||||
CN_USTREAMER_REPO="https://github.com/pikvm/ustreamer.git"
|
||||
CN_USTREAMER_BRANCH="master"
|
||||
CN_CAMERA_STREAMER_REPO="https://github.com/ayufan-research/camera-streamer.git"
|
||||
CN_CAMERA_STREAMER_BRANCH="master"
|
||||
|
||||
### Messages
|
||||
header_msg() {
|
||||
@ -75,16 +77,6 @@ env_path_msg() {
|
||||
echo -e "\tDefault: \e[32m${CN_CONFIG_ENVPATH}\e[0m\n"
|
||||
}
|
||||
|
||||
apply_raspicamfix_msg() {
|
||||
header_msg
|
||||
echo -e "Should the Raspicam Fix be applied?\n"
|
||||
echo -e "\t\e[34mNOTE:\e[0m\n\tThis should be only applied if you are"
|
||||
echo -e "\tusing a Raspberry Pi! This will force Raspicams"
|
||||
echo -e "\tusing the device path '/dev/video0'\n"
|
||||
echo -e "Available are:\n Yes [y or Y]\n No [n or N]\n Hit ENTER for auto\n"
|
||||
echo -e "In 'auto' mode Installer try to detect device and applies fix if SBC is a Pi!\n"
|
||||
}
|
||||
|
||||
add_moonraker_entry_msg() {
|
||||
header_msg
|
||||
echo -e "Should the update_manager entry added to your moonraker.conf?\n"
|
||||
@ -152,6 +144,8 @@ create_config_header() {
|
||||
echo -e "BASE_USER=\"${CN_CONFIG_USER}\"";
|
||||
echo -e "CROWSNEST_USTREAMER_REPO_SHIP=\"${CN_USTREAMER_REPO}\"";
|
||||
echo -e "CROWSNEST_USTREAMER_REPO_BRANCH=\"${CN_USTREAMER_BRANCH}\""
|
||||
echo -e "CROWSNEST_CAMERA_STREAMER_REPO_SHIP=\"${CN_CAMERA_STREAMER_REPO}\"";
|
||||
echo -e "CROWSNEST_CAMERA_STREAMER_REPO_BRANCH=\"${CN_CAMERA_STREAMER_BRANCH}\""
|
||||
} >> "${CN_CONFIG_CONFIGFILE}"
|
||||
}
|
||||
|
||||
@ -204,29 +198,6 @@ specify_env_path() {
|
||||
fi
|
||||
}
|
||||
|
||||
apply_raspicamfix() {
|
||||
local reply
|
||||
apply_raspicamfix_msg
|
||||
read -erp "Enable Raspicamfix?: " reply
|
||||
if [[ -z "${reply}" ]]; then
|
||||
echo -e "CROWSNEST_RASPICAMFIX=\"auto\"" >> \
|
||||
"${CN_CONFIG_CONFIGFILE}"
|
||||
return 0
|
||||
fi
|
||||
while true; do
|
||||
case "${reply}" in
|
||||
[yY]*)
|
||||
echo -e "CROWSNEST_RASPICAMFIX=\"1\"" >> "${CN_CONFIG_CONFIGFILE}"
|
||||
break
|
||||
;;
|
||||
[nN]*)
|
||||
echo -e "CROWSNEST_RASPICAMFIX=\"0\"" >> "${CN_CONFIG_CONFIGFILE}"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
add_moonraker_entry() {
|
||||
local reply
|
||||
add_moonraker_entry_msg
|
||||
@ -265,11 +236,9 @@ main() {
|
||||
specify_log_path
|
||||
# Step 6: Specify env path.
|
||||
specify_env_path
|
||||
# Step 7: Raspicam fix
|
||||
apply_raspicamfix
|
||||
# Step 8: Moonraker entry
|
||||
# Step 7: Moonraker entry
|
||||
add_moonraker_entry
|
||||
# Step 9: Display finished message
|
||||
# Step 8: Display finished message
|
||||
goodbye_msg
|
||||
}
|
||||
|
||||
|
100
tools/install.sh
100
tools/install.sh
@ -22,7 +22,7 @@ set -Ee
|
||||
|
||||
# Global Vars
|
||||
TITLE="\e[31mcrowsnest\e[0m - A webcam daemon for multiple Cams and stream services."
|
||||
[[ -n "${BASE_USER}" ]] || BASE_USER="$(logname 2> /dev/null || echo "${PWD}" | cut -d'/' -f3)"
|
||||
[[ -n "${BASE_USER}" ]] || BASE_USER=""
|
||||
[[ -n "${CROWSNEST_UNATTENDED}" ]] || CROWSNEST_UNATTENDED="0"
|
||||
[[ -n "${CROWSNEST_DEFAULT_CONF}" ]] || CROWSNEST_DEFAULT_CONF="resources/crowsnest.conf"
|
||||
|
||||
@ -37,11 +37,19 @@ if [[ "${DEBIAN_FRONTEND}" != "noninteractive" ]]; then
|
||||
fi
|
||||
|
||||
### Check non-root
|
||||
if [[ ${UID} != '0' ]]; then
|
||||
echo -e "\n\tYOU NEED TO RUN INSTALLER AS ROOT!"
|
||||
if [[ -z "${BASE_USER}" ]] && [[ -z "${SUDO_USER}" ]]; then
|
||||
echo -e "\n\tYou need to run this script with sudo priviledges!"
|
||||
echo -e "\tPlease try '\e[32msudo make install\e[0m'\n\nExiting..."
|
||||
exit 1
|
||||
fi
|
||||
## Determine BASE_USER
|
||||
if [[ -z "${BASE_USER}" ]] && [[ "${SUDO_USER}" == "root" ]]; then
|
||||
printf "\n\tPlease do NOT run this script as root!\n"
|
||||
printf "\tLogin in as regular user and run with '\e[32msudo make install\e[0m'\n\n"
|
||||
exit 1
|
||||
else
|
||||
BASE_USER="${SUDO_USER}"
|
||||
fi
|
||||
|
||||
### Global functions
|
||||
|
||||
@ -123,7 +131,8 @@ import_config() {
|
||||
CROWSNEST_ENV_PATH="/home/${BASE_USER}/printer_data/systemd"
|
||||
CROWSNEST_USTREAMER_REPO_SHIP="https://github.com/pikvm/ustreamer.git"
|
||||
CROWSNEST_USTREAMER_REPO_BRANCH="master"
|
||||
|
||||
CROWSNEST_CAMERA_STREAMER_REPO_SHIP="https://github.com/ayufan/camera-streamer.git"
|
||||
CROWSNEST_CAMERA_STREAMER_REPO_BRANCH="master"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -138,7 +147,7 @@ create_filestructure() {
|
||||
### Detect legacy webcamd.
|
||||
detect_existing_webcamd() {
|
||||
local remove
|
||||
if [ -x "/usr/local/bin/webcamd" ] && [ -d "${HOME}/mjpg-streamer" ]; then
|
||||
if [ -x "/usr/local/bin/webcamd" ] && [ -d "/home/${BASE_USER}/mjpg-streamer" ]; then
|
||||
detect_msg
|
||||
read -erp "Do you want to remove existing 'webcamd'? (y/N) " -i "N" remove
|
||||
case "${remove}" in
|
||||
@ -207,8 +216,9 @@ install_packages() {
|
||||
PKGLIST="git crudini bsdutils findutils v4l-utils curl"
|
||||
### Ustreamer Dependencies
|
||||
PKGLIST="${PKGLIST} build-essential libevent-dev libjpeg-dev libbsd-dev"
|
||||
### simple-rtsp-server Dependencies
|
||||
PKGLIST="${PKGLIST} libxcomposite1 libxtst6 ffmpeg"
|
||||
### Camera-Streamer Dependencies
|
||||
PKGLIST="${PKGLIST} cmake libavformat-dev libavutil-dev libavcodec-dev libcamera-dev"
|
||||
PKGLIST="${PKGLIST} liblivemedia-dev pkg-config xxd build-essential cmake libssl-dev"
|
||||
|
||||
echo -e "Running apt update first ..."
|
||||
### Run apt update
|
||||
@ -341,24 +351,38 @@ clone_ustreamer() {
|
||||
fi
|
||||
sudo -u "${BASE_USER}" \
|
||||
git clone "${CROWSNEST_USTREAMER_REPO_SHIP}" \
|
||||
-b "${CROWSNEST_USTREAMER_REPO_BRANCH}" bin/ustreamer
|
||||
## Buster workaround
|
||||
## ustreamer support omx only till version 4.13
|
||||
## so stick to that version
|
||||
-b "${CROWSNEST_USTREAMER_REPO_BRANCH}" \
|
||||
--depth=1 --single-branch bin/ustreamer
|
||||
if [[ "$(get_os_version buster)" != "0" ]]; then
|
||||
pushd bin/ustreamer &> /dev/null || exit 1
|
||||
git reset --hard 61ab2a8
|
||||
popd &> /dev/null || exit 1
|
||||
printf "NOTE: Crowsnest has dropped support for OMX in ustreamer ... \n"
|
||||
fi
|
||||
}
|
||||
|
||||
clone_cstreamer() {
|
||||
## remove bin/ustreamer if exist
|
||||
if [[ -d bin/camera-streamer ]]; then
|
||||
rm -rf bin/camera-streamer
|
||||
fi
|
||||
sudo -u "${BASE_USER}" \
|
||||
git clone "${CROWSNEST_CAMERA_STREAMER_REPO_SHIP}" --recursive \
|
||||
-b "${CROWSNEST_CAMERA_STREAMER_REPO_BRANCH}" \
|
||||
--depth=1 --single-branch bin/camera-streamer
|
||||
}
|
||||
|
||||
build_apps() {
|
||||
echo -e "Build dependend Stream Apps ..."
|
||||
echo -e "Cloning ustreamer repository ..."
|
||||
clone_ustreamer
|
||||
pushd bin > /dev/null
|
||||
sudo -u "${BASE_USER}" make all
|
||||
popd > /dev/null
|
||||
## Detect Image build for Raspberrys
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]] ||
|
||||
[[ -f "/etc/rpi-issue" ]]; then
|
||||
echo -e "Cloning camera-streamer repository ..."
|
||||
clone_cstreamer
|
||||
fi
|
||||
if [[ "$(is_raspberry_pi)" = "0" ]]; then
|
||||
echo -e "Install of camera-streamer skipped, only supported on Raspberry Pi's! ... "
|
||||
fi
|
||||
sudo -u "${BASE_USER}" bin/build.sh --build
|
||||
}
|
||||
|
||||
is_raspberry_pi() {
|
||||
@ -370,33 +394,11 @@ is_raspberry_pi() {
|
||||
fi
|
||||
}
|
||||
|
||||
install_raspicam_fix() {
|
||||
if [[ "${CROWSNEST_RASPICAMFIX}" == "auto" ]]; then
|
||||
if [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
echo -e "Device is a Raspberry Pi"
|
||||
CROWSNEST_RASPICAMFIX="1"
|
||||
fi
|
||||
if [[ "$(is_raspberry_pi)" = "0" ]]; then
|
||||
echo -e "Device is \e[31mNOT\e[0m a Raspberry Pi ... [${CN_SK}]"
|
||||
CROWSNEST_RASPICAMFIX="0"
|
||||
fi
|
||||
fi
|
||||
# This is also used for unattended Install
|
||||
# Needs special handling if targeted Image is not for Raspberry Pi's!
|
||||
if [[ "${CROWSNEST_RASPICAMFIX}" == "1" ]] &&
|
||||
[[ -f /boot/config.txt ]]; then
|
||||
echo -en "Applying Raspicam Fix ... \r"
|
||||
bash -c 'echo "bcm2835-v4l2" >> /etc/modules'
|
||||
cp resources/bcm2835-v4l2.conf /etc/modprobe.d/
|
||||
echo -e "Applying Raspicam Fix ... [${CN_OK}]"
|
||||
fi
|
||||
}
|
||||
|
||||
enable_legacy_cam() {
|
||||
set_gpu_mem() {
|
||||
local cfg
|
||||
local -a model
|
||||
cfg="/boot/config.txt"
|
||||
model=(pi3 pi4)
|
||||
model=(pi3 pi4 cm4)
|
||||
if [[ -f "${cfg}" ]] && [[ "$(is_raspberry_pi)" = "1" ]]; then
|
||||
|
||||
# Helper func
|
||||
@ -404,10 +406,11 @@ enable_legacy_cam() {
|
||||
crudini --get "${cfg}" "${1}" gpu_mem 2> /dev/null
|
||||
}
|
||||
|
||||
echo -en "Enable legacy camera stack ... \r"
|
||||
sed -i "s/camera_auto_detect=1/#camera_auto_detect=1/" "${cfg}"
|
||||
echo -en "Set gpu_mem ... \r"
|
||||
if [[ "$(grep -c "start_x" "${cfg}")" = "0" ]]; then
|
||||
crudini --set --inplace "${cfg}" all start_x 1 &> /dev/null
|
||||
sed -i 's/^#start_x/start_x/g' "${cfg}"
|
||||
sed -i 's/^start_x=0/start_x=1/g' "${cfg}"
|
||||
fi
|
||||
|
||||
for d in "${model[@]}"; do
|
||||
@ -418,7 +421,7 @@ enable_legacy_cam() {
|
||||
if [[ "$(get_val pi0)" -lt "129" ]]; then
|
||||
sudo crudini --set --inplace "${cfg}" pi0 gpu_mem 160 &> /dev/null
|
||||
fi
|
||||
echo -e "Enable legacy camera stack ... [${CN_OK}]"
|
||||
echo -e "Set gpu_mem ... [${CN_OK}]"
|
||||
fi
|
||||
## crudini workaround
|
||||
## used version of crudini puts spaces between values and parameters
|
||||
@ -536,7 +539,7 @@ main() {
|
||||
## Step 7: Enable Legacy Camera Stack
|
||||
if [[ "$(get_os_version bullseye)" != "0" ]] &&
|
||||
[[ -f "/boot/config.txt" ]]; then
|
||||
enable_legacy_cam
|
||||
set_gpu_mem
|
||||
fi
|
||||
|
||||
### buntu workaround
|
||||
@ -562,15 +565,12 @@ main() {
|
||||
## Step 10: Install logrotate file
|
||||
install_logrotate
|
||||
|
||||
## Step 11: Install raspicamfix
|
||||
install_raspicam_fix
|
||||
|
||||
## Step 12: Add moonraker update_manager entry
|
||||
## Step 11: Add moonraker update_manager entry
|
||||
if [[ "${CROWSNEST_ADD_CROWSNEST_MOONRAKER}" = "1" ]]; then
|
||||
add_update_entry
|
||||
fi
|
||||
|
||||
## Step 13: Ask for reboot
|
||||
## Step 12: Ask for reboot
|
||||
## Skip if UNATTENDED
|
||||
goodbye_msg
|
||||
if [[ "${CROWSNEST_UNATTENDED}" = "0" ]]; then
|
||||
|
@ -43,7 +43,7 @@ welcome_msg() {
|
||||
echo -e "\t\e[34mAhoi!\e[0m"
|
||||
echo -e "\tTo sad that you want to uninstall crowsnest :("
|
||||
echo -e "\tThis will take a while ... "
|
||||
echo -e "\tPlease reboot after installation has finished.\n"
|
||||
echo -e "\tPlease reboot after uninstallation has finished.\n"
|
||||
sleep 1
|
||||
}
|
||||
|
||||
@ -96,7 +96,6 @@ ask_uninstall() {
|
||||
y|Y|yes|Yes|YES)
|
||||
source_env_file
|
||||
uninstall_crowsnest
|
||||
remove_raspicam_fix
|
||||
remove_logrotate
|
||||
ask_remove_config
|
||||
goodbye_msg
|
||||
@ -174,21 +173,6 @@ ask_remove_config() {
|
||||
return 0
|
||||
}
|
||||
|
||||
remove_raspicam_fix() {
|
||||
if [[ -f /etc/modprobe.d/bcm2835-v4l2.conf ]] &&
|
||||
[[ -f /proc/device-tree/model ]] &&
|
||||
grep -q "Raspberry" /proc/device-tree/model ; then
|
||||
echo -en "Removing Raspicam Fix ...\r"
|
||||
sudo sed -i '/bcm2835/d' /etc/modules
|
||||
sudo rm -f /etc/modprobe.d/bcm2835-v4l2.conf
|
||||
echo -e "Removing Raspicam Fix ... [${CN_OK}]"
|
||||
else
|
||||
echo -e "Removing Raspicam Fix ... [${CN_SK}]"
|
||||
echo -e "\tThis is not a Raspberry Pi"
|
||||
echo -e "\tor Raspicamfix not installed ... \n"
|
||||
fi
|
||||
}
|
||||
|
||||
function remove_logrotate {
|
||||
echo -en "Removing Logrotate Rule ...\r"
|
||||
sudo rm -f /etc/logrotate.d/crowsnest
|
||||
|
Loading…
x
Reference in New Issue
Block a user