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:
Stephan Wendel 2023-03-11 18:57:19 +01:00
parent 20ed6a8b58
commit 95c1dca13f
No known key found for this signature in database
GPG Key ID: F465B83ACBA45639
25 changed files with 578 additions and 764 deletions

4
.gitignore vendored
View File

@ -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

View File

@ -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'

View File

@ -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
View 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

View File

@ -1 +0,0 @@
v0.20.2

View File

@ -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"

View File

@ -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
View 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!"
}

View File

@ -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
}

View File

@ -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: '${ustreamer##*/}' not found. Exiting!"
exit 1
fi
## 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
log_msg "Dependency: '$(echo "${chk}" | cut -d '/' -f3)' not found. Exiting!"
log_msg "Dependency: '${cstreamer##*/}' 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"
else
echo "0"
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
}

View File

@ -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
}

View File

@ -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!"
}

View File

@ -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}"
}

View File

@ -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."

View File

@ -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]"
}

View File

@ -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
# Use MJPEG Hardware encoder if possible
if [ "$(detect_mjpeg "${cam_sec}")" = "1" ]; then
start_param+=( -m MJPEG --encoder=HW )
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)

View File

@ -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
}

View File

@ -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})"
fi
if [ "${cur_ver}" != "${avail_ver}" ]; then
vc_log_msg "rtsp-simple-server new version available: ${avail_ver} (${cur_ver})."
fi
popd || exit 1
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 "camera-streamer new version available: ${avail_ver} (${cur_ver})."
fi
popd &> /dev/null || exit 1
fi
}
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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
}

View File

@ -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

View File

@ -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