Install and run

Some things are to be configured in ipcam2rec.sh. Adjust the paths to your logfiles (if needed) and your dyndns hostname, ipcam username and password.

LOGFILE="/var/log/ipcam.log"
FTPLOGFILE="/var/log/proftpd/proftpd.log"
CAMHOST="http://stream:pass@mynick.dyndns.org"

And here are the names and the ports (you forwarded the cams to) of every single cam. You can add up to 10 cams.

 if [[ $cam =~ 'ipcam1' ]]; then cam='ipcam1'; port=1111; fi
 if [[ $cam =~ 'ipcam2' ]]; then cam='ipcam2'; port=1112; fi
 if [[ $cam =~ 'ipcam3' ]]; then cam='ipcam3'; port=1113; fi

Installing the script is easy done. There are two cronjobs to install. The first one runs each minute and looks for cam to switch off (in case the last “FTP ping” is older than 1 minute). The second jobs also runs each minute and starts the ipcam2rec daemon if that is not started yet. (I’m sure, it can be done better with some rc.d stuff, but I’m not familiar with it.) So just add these lines to your cron table:

* * * * * find /var/run/ipcam?.pid -mmin +1 -exec /path/to/ipcam2rec.sh stopcam {} \; 1>/dev/null 2>&1
* * * * * if [ "`pidof -sx ipcam2rec.sh`" = "" ]; then nohup /path/to/ipcam2rec.sh start 1>/dev/null 2>&1 & fi

Starting the script is not needed, it will be started automatically by the cronjob within a minute. You can look how the daemon works in the logfile:

tail -f /var/log/ipcam.log

You may also want to setup a logrotation job in /etc/logrotate.d/ipcam for the log file:

/var/log/ipcam.log {
 weekly
 missingok
 rotate 4
 compress
 notifempty
 sharedscripts
}

And you have to add this line to the postrotate section for /var/log/proftpd/proftpd.log in /etc/logrotate.d/proftpd-basic in order to restart the daemon after log rotation. Otherwise inotifywait seems to loose the logfile monitoring after log rotation.

/path/to/ipcam2rec.sh stop # Restarts automatically by cron

And this is how the process tree is looking like:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root     29498  0.0  0.2  16884  1420 ?        S    21:37   0:00 /bin/sh ipcam2rec.sh start
root     29500  0.0  0.1   6028   572 ?        S    21:37   0:00  _ inotifywait ...
root     29501  0.0  0.1  16908  1032 ?        S    21:37   0:00  _ /bin/sh /root/ipcam2rec.sh start
root     29528 15.6 15.5 144852 81604 ?        S    21:37   0:06      _ /usr/local/bin/ffmpeg ...
root     29560  7.5 15.6 144864 81988 ?        S    21:37   0:02      _ /usr/local/bin/ffmpeg ...

As you see Ffmpeg processes takes a lot of RAM (15% each on my VPS). Thatswhy many parallel records can slow down your server. CPU load is not that problematic because the total load depends on the image frames transmitted per second and this amount is restricted by the upload bandwidth.

Daily job for finalizing video and cleaning up is also done by the main ipcam2rec.sh script. You may want to adjust the FTP directories or the path for the resulting files in the finalize section. Then just add this line to your cron table in order to start finalizing each night at 0:00 pm.

0 0 * * * /path/to/ipcam2rec.sh finalize YESTERDAY

The script does two things:

  • Process the single images uploaded by the cam via FTP into mp4 file. It adds a timestamp to each image using mogrify and then encode the images with Ffmpeg. The images will be deleted after that if you add clean to the command.
  • Check and merge the mp4 files recorded by the daemon during the day using AtomicParsley and MP4Box. The single mp4 files are deleted then if you add clean to the command. The limitation of MP4Box is that it only allows to merge 20 mp4 files at time. So we need a script called mergeMP4.sh for merging even more files.
#!/bin/bash

##################################################################
# mergeMP4.sh
# Merges multiple mp4 files
#
# USAGE: mergeMP4.sh -i "file1 file2"
#
# OUTPUT: final.mp4 (override this with -o new_target_name.mp4)
#
##################################################################

DIRS="."
OFILE=""
TARGET="final.mp4"

while getopts "i:o:a" opt; do
 case $opt in
 i)
 DIRS=$OPTARG
 ;;

 o)
 TARGET=$OPTARG
 ;;

 *)
 echo "USAGE: $0 -i file_list [-o output_file]"
 exit 1
 ;;
 esac
done

MP4BOX_PARAMS=""
MP4BOX_OPT=""

first=0
scale=4
page_size=10

listing=$DIRS

# convert to an array for easy access
count=1
for item in $listing; do
 array_listing[$count]=$item
 count=$((count + 1))
done

page_size=10
total_files=`echo $listing | wc -w`
page=1;
pages=$(($total_files / page_size))
last_items=$(($total_files % page_size))

# Work on the array
index=1

while [ $page -le $pages ]; do
 count=1
 mp4box_args=""
 while [ $count -le $page_size ]; do
 if  [ "$mp4box_args" == "" ]; then
 mp4box_args="-add ${array_listing[$index]}"
 else
 mp4box_args="$mp4box_args -cat ${array_listing[$index]}"
 fi
 count=$((count + 1))
 index=$((index + 1))
 done

 printf -v OFILE "ofile_%03d.mp4" $page
 /usr/bin/MP4Box $MP4BOX_PARAMS $mp4box_args -new $OFILE
 page=$((page + 1))
done

if [ $last_items -gt 0 ]; then
 count=1
 printf -v OFILE "ofile_%03d.mp4" $page
 mp4box_args=""

 while [ $count -le $last_items ]; do
 if  [ "$mp4box_args" == "" ]; then
 mp4box_args="-add ${array_listing[$index]}"
 else
 mp4box_args="$mp4box_args -cat ${array_listing[$index]}"
 fi
 count=$((count + 1))
 index=$((index + 1))
 done

 /usr/bin/MP4Box $MP4BOX_PARAMS $mp4box_args -new $OFILE
fi

mp4box_args=""
for ofile in `ls ofile*`; do
 if  [ "$mp4box_args" == "" ]; then
 mp4box_args="-add $ofile"
 else
 mp4box_args="$mp4box_args -cat $ofile"
 fi
done

/usr/bin/MP4Box $MP4BOX_PARAMS $mp4box_args -new $TARGET
rm ofile*.mp4

exit 0

Now you know how it’s done.