Stop-Motion Security Cam with Linux and Android

Here's an interesting use for your old Android phone. Grab a tripod mount for phones, such as the Case Star or the Square Jellyfish and download ipwebcam (free) from the Google Play store.

With just those items and a Linux computer you can easily capture photos or video automatically to monitor your house or your pets.

grab stills

Once you have ipwebcam running, which is so simple I won't provide any explanation here, log into it from your computer's browser and you'll see two links for capturing stills. One says Download immediate video frame, and allows you to download a low-res photo. This will be smaller and faster. The other photo option says Take full-resolution photo, and takes a photo in the full resolution you specified in your ipwebcam settings.

A simple wget command is sufficient to grab the most recent photo:

wget http://your_ip:8080/photo.jpg

However, this will use the same filename (photo.jpg) for each file, overwriting previous versions, so we'll add a timespamp to the filename:

STAMP=$(date "+%Y-%m-%d_%H%M%S")
wget -O "${STAMP}.jpg" 'http://your_ip:8080/photo.jpg'

Next, run this in a loop, so you download a photo about once per second (depending on your network speed and how powerful your Android device is).

while true; do

    STAMP=$(date "+%Y-%m-%d_%H%M%S")
    wget -O "${STAMP}.jpg" 'http://your_ip:8080/photo.jpg' || break

done

Note the break command -- if the phone reboots or the network connection is lost for some reason, this prevents you from saving a bunch of empty files.

Here's the full version of the script I'm using, with a several features not yet discussed:

#!/usr/bin/env bash

#redis isn't on the PATH by default
#when the script is read by cron
export PATH=/usr/local/bin:$PATH

#place to put the images
cd /home/shawn/temp/ipwebcam

#a key for redis
key=grab_photo_counter
#check key
x=$(redis-cli incr $key)

#no way the key will somehow live forever
#without a process
redis-cli expire $key 10

#if the value is 1, no other process
#is running, or has been very recently
if [ "$x" != "1" ]
then
    exit
fi

while true; do

    STAMP=$(date "+%Y-%m-%d_%H%M%S")
    wget -O "${STAMP}_high.jpg" 'http://your_ip:8080/photo.jpg' || break

    #keep telling Redis I'm still working
    redis-cli expire $key 30

done

This script makes great use of Redis as a tool for shell scripting. The redis-cli program can easily be used to maintain state, avoid race conditions, and store persistent data between runs for various things. In this case, we're just using it to ensure that two instances of this script don't run at the same time.

Now that we have both the Redis and the break command, you can see that if you schedule this to run regularly in your crontab, it will die when your phone isn't serving data, but wake up regularly to try again. I run it every 15 minutes via cron.

create video directly

You can, of course, use VLC from the command line to capture the video stream directly from the camera. This, however, takes up a lot of space, and is much more difficult to scan for activity.

If you want to record the video directly, here's a working example that will record to an ogv file with no audio, headless (so it can be run remotely):

cvlc http://your_ip:8080/video --sout \
    '#transcode{vcodec=ogg, acodec=ogg, vb=100, venc=theora, fps=6, acodec=none} \
    :standard{access=file, dst=/tmp/home_%Y-%m-%d_%H%M%S.ogv, mux=ogg, venc=ogg}'

create stop-motion video

All that's left, really, is to use mencoder to create a video from the stills. Here's a two-pass method to produce high-quality video. The reason a file list is created is that both passes have to use exactly the same input, or mencoder will return an error.

The reason the first pass sends the output to /dev/null (a black hole) is that it's only interested in a log file that's created during the pass. This log file is used during the second pass to create the actual output.

INPUT=/tmp/files.txt
\ls -1 | grep ".jpg" | sort > $INPUT

#high quality
let vbr=60*$fps*1024*768/256

filename=${timestamp}_${fps}fps.avi
#time mencoder -nosound -ovc lavc -lavcopts vcodec=mpeg4 -o $filename -mf type=jpeg:fps=${fps} mf://@${INPUT}
mencoder -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$vbr:mbd=2:trell:vpass=1 -mf type=jpg:fps=$fps -o /dev/null "mf://@$INPUT"
mencoder -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$vbr:mbd=2:trell:vpass=2 -mf type=jpg:fps=$fps -o ${filename} "mf://@$INPUT"

I got a lot of help from a couple of blog posts to help me piece that together, and there's a lot of other good info there you might be interested in. So check these out: cenolan and terencechan.

full video-creation script

Just as in the capture script, I'm running a fully-automated version of the mencoder script. In addition to just creating a video, it copies it to my Dropbox on a schedule, and watermarks each image with the image name (since it contains a convenient timestamp).

This script also creates videos with multiple framerates. This way, you can watch all the video very quickly, and if there's anything you want to see in more detail you can find the same timestamp (via watermark) in the lower-framerate video.

For more information on creating watermarks, check out the excellent ImageMagick documentation.

#!/usr/bin/env bash

#redis isn't on the PATH by default
export PATH=/usr/local/bin:$PATH

cd /home/shawn/temp/ipwebcam

#cleanup junk files
#junk photos are zero bytes
#junk video files are ~4k
find . -size -1 -exec rm -f {} \;
find . -size -6k -name '*.avi' -exec rm -f {} \;

#timestamp for this run, will be used for
#naming the video files
timestamp=$(date +"%Y-%m-%d_%H%M")

INPUT=/tmp/files.txt
ARCHIVE=/path/to/external/drive/

#create input file of all current files
#this is so that both mencoder passes work on the same
#set of files; if we just use *.jpg as input, the second
#pass fails because the number of input frames is different
#
#this file is also convenient for later just cleaning up
#only the files processed in this run
\ls -1 | grep ".jpg" | sort > $INPUT

#watermark the filename so timestamp is in video
for X in *.jpg
do

    key="${X}_stamped"

    #skip records already done
    if [ $(redis-cli exists $key) == 1 ]
    then
        continue
    fi

    #set key, add watermark, move original to archive
    redis-cli setex $key 86400 "foo" \
    && convert $X -font courier -pointsize 40 \
        -draw "gravity south fill white text 1,11 '$X' " /tmp/temp.jpg \
    && mv $X $ARCHIVE \
    && mv /tmp/temp.jpg $X

done


#make videos
for fps in 4 32

do

    #high quality
    #let vbr=60*$fps*1024*768/256

    #half quality
    let vbr=30*$fps*1024*768/256

    filename=${timestamp}_${fps}fps.avi
    #time mencoder -nosound -ovc lavc -lavcopts vcodec=mpeg4 -o $filename -mf type=jpeg:fps=${fps} mf://@${INPUT}
    mencoder -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$vbr:mbd=2:trell:vpass=1 -mf type=jpg:fps=$fps -o /dev/null "mf://@$INPUT"
    mencoder -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=$vbr:mbd=2:trell:vpass=2 -mf type=jpg:fps=$fps -o ${filename} "mf://@$INPUT"

done

#delete watermarked files
cat $INPUT | xargs rm -f

#move video to Dropbox
mv -u *.avi ~/Dropbox/ipwebcam/

conclusion

There are some issues I'd like to correct, such as splitting the watermarking and video creation into different scripts, but overall this works very well.

You can also make use of a service such as No-IP to give your home machine a domain name so you can easily monitory your ipwebcam server from anywhere.

Comments !

blogroll

social