Skip to content

Instantly share code, notes, and snippets.

@paius1
Last active October 4, 2022 18:05
Show Gist options
  • Save paius1/f758e33183616c03d6bd66b4b89ae2a1 to your computer and use it in GitHub Desktop.
Save paius1/f758e33183616c03d6bd66b4b89ae2a1 to your computer and use it in GitHub Desktop.
Script to view and Monitor rtsp:// stream from a security camera
#!/usr/bin/env bash
#
# Simple script to monitor a ipcam stream running on mpv
# using the ipc-server
# requires mpv, socat, and jq written for bash 4
# xwinwrap to run on the Desktop
# by gmail plgroves 2019
#
# script creates a kill script in same path as this script
# named This_script-kill
# if path is not writable it is in $HOME
#
# it also creates a .desktop file so it shows up in the
# menu under the Video/Multimedia Category
#
userName="username"
passWord="password" # We shouldn't put plaintext passwords in files
HOST="127.0.0.1"
PORT="554"
pATH="/path/to/stream"
# Run as desktop background
deskTop=false
# You may have to play with SLEEP stay in the middle loop
SLEEP=2
# audioDevice=(--no-audio)
# or mpv --audio-device=help | grep pulse or alsa
# audioDevice=(--audio-device=pulse/alsa_output.1.analog-stereo)
# Here you can add your own arguments to mpv
# addArguments+=()
addArguments+=(--window-scale=.5)
[[ $deskTop == "true" ]] && { addArguments+=(--no-osc); }
#-----------------------------------------------------
me=$0 && fileName=$(basename ${me})
hostName=$(nmap -sL $HOST | grep report | cut -d' ' -f5)
socket="/tmp/mpvsocket-${fileName} " # yes there is a space here
logFile=/tmp/mpvlog.file
# ----------------------------------------------------
# exit from an infinite loop cleanly
trap ctrl_c INT
echo Double Ctrl-C to exit
function ctrl_c {
echo -en "\n## Caught SIGINT; Clean up and Exit \n"
$killScript
exit $?
}
# Check for required programs
# mpv
[[ $(command -v mpv) ]] || { echo "\n mpv not found\n Can't continue\n"; ctrl_c; }
# socat
[[ $(command -v socat) ]] || { echo "\n socat not found\n Can't communicate with mpv\n"; ctrl_c; }
# jq
[[ $(command -v jq) ]] || { echo -e "\n jq not found\n can't keep track of stream position \n"; ctrl_c; }
# xwinwrap to run mpv on the Desktop
[[ ${deskTop} == "true" && $(command -v xwinwrap) ]] || { deskTop=false; echo -e "\n\tWon't run on the Desktop\n"; }
# Changes to $command to run as the Desktop Background
[[ ${deskTop} == "true" ]] && { winDesktop=( -wid WID --window-scale=1); runDesktop=(xwinwrap -g $(xdpyinfo | grep 'dimensions:'|awk '{print $2}') -ov --); addArguments=($(echo ${addArguments[@]//--window-scale*/})); }
# Create .desktop file for this script - you can edit for future use
scriptDesktop=$HOME/.local/share/applications/${hostName}.$(echo $HOST | cut -d'.' -f4).desktop
if [ ! -f "${scriptDesktop}" ]; then
# Create entry in the Application/Video Menu
cat <<-EOF >$scriptDesktop
[Desktop Entry]
Type=Application
StartupNotify=true
Name=IP Camera @ $hostName
Icon=mpv
Exec=$me
Categories=GTK;GNOME;Video;AudioVideo
EOF
fi
# Create kill script for infinite 'while' loop
# and add to Application/Video Menu
killScript=$me-kill
scriptDesktop=$HOME/.local/share/applications/${fileName}-kill.desktop
touch $killScript &>> /dev/null || killScript=$HOME/$fileName-kill
echo -e "Kill with\n $killScript"
cat <<-EOF > $killScript
#!/bin/sh
echo '{ "command": ["quit"] }' | socat - $socket > /dev/null 2>&1
rm -f $socket $scriptDesktop $killScript
echo -e "\n`date "+%F %l:%M:%S %p"` Stopped server for $hostName cleanly" | tee -a $logFile
ps -ef | egrep -wi --color '${socket}|${fileName}' | grep -v grep | awk '{print \$2}' | xargs kill -9 > /dev/null 2>&1
EOF
chmod u+x $killScript
# Create .desktop
cat <<-EOF >$scriptDesktop
[Desktop Entry]
Type=Application
Name=IP Camera @ $hostName STOP
Icon=gtk-delete
Exec=$killScript
Categories=GTK;GNOME;Video;AudioVideo;
EOF
# Functions
# create stream url
function streamUrl () {
stream="rtsp://$userName:$passWord@$HOST:$PORT$pATH"
}
# start server
function start_Server () {
command="${runDesktop[*]} mpv ${winDesktop[*]} --input-ipc-server=${socket} --idle --keep-open=always ${audioDevice[*]} ${addArguments[*]} --title $hostName --x11-name $hostName --no-cache --untimed --no-demuxer-thread --video-sync=audio --vd-lavc-threads=1 --log-file=/tmp/camera.log > /tmp/cameraOutput.log"
$command &>> /dev/null 2>&1 &
}
# send video file to mpv
function open_Stream () {
stream=$1
#sleep $SLEEP
response=$(echo "{ \"command\": [\"loadfile\", \"${stream}\"] }" | socat - $socket) || return 255
return 0
}
# Monitor stream position
function streamPosition () {
POSITION=$(echo '{ "command": ["get_property_string", "time-pos"] }' | socat - $socket | jq .data | tr '"' ' ' | cut -d'.' -f 1 | xargs -i echo "({}/1)" | bc )
}
# End Functions
# Starting Server
echo -e "\n`date "+%F %l:%M:%S %p"` Starting Server for $hostName" | tee -a $logFile
start_Server
# Wait for camera to become reachable
while true; do printf '%s\r' "`date "+%F %l:%M:%S %p"` ping $hostName" | tee -a $logFile; ping -c 1 $HOST > /dev/null && break; sleep 2; done
sleep $SLEEP
streamUrl
open_Stream $stream
echo -e "`date "+%F %l:%M:%S %p"` Opened $stream" | tee -a $logFile
# Waiting for stream to stream.
while [[ $POSITION -lt 1 ]]; do
streamPosition
printf '%s\r' "`date "+%F %l:%M:%S %p"` waiting for $hostName $stream" | tee -a $logFile
done;
sleep $SLEEP
echo | tee -a $logFile
# Starting endless Loop
while true; do
positionCheck=0
# This Loop monitors "the postion in the stream"
while true; do
streamPosition
printf '%s %s\r' "previous $positionCheck" "current $POSITION $hostName" | tee -a /tmp/debug.log
if [ -z $POSITION ]; then break; fi
# Not moving forward then let's break and restart
[[ "$positionCheck" -ge "$POSITION" ]] && { echo -e "\n${hostName} ${stream} not moving forward \nprevious position $positionCheck current $POSITION" | tee -a $logfile; break; }
# print "the postion in the stream"
printf ' %d:%02d:%02d %s \r' $(($POSITION/3600)) $(($POSITION%3600/60)) $(($POSITION%60)) $stream | tee -a $logFile
positionCheck=$POSITION
sleep $SLEEP
done
# if we break out of the previous loop something is amiss
echo -e "\n`date "+%F %l:%M:%S %p"` $hostName $stream stalled" | tee -a $logFile
echo '{ "command": ["get_property", "mpv-version"] }' | socat - /tmp/mpvsocket-ipcam1-mpv.sh &> /dev/null || { echo -e "`date "+%F %l:%M:%S %p"` Restarting Server for $hostName" | tee -a $logFile; rm $socket; start_Server; sleep $SLEEP; }
# Server started/still running
echo -e "`date "+%F %l:%M:%S %p"` Trying to restart $hostName $stream" | tee -a $logFile
# Wait for camera to become reachable
while true; do printf '%s\r' "`date "+%F %l:%M:%S %p"` ping $hostName" | tee -a $logFile; ping -c 1 $HOST > /dev/null && break; sleep 2; done
echo -e "\n$nullStream" '=>' "$socket" #debug
open_Stream $nullStream
echo -e "response-NULL $response return-NULL $retUrn" # debug
for n in {1..5}; do sleep 1; done
streamUrl
echo -e "\n$stream" '=>' "$socket" # debug
open_Stream $stream
echo -e "respone-STREAM $response return-STREAM $retUrn" # debug
response=$(echo $response | cut -d '"' -f6)
[[ "$response" = "success" ]] && { while [[ $POSITION -lt 1 ]]; do streamPosition; done; } || ps -ef | egrep -wi --color ${socket} | grep -v grep | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1
sleep $SLEEP
echo -e "\n`date "+%F %l:%M:%S %p"` Retry complete on $hostName" | tee -a $logFile
done
# End of 'infinite' loop
echo "`date "+%F %l:%M:%S %p"` Something went Horribly wrong on $hostName" | tee -a $logFile
ctrl_c
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment