Wait command not acting as expected in Bash Script - bash

I am making a script to play 4 different video feeds from 4 different camera DVRs via RTSP, and need them to be pretty much hands-free i.e. a dedicated mini pc running Debian that will, network connection permitting, always have the feeds playing without users needing to restart anything. My script below works as expected at first, but if I close one of the feeds or disconnect then reconnect the network connection, the next (and all the following) iterations of the while loop this happens:
feeds 1 through 4 connect correctly
wmctrl places windows correctly
All windows immediately close, and the loop continues as if the connection was lost.
I've tried removing the process numbers from the wait command as well as moving the function declaration outside of the loop (not sure what help that would do but tried nonetheless) but that changed nothing.
#!/bin/bash
case $1 in
-d) debug=1 ;;
*) ;;
esac
close_all() {
kill $p1 $p2 $p3 $p4
return
}
source1="rtsp://user:password#10.0.20.30:554/streaming/channels/001"
source2="rtsp://user:password#10.0.20.33:1026/streaming/channels/001"
source3="rtsp://user:password#10.0.20.31:1024/streaming/channels/001"
source4="rtsp://user:password#10.0.20.34:1027/streaming/channels/001"
while true; do
ffplay -rtsp_transport tcp -infbuf -stimeout 100 -autoexit -i $source1 -vf "scale=w=960:h=540" > /dev/null 2>&1 & p1=$!
ffplay -rtsp_transport tcp -infbuf -stimeout 100 -autoexit -i $source2 -vf "scale=w=960:h=540" > /dev/null 2>&1 & p2=$!
ffplay -rtsp_transport tcp -infbuf -stimeout 100 -autoexit -i $source3 -vf "scale=w=960:h=540" > /dev/null 2>&1 & p3=$!
ffplay -rtsp_transport tcp -infbuf -stimeout 100 -autoexit -i $source4 -vf "scale=w=960:h=540" > /dev/null 2>&1 & p4=$!
sleep 5
window1=$(wmctrl -l | grep "$source1" | cut -d' ' -f 1 )
window2=$(wmctrl -l | grep "$source2" | cut -d' ' -f 1 )
window3=$(wmctrl -l | grep "$source3" | cut -d' ' -f 1 )
window4=$(wmctrl -l | grep "$source4" | cut -d' ' -f 1 )
wmctrl -i -r $window1 -e 0,0,0,960,540
wmctrl -i -r $window2 -e 0,0,540,960,540
wmctrl -i -r $window3 -e 0,960,0,960,540
wmctrl -i -r $window4 -e 0,960,540,960,540
[ "$debug" = 1 ] && echo " $p1 $p2 $p3 $p4"
# Originally had wait -n $p1 $p2 $p3 $p4 here
wait -n
#One of the ffplay commands exited so something must be wrong,
#close all and retry
close_all
echo "Connection interrupted, retrying..."
echo "Or press q to exit..."
read -t 3 -N 1 input
case $input in
q|Q) exit ;;
*) ;;
esac
# Picking an arbitrary DVR to test for now.
until ping -q -c 1 10.0.20.30 > /dev/null 2>&1; do
echo "Connection failed, will keep retrying in 3 seconds..."
echo "Press q to abort."
read -t 3 -N 1 input
case $input in
q|Q) exit ;;
*) ;;
esac
done
done
I need it to behave like it does the first iteration and wait until something actually goes wrong. I assume it has something to do with the wait command and processes or job control or something but google searching has led me nowhere. Can someone help me please?

Related

Tiny BASH MPV Cut & Join Script - Bash does not read line correctly in my script

I have created a script that takes the filename of screenshots from MPV player and grabs the time codes and cuts the video.
I like MPV because it is very fast on big movie files and hitting s (screenshot) for every in and out cut is very easy. I have not found any bash script (I can only do bash or are learning bash) that can do this, only lua and java scripts.
The bash script:
#!/bin/bash
clear
DATE=$(date +"%Y%m%d_%H%M%S")
x-terminal-emulator -geometry 50x20+3100+0 -e "bash -c 'while true; do clear;ls *.jpg;sleep 1;done' &"
rm *.jpg CUT*.mp4 cutLines.* cutMerge.*
mpv --screenshot-template="~/%F-(%P)-%03n" "$1"
echo
read -p "--- Hit ENTER to CUT ---"
echo
ls *.jpg | cut -c 24-35 > cutLines.txt
IFS=$'\n'
while IFS= read -r ONE; do read -r TWO
echo " Making cut for duration: $ONE - $TWO stored as: CUT_${ONE}.mp4"
ffmpeg -nostdin -loglevel quiet -ss "${ONE}" -to "${TWO}" -i "${1}" -c copy CUT_"${ONE}".mp4
echo CUT_"${ONE}".mp4 >> cutMerge.tmp
done < cutLines.txt
cat cutMerge.tmp | sed "s/^/file '/" |sed "s/$/'/" > cutMerge.txt
ffmpeg -f concat -safe 0 -i cutMerge.txt -c copy CUTmerge_"$DATE".mp4
The script works for the clips.
Here is the link where you see what I struggle with.
It looks like read line does not read all the data or something?
Video showing what the problem is
Thanks to Ed Morton's TIPS the script is now working!
It looks like the problem was missing double quotes and the ffmpeg option -nostdin that was the main problem for this script.
#!/bin/bash
clear
rm *.jpg cutLines.txt cutMerge.txt cutMerge.tmp
DATE=$(date +"%Y%m%d_%H%M%S")
x-terminal-emulator -geometry 50x20+3100+0 -e "bash -c 'while true; do clear;ls *.jpg;sleep 1;done' &"
mpv --screenshot-template="~/%F-(%P)-%03n" "$1"
echo
read -p "--- Hit ENTER to CUT ---"
echo
ls *.jpg | cut -c 24-35 > cutLines.txt
IFS=$'\n'
while IFS= read -r ONE; do read -r TWO
echo " Making cut for duration: $ONE - $TWO stored as: CUT_${ONE}.mp4"
ffmpeg -nostdin -loglevel quiet -ss "${ONE}" -to "${TWO}" -i "${1}" -c copy CUT_"${ONE}".mp4
echo CUT_"${ONE}".mp4 >> cutMerge.tmp
done < cutLines.txt
cat cutMerge.tmp | sed "s/^/file '/" |sed "s/$/'/" > cutMerge.txt
ffmpeg -f concat -safe 0 -i cutMerge.txt -c copy VideoMerged_"$DATE".mp4
echo -e "\n--- cutLines"
cat cutLines.txt
echo -e "\n--- cutMerge\n"
cat cutMerge.txt
rm *.jpg cutLines.txt cutMerge.txt cutMerge.tmp
mpv VideoMerged_"$DATE".mp4

"If" "then" "else" and "return" command syntax

I am a pipefitter working in a weld shop and need music as commercial radio is very repetitive. I went and bought a RPI3 and put Raspian Stretch Lite on it with the fm_transmitter program. I got the shell script far along enough that the radio works fine but sometimes I'll get a song to play again within a short time. This is just by chance as the RANDOM_FILE variable chooses from an entire directory. I have also gotten the script to log each song that is played in a text file. I would like to use tail and grep to search the text file for the current selected RANDOM_FILE and check if it is present within the last X number of songs in the log. I want the script to restart back at the top defining a new RANDOM_FILE if the RANDOM_FILE is present in the grep command. If it isn't present, I want the script to continue, allowing the RANDOM_FILE to play through the fm_transmitter program.
I would also like for someone to check that the string defining RANDOM_FILE is in fact, truly random and correct.
Below is the script in it's current form. I believe I am close, but am going wrong somewhere as I still am getting repeats. I need help with the "if" "then" "else" and "return" commands. I am hoping this is a quick and easy solution.
Thank you in advance to anyone that takes the time to help me out.
#!/bin/bash
while :
do
files=(/home/pi/music/*.wav)
RANDOM_FILE="${files[RANDOM % ${#files[#]}]}"
if tail -n 25 /home/pi/transmit_log.txt | grep "$RANDOM_FILE" = true ; then
echo -e "---SONG_SKIPPED---" >> /home/pi/transmit_log.txt ; return 7
else
# [ tail -n 25 /home/pi/transmit_log.txt | grep "$RANDOM_FILE" = false ] ; then
return 22
fi
echo -e "$RANDOM_FILE" >> /home/pi/transmit_log.txt
sox -v 3 "$RANDOM_FILE" -r 44100 -c 1 -b 16 -t wav - | sudo ./fm_transmitter -f 91.7 -
sleep .5
done
I don't believe you can return in the if/else statement like so. Return is used in a function to return a value.
Instead, you can use continue, that will skip until the next iteration of the loop (and fix the test like suggested by codeforester):
#!/bin/bash
while :
do
files=(/home/pi/music/*.wav)
RANDOM_FILE="${files[RANDOM % ${#files[#]}]}"
if tail -n 25 /home/pi/transmit_log.txt | grep -q "$RANDOM_FILE" ; then
echo "---SONG_SKIPPED---" >> /home/pi/transmit_log.txt
continue
fi
echo "$RANDOM_FILE" >> /home/pi/transmit_log.txt
sox -v 3 "$RANDOM_FILE" -r 44100 -c 1 -b 16 -t wav - | sudo ./fm_transmitter -f 91.7 -
sleep .5
done
Alternatively, it can be better to split your tests like shown below to make the if condition smaller and more readable:
already_played=$( tail -n 25 /home/pi/transmit_log.txt | grep "$RANDOM_FILE" )
if [ ! -z "$aldeady_played" ]; then
...
fi
The issue is in this statement:
if tail -n 25 /home/pi/transmit_log.txt | grep "$RANDOM_FILE" = true; then ...
The right way to it is:
if tail -n 25 /home/pi/transmit_log.txt | grep -q "$RANDOM_FILE"; then ...
In your construct, = true would be passed as arguments to grep. That's not what you want, right? grep -q will return 0 (or true) if the pattern was found and I believe that is what you want.

Sync two audio files

I have 2 audio files:
correct.wav (duration 3:07)
incorrect.wav (duration 3:10)
They are almost the same, but was generated with different sound fonts.
The problem: The second file is late for a few seconds.
How can I sync second file with the first one? Maybe there some bash software that could detect first loud sounds appearance in the first sound and compare correct.wav with incorrect.wav, shorten the end of the incorrect.wav file.
I know I can do it manually, but I need automated soulution for a lot of files.
Here is approximate solutions I found:
1) for detecting sound syncing to use this Python script - https://github.com/jeorgen/align-videos-by-sound but it's not perfect, not detecting 100%.
2) use sox for cutting/trimming/comparing/detecting sound durations (code extraction):
length1ok=$(sox correct.wav -n stat 2>&1 | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\)$#\1#p')
length2ok=$(sox incorrect.wav -n stat 2>&1 | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\)$#\1#p')
if [[ $length1ok == $length2ok ]]; then
echo "Everything OK: $length1ok = $length2ok"
else
echo "Fatal error: Not the same final files"
fi
diff=$(echo "$length2 - $length1" | bc -l)
echo "difference = $diff"
echo "webm $length1 not greater than fluid2 $length2"
sox correct.wav incorrect.wav pad 0 $diff
Comment to UltrasoundJelly's answer:
Here what result I get for your code:
Here what result I need:
Here's one solution:
Use ffmpeg to find the leading silence in each file
If the new file has a longer leading silence, trim the difference with sox
If the new file has a shorter leading silence, pad the start with sox
Trim the new file to the same length as the original with sox
Bash Script:
FILEONE=$1
FILETWO=$2
MINSILENCE=0.1
THRESH="-50dB"
S1=$(ffmpeg -i $FILEONE -af silencedetect=noise=$THRESH:d=$MINSILENCE -f null - 2>&1 | grep silence_duration -m 1 | awk '{print $NF}')
S2=$(ffmpeg -i $FILETWO -af silencedetect=noise=$THRESH:d=$MINSILENCE -f null - 2>&1 | grep silence_duration -m 1 | awk '{print $NF}')
if [ -z "$S1" ]; then echo "no starting silence found in $FILEONE" && exit 1;fi
if [ -z "$S2" ]; then echo "no starting silence found in $FILETWO" && exit 1;fi
DIFF=$(echo "$S1-$S2"|bc)
ISNEG=$(echo $DIFF'>0'| bc -l)
DIFF=${DIFF#-}
BASE="${FILETWO%.*}"
if [ $ISNEG -eq 1 ]
then
echo "$1>$2 ... padding $2"
SAMPRATE=$(sox --i -r $FILETWO)
sox -n -r $SAMPRATE -c 2 silence.wav trim 0.0 $DIFF
sox silence.wav $FILETWO $BASE.shift.wav
rm silence.wav
else
echo "$1<$2 ... trimming $2"
sox $FILETWO $BASE.trim.wav trim $DIFF
fi
length1=$(sox $FILEONE -n stat 2>&1 | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\)$#\1#p')
length2=$(sox $BASE.trim.wav -n stat 2>&1 | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\)$#\1#p')
if (( $(echo "$length2 > $length1" | bc -l) )); then
diff=$(echo "$length2 - $length1" | bc -l)
echo "difference = $diff"
sox $BASE.trim.wav finished.wav trim 0 -$diff
fi

send: spawn id exp6 not open while executing

#!/bin/bash
MN_ROUTER_NUM=$1 #5
MN_BANDWIDTH=$2 #12
MN_DELAY=$3 #1ms
MN_LOSS=$4 #10
PING_COUNT=$5 #100
PING_PACKET_SIZE=$6 #100
PING_INTERVAL=$7 #0
if [ ! -f topology.py ];
then
echo "topology.py file could not be found."
exit 1;
fi
(expect - << EOD
spawn sudo mn --custom topology.py --topo mytopo,n=$MN_ROUTER_NUM --link tc,bw=$MN_BANDWIDTH,delay=$MN_DELAY,loss=$MN_LOSS
set timeout 1000
expect "*mininet>*"
sleep 1
send -- "h1 ping h2 -s $PING_PACKET_SIZE -c $PING_COUNT -i $PING_INTERVAL \n"
expect "*mininet>*"
sleep 1
send -- "exit\n"
interact
EOD
) | tee .ping.out | grep -e "ttl=" | awk '{print $(NF-1)" ms"}' | cut -d'=' -f2 > n_${MN_ROUTER_NUM}_BW_${MN_BANDWIDTH}_delay_${MN_DELAY}_loss_${MN_LOSS}.out
cat .ping.out | grep -e "packets transmitted" -e "rtt min/avg/max/mdev"
rm -f .ping.out
echo "Delays are saved in "n_${MN_ROUTER_NUM}_BW_${MN_BANDWIDTH}_delay_${MN_DELAY}_loss_${MN_LOSS}.out
This script connects two hosts via routers.
When I run above script, I get this error sometimes, but not every time:
send: spawn id exp6 not open
while executing
"send -- "h1 ping h2 -s 100 -c 100 -i 0 \n""
I don't know why it sometimes work but sometimes do not.What could be the reason?

How to get the duration of a file in milliseconds

I am trying to get the duration of an audio file (.wav) in milliseconds.
I have seen some command lines using ffmpeg, but this library is deprecated (remplaced by avconv) on my Ubuntu version, and i didn't find anything on it.
I can get the duration by running avconv -i <file> but i am looking for the result in milliseconds.
Wrote this as a small stand-alone .shfile:
file="$1"
info=$(sox $file -n stat 2>&1)
full_length=$(echo "$info" | sed -n 's#^Length (seconds):[^0-9]*\([0-9.]*\).*$#\1#p')
seconds=$(echo $full_length | cut -f1 -d.)
if [ -n "$seconds" ]; then
milliseconds=$(echo $full_length | cut -f2 -d. | cut -c -3)
result=$(($seconds * 1000))
result=$(($result + $milliseconds))
echo "$result"
exit
fi
echo "0"
exit
Call it like
new-file.sh test.wav
To get a result like (duration in ms)
1337
(You can find the full code with all sources I used on GitHub)

Resources