Walter Eastes
This page reports what I did to set up a video surveillance system using inexpensive cameras, an inexpensive video capture card, and an old computer. What I wanted was a system that would run day and night for months, capturing images from a couple of cameras whenever there was something happening, and that one could inspect from a computer far away. The main problem in accomplishing it, and what makes it interesting really, is that there are so many pieces that must work together and so many choices for each part. Thus there are many ways to do it, but this is what I did.
This first part deals with setting up a system that can be dialed up by telephone from a remote location to view the surveillance images. The second part shows what I did later to serve the images over the internet at much higher speeds.
The computer, named toad4, is an old 400 MHz Intel Pentium running Slackware 11.0 with the 2.6.17.13 Linux kernel. It has two 30 GB hard drives, one mounted as / and the other as /ext or "extra" that is dedicated to the surveillance images.
The camera is a Swann SW214-OCD indoor/outdoor color and infrared LED camera that comes with a 100 foot cable. The cable has both composite video and electrical power. The power can be supplied either at the camera end or at the computer end by plugging it into a transformer brick that plugs into an electrical outlet.
The camera connects to a PV-143 four port video capture card from Bluecherry. The video card has BNC connectors and I had to get composite to BNC connectors for it.
Although it is not the first thing one thinks about, it is critical for an unattended computer to restart and resume video capture after the electricity goes off and comes back on. Most PC-style computers today have a momentary contact power switch that must be pushed again after the electricity is restored to start the computer. We need to defeat this feature so that it starts when power is applied. Sometimes it is a BIOS option, but on this machine with a Microstar International MS-6119 ATX BX2 motherboard, one removes jumper JP1 on the motherboard.
With this change, it boots, checks the disks, and goes to the login prompt right after it is plugged in. Now all we need is to start the required services automatically, by putting statements at the end of the /etc/rc.d/rc.local script. These are described further on.
That the camera works was verified by plugging it into the composite video port of a (U.S.) television. It also works when plugged into the composite video port of a Hauppauge WinTV-PRV-250 PCI tuner card on another computer, where it was operated with the excellent ivtv driver from www.ivtvdriver.org or dl.ivtvdriver.org. There one sets input 1, the composite video input jack with
ivtvctl -p 1The NTSC format is the default. Now all you have to do is
cat /dev/video0 >something.mpgto produce an mpeg video. In fact you can do
cat /dev/video0 | xine -to watch the camera image with a couple of seconds delay. However, the motion video capture software will not work with ivtv and the video capture card operated with the bttv driver does not work this way.
The video capture card has one identifiable chip labeled
Conexant Fusion 878AClearly it is a Brooktree 878 compatible device that can work on one port at a time, which is all I need. The card is detected upon bootup and lspci shows
00:0e.0 Multimedia video controller: Brooktree Corporation Bt878 Video \ Capture (rev 11) 00:0e.1 Multimedia controller: Brooktree Corporation Bt878 Audio Capture \ (rev 11)(The backslash \ at the end of a line indicates that it was continued onto the following line.) Doing
modprobe -v bttvresults in this output from /var/log/messages:
Linux video capture interface: v1.00 i2c_core: exports duplicate symbol i2c_register_driver (owned by kernel) i2c_algo_bit: exports duplicate symbol i2c_bit_add_bus (owned by kernel) firmware_class: exports duplicate symbol release_firmware (owned by kernel) bttv: driver version 0.9.16 loaded bttv: using 8 buffers with 2080k (520 pages) each for capture bttv: Host bridge needs ETBF enabled. bttv: Bt8xx card found (0). ACPI: PCI Interrupt Link [LNKA] enabled at IRQ 11 ACPI: PCI Interrupt 0000:00:0e.0[A] -< Link [LNKA] -< GSI 11 (level, low) \ -$lt; IRQ 11 bttv0: Bt878 (rev 17) at 0000:00:0e.0, irq: 11, latency: 64, mmio: 0xe7002000 bttv0: detected: Provideo PV143A [card=105], PCI subsystem ID is aa00:1430 bttv0: using: ProVideo PV143 [card=105,autodetected] bttv0: enabling ETBF (430FX/VP3 compatibilty) bttv0: gpio: en=00000000, out=00000000 in=00ffffff [init] bttv0: using tuner=-1 bttv0: i2c: checking for TDA9875 @ 0xb0... not found bttv0: i2c: checking for TDA7432 @ 0x8a... not found bttv0: i2c: checking for TDA9887 @ 0x86... not found bttv0: registered device video0 bttv0: registered device vbi0 bttv0: PLL: 28636363 => 35468950 .. ok bttv0: PLL can sleep, using XTAL (28636363).The duplicate symbol errors come about because i2c and some other things are compiled into this particular kernel, and are not modules that modprobe tries to load. It causes no problem, however. Here is the partial lsmod output:
Module Size Used by bttv 158708 0 video_buf 19716 1 bttv ir_common 23684 1 bttv compat_ioctl32 1152 1 bttv v4l2_common 13568 1 bttv btcx_risc 4104 1 bttv tveeprom 13456 1 bttv videodev 6912 1 bttvTherefore, this line
modprobe bttvwas entered at the end of /etc/rc.d/rc.local.
Both the motion and the Zoneminder documentation suggest getting the camera to work with something like xawtv to determine the right parameters. Thus xawtv-3.95 was downloaded from bytesex.org/xawtv, compiled and installed. Whereas xawtv with the -hwscan option works fine, starting xawtv gives these messages and quits:
This is xawtv-3.95, running on Linux/i686 (2.6.17.13) WARNING: Your X-Server has no DGA support. WARNING: couldn't find framebuffer base address, try manual configuration ("v4l-conf -aNo amount of messing with v4l-conf or trying to enable DGA or load v4l in xorg.conf resulted in anything that worked. In fact I locked up the kernel hard as a brick a couple of times. It looks as if the graphics card is not going to display xawtv. It doesn't work with xine either.") Warning: Cannot convert string "-*-ledfixed-medium-r-*--39-*-*-*-c-*-*-*" \ to type FontStruct ioctl: VIDIOC_OVERLAY(int=1): Invalid argument
However, we don't need all of that. One can take a snapshot from the card with this sequence:
v4lctl setnorm NTSC v4lctl setinput Composite0 v4lctl snap ppm 720x480 snap0.ppmThis also works:
streamer -s 720x480 -o firstcapture.ppmFurthermore, doing
v4lctl setinput Composite1and plugging the camera into the second port works too. So now we know how to configure the motion software.
The 720x480 size was used here because that appears what the cameras will do, although there is a black border on two sides of the images that is a different size on different cameras. The Bluecherry web site says that the PV-143 can go as big as 640x480 NTSC, but we may be exceeding that here.
By the way, it took a lot of trial and error before I was able to get an image from the camera. At first, all I could get with either streamer or with motion was a blue screen, suggesting that no camera was connected to the capture card. Then I discovered that the video composite connector was very loose on the jack. All I had to do was bend the three ground tabs a bit inward to get good contact and right away got an image.
As another aside, here is a script that allows you to view the output of the camera more or less continuously:
#!/bin/bash # File name: seecam # Issues v4lctl statements to set up the capture card and # snap pictures continuously # Walter Eastes: May, 2009 # # The first arg is the camera input number, starting at 0 if [ $# -lt 1 ] ; then echo "Use: seecam [0,1,2,...]" exit else cam=$1 fi # # Set up the capture card and take the first shot v4lctl setnorm NTSC v4lctl setinput Composite$cam v4lctl snap ppm 720x480 seecam$$.ppm # # Run ImageMagick in the background, updating it each second display -update 1 seecam$$.ppm & # # Get rid of the image file when killed or stopped with Ctrl-C trap "rm seecam$$.ppm; exit" INT TERM # # Keep snapping images while true ; do v4lctl snap ppm 720x480 seecam$$.ppm doneIt snaps images as fast as it will go and uses ImageMagick to display them on the screen with a one-second update. But don't do this while motion is running (see the next section) or things will get really confused.
The package motion-3.2.10.1 was downloaded from www.lavrsen.dk/twiki/bin/view/motion/WebHome, compiled and installed. It is virtually certain that Zoneminder from www.zoneminder.com would work too, but motion appeared to be a lot smaller and simpler.
Since I have only one camera for now, all configuration was taken care of in the file /usr/local/etc/motion.conf, which contained these statements that were changed from the file as distributed:
v4l2_palette 4 # The video input to be used (default: 8) # Should normally be set to 0 or 1 for video/TV cards, and 8 for USB cameras input 0 # The video norm to use (only for video capture and TV tuner cards) # Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) norm 1 # Image width (pixels). Valid range: Camera dependent, default: 352 width 720 # Image height (pixels). Valid range: Camera dependent, default: 288 height 480 # Make automated snapshot every N seconds (default: 0 = disabled) snapshot_interval 60 # Draws the timestamp using same options as C function strftime(3) # Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock # Text is placed in lower right corner text_right %Y-%m-%d\n%T # Draw characters at twice normal size on images. (default: off) text_double on # Target base directory for pictures and films # Recommended to use absolute path. (Default: current working directory) target_dir /ext/motion # File extension .jpg or .ppm is automatically added so do not include this. # Note: A symbolic link called lastsnap.jpg created in the target_dir will always # point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' snapshot_filename %Y%m%d/%H%M%S-snap # File extension .jpg or .ppm is automatically added so do not include this # Set to 'preview' together with best-preview feature enables special naming # convention for preview shots. See motion guide for details jpeg_filename %Y%m%d/%H%M%S-%q
This file configures motion to take one snapshot every minute even if nothing is happening and save it in a directory under /ext/motion labeled with the four digit year, two digit month, and two digit day with the file name made up of the two digit hour (0 to 23), two digit minute, two digit second, and "-snap.jpg". When motion is detected, two images are captured per second and stored in the same directory with the same file name but ending in "-NN.jpg", where NN is a two digit sequence number.
The program is started simply with "motion" (really /usr/local/bin/motion as I installed it) and operates in the background. That statement goes into /etc/rc.d/rc.local too.
Now we have motion running continuously capturing images, and we need a convenient way to inspect them. Certainly one can cd to a given date in the /etc/motion directory and view the jpeg files with xv or something. But a better way would be to make them available to a web browser.
The Apache web server is installed but not started by default in this Slackware distribution. However, all that is needed is
chmod +x /etc/rc.d/rc.httpdto make it start at bootup, and
/etc/rc.d/rc.httpd startto start it now. One can do
/etc/rc.d/rc.httpd restartto reread its configuration file and begin again.
The configuration file is /etc/apache/httpd.conf and these are the lines that were changed from what was distributed:
MinSpareServers 1 MaxSpareServers 1 StartServers 1With just one user, me, it did not seem reasonable to start five servers as in the distributed configuration file.
When one requests the web page http://localhost, one gets the file /var/www/htdocs/index.html, which I changed to this:
<html> <head> <title> Video Surveillance </title> </head> <body> <center> <h2> Surveillance Web Pages </h2> </center> <ul> <p> <li>Camera images: <ul> <li><a href="cgi-bin/makeday.pl?today">Today</a> <li><a href="cgi-bin/makeday.pl?yesterday">Yesterday</a> <li><a href="cgi-bin/makemonth.pl?this">This month</a> <li><a href="cgi-bin/makemonth.pl?last">Last month</a> <li><a href="cgi-bin/earlier.pl">Earlier</a> </ul> <p> <li><a href="motion/">Motion Directory</a> <p> <li><a href="manual/index.html">Apache Manual</a> <li><a href="motion_guide.html">Motion Manual</a> </ul> </body> </html>
This index page retains the Apache manual, since it is handy to have it available, but adds a series of cgi scripts in perl. It is clear that the web pages for the motion images must be dynamically generated, since there is no way to write an html page knowing what images will be available in the future. If all else fails, the "Motion directory" entry gives access directly to the /etc/motion directory. To make the motion image directory available to the web pages, a link was made:
ln -s /ext/motion /var/www/htdocs/motion
The dynamic web pages are generated at several levels. At the top level, the "Earlier" link generates a table of all available years and months with this perl code:
#!/usr/bin/perl # File name: cgi-bin/earlier.pl # Displays the available year/month motion images # Walter Eastes: February, 2009 # Modified to show the number of days available in each month: June, 2009 # Got rid of this and the motion link to locate image files: September, 2009 # # Use: <a href="cgi-bin/earlier.pl">... # # Copyright (C) 2009 by Walter Eastes # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License in the file gpl.txt for more details. @months = ("January","February","March","April","May","June", "July","August","September","October","November","December"); print "Content-type: text/html\n\n"; print "<html>\n"; print "<head>\n"; print "<title>\n"; print "Years and Months with Surveillance Images\n"; print "</title>\n"; print "</head>\n"; print "<body>\n"; print "<h2>\n"; print "Years and Months with Surveillance Images\n"; print "</h2>\n"; print "<p>\n"; print "<table border cellpadding=12></center>\n"; print "<tr> <th>Year</th> <th>Month</th> </tr>\n"; # Write a table of available years and months @dirs = getfiles("../htdocs/motion/*"); $lastyyyy = $lastmm = ""; for $dir (reverse @dirs) { # Decode the directory name into 4 digit year, 2 digit month, 2 digit # day, but display only unique years and months $dir =~ /(\d\d\d\d)(\d\d)(\d\d)$/ or next; $yyyy = $1; $mm = $2; $dd = $3; $mo = $mm - 1; next if $yyyy eq $lastyyyy && $mm eq $lastmm; print "<tr> <td>$yyyy</td> <td><a href=\"makemonth.pl?$yyyy$mm\">"; print "$months[$mo]</a></td> "; print "</tr>\n"; $lastyyyy = $yyyy; $lastmm = $mm; } print "</table></center>\n"; print "<p>\n"; print "</body>\n"; print "</html>\n"; # GETFILES: Returns an array of the files matching the glob-style file name sub getfiles { my ($fname,$rest) = @_; local @files; @files = sort(glob($fname)); return @files; }
At the next level is the makemonth.pl script that is called with the four digit year and two digit month. The words "this" and "last" can be used also to work out this and last month at the time it is called. As can be seen in the following code, the real work is done by the cal program, the output of which is captured and formatted as an html table. Clicking on one of the days in this calendar executes makeday.pl with the selected year, month, and day.
#!/usr/bin/perl # File name: cgi-bin/makemonth.pl # Creates an html page with a calendar of the given month # Walter Eastes: February, 2009 # Modified to make a link only for days with images available: June, 2009 # Streamlined checking for days with images: September, 2009 # # Use: <a href="cgi-bin/mkmonth.pl?yyyymm">... # # Copyright (C) 2009 by Walter Eastes # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License in the file gpl.txt for more details. # Get the argument and work out the year and month $arg = $ENV{'QUERY_STRING'}; # Special cases of this and last if ($arg eq "this" || $arg eq "last") { ($sec,$min,$hr,$mday,$mo,$yr,$wday,$yday,$isdst) = localtime(time()); $yr += 1900; $mo += 1; if ($arg eq "this") { $year = $yr; $month = $mo; } elsif ($arg eq "last") { $month = $mo - 1; $year = $yr; if ($month <= 0) { $month = 12; $year -= 1; } } } else { ($year,$month) = unpack("A4 A2",$arg); } print "Content-type: text/html\n\n"; print "<html>\n"; print "<head>\n"; print "<title>\n"; print "Surveillance Images for the Month\n"; print "</title>\n"; print "</head>\n"; print "<body>\n"; print "<h2>\n"; print "Surveillance Images for the Month\n"; print "</h2>\n"; print "<p>\n"; # Get the calendar and put on a table heading open(FI,"/usr/bin/cal $month $year |") or die "makemonth.pl: cannot execute cal program\n"; defined($line = <FI>) or die "makecal.pl: cal returned nothing\n"; print "<center><table border=3 cellpadding=6>\n"; print "<tr><th colspan=7>$line</th></tr>\n"; defined($line = <FI>) or die "makecal.pl: calendar has only one line\n"; @day = unpack("A2 x" x 7, $line); print "<tr> "; for ($i = 0; $i < 7; $i++) { print "<th>$day[$i]</th> "; } print "</tr>\n"; # Read each week and write the table entries while (defined($line = <FI>)) { length($line) > 1 or last; $line = sprintf("%-21s", $line); @day = unpack("A2 x" x 7, $line); print "<tr> "; for ($i = 0; $i < 7; $i++) { if ($day[$i] + 0 == 0) { print "<td> </td> "; } else { $yrmoda = sprintf("%04d%02d%02d", $year,$month,$day[$i]); if (-d "../htdocs/motion/$yrmoda") { print "<td><a href=\"makeday.pl?$yrmoda\">$day[$i]</a></td> "; } else { print "<td>$day[$i]</td> "; } } } print "</tr>\n"; } print "</table></center>\n"; print "<p>\n"; print "</body>\n"; print "</html>\n";
Before printing each calendar entry, we check to see if the directory for that day exists with the "if ( -d ..." statement. If so, then it becomes a link to the next level program to display the images for that day. If not, then it is just printed as text.
Suceedingly lower levels of cgi scripts display the hours of the selected day showing how many snapshots and motion images are there. Eventually, one can select individual images to view directly in the browser, since they are just jpeg files.
We want to dial up toad4, the computer that is running motion and look at the images with a web browser that accesses the web server configured as described in the last section. In order to try out the setup without driving all the way to a remote computer, I set up another, even older computer, a 200 MHz Pentium called toad2, and connected it to toad4 with a serial null modem cable. The cable runs from /dev/ttyS0 on toad2 to /dev/ttyS0 on toad4.
The first thing to do is to enable login to toad4 on ttyS0, which is done by locating this line in /etc/inittab and changing it to
s1:12345:respawn:/usr/local/sbin/mgetty -r -s 115200 ttyS0The -r option indicates it is not a modem but a direct connection. The -s speed option is needed since the mgetty default is 38400. The command "telinit q" rereads the /etc/inittab file so that the changes take effect.
The getty installed on this Slackware distribution is getty_ps, which works for this serial line. But, as outlined in the next section, it failed to do dialup the way we need. Thus I downloaded mgetty-1.1.36 from mgetty.greenie.net, compiled and installed it. For use on a serial line, I needed to add the lines
port ttyS0 toggle-dtr noin /usr/local/etc/mgetty+sendfax/mgetty.config. By the way, the progress of mgetty can be watched by
tail -f /var/log/mgetty.ttyS0
Now starting "minicom -s" on toad2 allows one to backspace over all the modem initialization strings so that no initialization is done on ttyS0. Likewise, the speed needs to be set to 115200 to match what mgetty was told on toad4. One must not forget to save the settings, which go into the default file /etc/minirc.dfl. Then starting minicom with no arguments results in a login prompt and we can log in as some user. Note that root login is denied in /etc/securetty, as it does not list ttyS0.
The next step is to start the point to point protocol with pppd on both toad4 and toad2 so that we can run a web browser on toad2 and get web pages from the Apache server on toad4.
Configuration of pppd on toad4 is done with two files in /etc/ppp. General options for both serial and dialup are in /etc/ppp/options and are these:
asyncmap 0 noauth crtscts noipdefault -detach lcp-echo-interval 30 lcp-echo-failure 4We are not doing any authorization (noauth) or login with pppd since it was already done with mgetty. Options specially for the serial port ttyS0 on toad4 are in /etc/ppp/options.ttyS0 which contains
toad4:serial-client local 115200It specifies the IP addresses to assign to the local and remote machine as symbolic names, which are defined as dotted quads in the /etc/hosts file which contains
192.168.0.1 toad4 192.168.1.1 serial-client 192.168.2.1 dialin-clientThese are IP addresses reserved for local networks and not exposed to the internet. In addition, we set the alias
alias ppp=/usr/sbin/pppdto make it easy to start pppd on toad4. Not to forget, we need to make pppd executable by any user with
chmod u+s /usr/sbin/pppd
Now on toad2, we need /etc/ppp/options to have
asyncmap 0 noauth crtscts local 115200 noipdefault -detach lcp-echo-interval 30 lcp-echo-failure 4We might as well make an alias on toad2 also:
alias ppp="/usr/sbin/pppd /dev/serial"where we have already linked /dev/ttyS0 to /dev/serial. Why not put the corresponding IP address into /etc/hosts on toad2:
192.168.0.1 toad4
Now back to toad2 where we are logged into toad4 as some user through minicom. Just do "ppp" to start pppd on toad4. Nothing further will happen on toad2, but on toad4, /var/log/messages will note the startup of pppd. Then on toad2, leave minicom running and start another xterm. Here issue the "ppp" command to toad2. That will start pppd on toad2, which will register the local and remote IP addresses that we specified to pppd on toad4. We can leave this xterm just sitting there.
Now we can open yet another xterm and start up a browser with http://toad4 and get the index.html page configured on toad4 and view the motion images.
To stop pppd, go back to the xterm on which it was started on toad2 and kill it with Ctrl-C. We can do this because of the -detach option in the toad2 /etc/ppp/options file. Without it, it would run in the background and we could look up its process id with
ps aux | grep pppdand stop it with the kill command. A termination message will appear here on toad2 and also in /var/log/messages on toad4. Then you can go back to the minicom instance where the prompt has now returned and continue to work on toad4 through toad2. One logs off toad4 as usual, Ctrl-D will do it.
The modem on toad4 is /dev/ttyS1, which linked to /dev/modem. Everything done with the serial line applies to the modem with the added issues of dialin and security.
The dialin issue is that the phone line is also used for ordinary voice calls in and out and has an answering machine set to answer after four rings. We want to use this line to dial up toad4 on top of all of that, and to do it from a remote location, of course. The standard way to handle all of this is to use the ringback feature, not to be confused with call back, which is something else again. With ringback, you dial the number, let it ring a couple of times, but not long enough to trigger the answering machine, then hang up. Then within a short time, 30 seconds in this case, you dial again, and mgetty answers it on the first ring this time.
This ringback protocol neatly takes care of several situations. First of all, anyone calling is going to get the answering machine if they ring four times and there will be no other call in 30 seconds while they are leaving a message. If they give up before four rings, then they are not likely to try again within 30 seconds. Furthermore, if someone answers the phone in a couple of rings, then they can talk to the caller. But anyway, if I am dialing up toad4 to view the motion images, it is because no one is home to answer the telephone, right? Persistent telemarketers might ring twice in 30 seconds, but then they would get the high-pitched connection tone, which is just what they deserve.
The already installed getty_ps-2.1.1 is supposed to support ringback, but I had trouble with it. Not only did it ignore the -R ringback option, but it failed to respond to the INIT string. A look at the source code revealed that ringback was compiled out by default, but I gave up because of the other problem and installed mgetty. What mgetty is doing can be watched by running
tail -f /var/log/mgetty.ttyS1especially interesting while the phone is ringing.
The security issue is that we do not really want just anybody dialing up these motion images. Since toad4 and toad2 are not accessible to anyone but me, I do not have passwords on any of the accounts, not even on root. We are going to need something to protect dialup, but I do not feel like putting passwords on all the accounts, not even on root.
(By the way, setting no password is not quite straight forward. The trick is to set a password of anything at all, even an empty string, with adduser, for example. Then edit /etc/shadow and delete all the characters in the password field, the second one. Then you can log into the account and will not be asked for a password.)
A nice solution is to create a new user called "ppp", say, on toad4 with the same shell as any other ordinary user but supply a robust password just for this account. Then add this line to /etc/login.access on toad4:
-:ALL EXCEPT ppp:ttyS1Now the only way to login to toad4 on the modem line is as user ppp, for which a password is required. After successful login as ppp, then one can su - to another user, even to root without a further password. Thus there is no restriction on what a dialin user can do as long as they can authorize themselves with the ppp password.
With these two issues resolved, we can configure toad4 for remote dialin. We need this line in /etc/inittab:
s2:12345:respawn:/usr/local/sbin/mgetty -R 30 ttyS1and "telinit q" to restart it. The -R 30 option sets ringback with no more than 30 seconds delay between calls. Next, we need the following pppd options for the modem in /etc/ppp/options.ttyS1:
toad4:dialin-client 38400 modemIt differs from options.ttyS0 in that gives a different IP address to the remote machine, a modem is specified, at a slower speed. Now toad4 is ready for remote dialin.
The configuration of the remote machine is similar to that of the toad2 serial line except that it is a modem. The default minicom configuration is usually correct if the modem is linked to /dev/modem. The default speed in minicom is 38400 just like that for mgetty, so we go with it. It is easy to dial in manually with minicom on the remote machine with
at dt 1,123 456 7890for example. After a couple of rings, it may be interrupted by pressing the Enter key. Then issue the dial command again. A connect message and login prompt follows.
After successful login as user ppp on toad4, one can issue "ppp" to start pppd on toad4. One does not need to specify the line here, as it appears to use the login line by default. But then, when one starts pppd on the remote machine from another xterm,
/usr/sbin/pppd /dev/modemis needed.
From here on, it is the same as for the toad2 serial line.
It is handy to automate the process of dialing up toad4 with ringback from a remote location and then starting pppd. This remote machine is also used to dial up an ISP for internet access, but I want to dial up toad4 to browse the video surveillance images too. The pppd program with a chat script can do both.
There is one script to dial up the ISP and another to dial up toad4. The one to dial toad4 is called "toad4" and contains just this:
/usr/sbin/pppd call toad4.options
The options common to dialing the ISP and toad4 are in /etc/ppp/options and are these:
noipdefault modem /dev/modem crtscts asyncmap 0For both purposes, either the ISP or toad4 will supply the IP addresses, thus noipdefault. In additon, the same modem is used, linked to /dev/modem.
The options specially for toad4 are in /etc/ppp/peers/toad4.options and are these:
noauth 38400 -detach lcp-echo-interval 30 lcp-echo-failure 4 connect "/usr/sbin/chat -V -f /etc/ppp/toad4.chat"The advantage of putting these options in a call statement, which means they need to be in the directory /etc/ppp/peers, is that they can be priviledged options like noauth but can be done by an ordinary user. Of course, pppd needs to be setuid root as before. This trick comes straight from the examples in the man page for pppd.
The chat script in /etc/ppp/toad4.chat needs to trigger ringback on toad4. The following slick method is given in the web page carnot.pathology.ubc.ca/DebianPage.html, apparently written by Peter Easthope at the University of British Columbia:
TIMEOUT 60 ABORT ERROR ABORT BUSY ABORT "NO CARRIER" ABORT "NO DIALTONE" "" "at &er;F h0" OK "atdt 1,123 456 7890,,,;h0" OK \d\d\d\d\d\d\d\d\d\d\c "" "atdt 1,123 456 7890" "ogin: " ppp "assword: " "the password goes here" "$ " pppThe first atdt dial command uses a few commas to delay after dialing while it connects and rings a couple of times, then just hangs it up (h0). In the next line, the series of \d characters delay for long enough for the toad4 modem to discover that the line is dead and to reset and wait for the ringback. Then the next atdt dial command rings it back, ignores the CONNECT reply and gives the user name ppp (the only one that is allowed) and password. Following that, it waits on the command prompt "$ " and starts pppd on toad4. The instance of pppd that is now running on this remote machine starts the ppp protocol and signs on with the local and remote IP addresses as usual.
Now I just start up a web browser on the web address http://toad4, assuming that
192.168.0.1 toad4is in /etc/hosts here too.
Two more cameras were procured, but these were a different kind, QSee QS60F. The main difference is that they have somewhat stronger infrared capability and can see a bit farther in the dark than the first camera.
Motion has a roundrobin feature that is meant for handling this situation with a multiple input video capture card that can handle only one camera at a time. The configuration options common to all cameras go in /usr/local/etc/motion.conf, which now has these statements that differ from the distributed file:
v4l2_palette 4 # The video norm to use (only for video capture and TV tuner cards) # Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) norm 1 # Image width (pixels). Valid range: Camera dependent, default: 352 width 720 # Image height (pixels). Valid range: Camera dependent, default: 288 height 480 # Maximum number of frames to be captured per second. # Valid range: 2-100. Default: 100 (almost no limit). framerate 2 ############################################################ # Round Robin (multiple inputs on same video device name) ############################################################ # Number of frames to capture in each roundrobin step (default: 1) roundrobin_frames 1 # Number of frames to skip before each roundrobin step (default: 1) roundrobin_skip 2 # Try to filter out noise generated by roundrobin (default: off) switchfilter off # Make automated snapshot every N seconds (default: 0 = disabled) snapshot_interval 60 # Draws the timestamp using same options as C function strftime(3) # Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock # Text is placed in lower right corner text_right %Y-%m-%d\n%T # Draw a user defined text on the images using same options as # C function strftime(3) # Default: Not defined = no text # Text is placed in lower left corner ##; text_left CAMERA %t # Draw characters at twice normal size on images. (default: off) text_double on # Target base directory for pictures and films # Recommended to use absolute path. (Default: current working directory) target_dir /ext/motion # File path for snapshots (jpeg or ppm) relative to target_dir # Default: %v-%Y%m%d%H%M%S-snapshot # Default value is equivalent to legacy oldlayout option # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot # File extension .jpg or .ppm is automatically added so do not include this. # Note: A symbolic link called lastsnap.jpg created in the target_dir will always # point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' ##snapshot_filename %Y%m%d/%H%M%S-snap # File path for motion triggered images (jpeg or ppm) relative to target_dir # Default: %v-%Y%m%d%H%M%S-%q # Default value is equivalent to legacy oldlayout option # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q # File extension .jpg or .ppm is automatically added so do not include this # Set to 'preview' together with best-preview feature enables special naming # convention for preview shots. See motion guide for details ##jpeg_filename %Y%m%d/%H%M%S-%q # Remember: If you have more than one camera you must have one # thread file for each camera. E.g. 2 cameras requires 3 files: # This motion.conf file AND thread1.conf and thread2.conf. # Only put the options that are unique to each camera in the # thread config files. thread /usr/local/etc/motion-front.conf thread /usr/local/etc/motion-side.conf thread /usr/local/etc/motion-back.conf
The main change is to invoke thread files at the end. I also needed to set roundrobin_skip 2 from the default 1. Without that modification, there were a lot of "composite" images taken, that is, images that had parts of one camera image and parts of another. Apparently, motion was switching inputs and grabbing an image while the last capture was in progress. Of course, such composite images were detected as motion and saved as motion images. After the number of frames to skip was bumped up to 2, composite images rarely were seen again. By the way, increasing roundrobin_frames did not help anything, and turning on switchfilter pretty much ignored all motion.
The thread files mentioned in motion.conf are motion-xxx.conf, where xxx is some description of the camera location. Here is one of them:
# File name: /usr/local/etc/motion-back.conf # Front camera thread file for motion # Walter Eastes: April, 2009 # The video input to be used (default: 8) # Should normally be set to 0 or 1 for video/TV cards, and 8 for USB cameras input 2 # Draw a user defined text on the images using same options as C function strftime(3) # Default: Not defined = no text # Text is placed in lower left corner text_left Back # File path for snapshots (jpeg or ppm) relative to target_dir # Default: %v-%Y%m%d%H%M%S-snapshot # Default value is equivalent to legacy oldlayout option # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot # File extension .jpg or .ppm is automatically added so do not include this. # Note: A symbolic link called lastsnap.jpg created in the target_dir will always # point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' snapshot_filename %Y%m%d/%H%M%S-snap-back # File path for motion triggered images (jpeg or ppm) relative to target_dir # Default: %v-%Y%m%d%H%M%S-%q # Default value is equivalent to legacy oldlayout option # For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q # File extension .jpg or .ppm is automatically added so do not include this # Set to 'preview' together with best-preview feature enables special naming # convention for preview shots. See motion guide for details jpeg_filename %Y%m%d/%H%M%S-%q-back
This thread file is for the camera plugged into the third input, numbered input 2, starting at 0. It puts the camera designation "Back" in the lower left corner of the images, names the snapshot images
yyyymmdd-snap-back.jpgand the motion images
yyyymmdd-NN-back.jpgNow both the image file names and the images themselves are identified as to the camera, which is useful.
With three cameras running all the time, things can get out of hand. For example, when one finds a few thousand images on the back camera between 3 and 4 pm one day that are nothing but bright sunlight speckled through wind blown tree branches, it is handy to delete them to keep from gobbling up disk space. One could try logging in and doing
rm /ext/motion/20090521/15*-0*-back.jpgto delete just the motion images. But that often yields too many arguments for the bash shell to handle. This would work:
find /ext/motion/20090521 -name "15*-0*-back.jpg" | xargs rmbut it is getting really inconvenient to type it right. You really want to be able to delete a range of specific images right from the web browser where you were viewing them.
It is easy to put a link to this script on appropriate pages:
#!/usr/bin/perl # File name: cgi-bin/delete.pl # Displays the motion images for each hour of the selected day and provides # links that will delete the selected ones # Walter Eastes: May, 2009 # # Use: <a href="cgi-bin/delete.pl?yyyymmdd">... # Copyright (C) 2009 by Walter Eastes # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License in the file gpl.txt for more details. @months = ("","Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"); @cams = ("front","side","back"); # Get the argument and work out the year, month, and day $arg = $ENV{'QUERY_STRING'}; if ($arg eq "today") { ($sec,$min,$hr,$mday,$mo,$yr,$wday,$yday,$isdst) = localtime(time()); $year = $yr + 1900; $month = $mo + 1; $day = $mday; } elsif ($arg eq "yesterday") { ($sec,$min,$hr,$mday,$mo,$yr,$wday,$yday,$isdst) = localtime(time() - 60*60*24); $year = $yr + 1900; $month = $mo + 1; $day = $mday; } else { ($year,$month,$day) = unpack("A4 A2 A2",$arg); $year += 0; $month += 0; $day += 0; } print "Content-type: text/html\n\n"; print "<html>\n"; print "<head>\n"; print "<title>\n"; print "Delete Selected Images from $day $months[$month] $year\n"; print "</title>\n"; print "</head>\n"; print "<body>\n"; print "<center>\n"; print "<h2>\n"; print "Delete Selected Images from $day $months[$month] $year\n"; print "</h2>\n"; print "</center>\n"; print "<p>\n"; # Write a table of hours of the day print "<center><table>\n"; $ncams = @cams; print "<tr> <th rowspan=2>Hour</th> <th colspan=$ncams>Snaps</th>"; print " <th colspan=$ncams>Motion</th> </tr>\n"; print "<tr>"; for $cam (@cams) { print "<th>$cam</th>"; } for $cam (@cams) { print "<th>$cam</th>"; } print "</tr>\n"; for ($i = 0; $i < 24; $i++) { $yyyymmdd = sprintf("%04d%02d%02d", $year,$month,$day); $hh = sprintf("%02d",$i); printf("<tr> <td>%02d</td>", $i); # # Write the number of snapshots for each camera for $cam (@cams) { $camsnap = getfiles("/ext/motion/$yyyymmdd/$hh*-snap-$cam.jpg"); print "<td><a href=\"rmglob?$yyyymmdd/$hh*-snap-$cam.jpg\">"; print "$camsnap</a></td>"; } # # Write the number of motion images for each camera for $cam (@cams) { $cammove = getfiles("/ext/motion/$yyyymmdd/$hh*-0*-$cam.jpg"); print "<td><a href=\"rmglob?$yyyymmdd/$hh*-0*-$cam.jpg\">"; print "$cammove</a></td>"; } print "</tr>\n"; } print "</table></center>\n"; print "<p>\n"; print "</body>\n"; print "</html>\n"; # GETFILES: Returns an array of the files matching # the glob-style file name sub getfiles { my ($fname,$rest) = @_; local @files; @files = sort(glob($fname)); return @files; }
The above perl code generates a table of numbers of snapshots and motion images for each camera at each hour of the selected day. Clicking on a number in a column deletes all of those images, because it is a link to rmglob with the file names specified in the same glob notation that did not always work in the rm command shown above.
The rmglob program is written in C and compiled so that it can be setuid to the owner of the motion images and thus has the permission to delete them. The glob routine in the C library makes rmglob.c really simple:
/* File name: rmglob.c * Deletes the selected motion images * Replaces rm.pl with a compiled program that can be setuid root * so that it can delete images no matter what the owner * Use: rmglob?yyyymmdd/hh....jpg * Walter Eastes: May, 2009 */ /* Copyright (C) 2009 by Walter Eastes * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License in the file gpl.txt for more details. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <glob.h> int main( ) { glob_t globbuf; int i,n; char *path,*querystring; char *prefix = "/ext/motion/"; printf("Content-type: text/html\n"); printf("Status: 204 No response\n"); printf("Status code 204: This message should not be displayed\n"); printf("\n"); /* Get the querystring and prefix it with the motion directory */ querystring = getenv("QUERY_STRING"); if (!querystring) exit(1); path = malloc(strlen(prefix) + strlen(querystring) + 1); if (!path) exit(2); strcpy(path,prefix); strcat(path,querystring); /* Get the list of matching files and delete them */ glob(path,0,NULL,&globbuf); n = globbuf.gl_pathc; for (i = 0; i < n; i++) unlink(globbuf.gl_pathv[i]); exit(0); }
By default, the Apachee web server runs the cgi programs as user nobody, whereas motion is started by /etc/rc.d/rc.local as user root, although it does not need to be. One solution might be to run motion as nobody also so that the motion images could be deleted by scripts run by Apachee. That should work, but I prefer rmglob to deal with whatever motion gives it. Thus rmglob.c is compiled to rmglob and then
chmod u+s /var/www/cgi-bin/rmglob
Under some circumstances, having rmglob setuid root would be a major security disaster as it allows any user to delete any or even all files on the system. But on this particular system running the surveillance cameras, any user can do "su -" and be running as root anyway. So rmglob gives nothing that they did not have already. Note that dial-in users need to give the ppp password first before they can do any of this.
A number of improvements suggest themselves. For one, the presence of a high speed internet connection brings up new possibilities, some simplifications and some complications. That is described in Part II of these pages. They are added to from time to time.
This page was last updated in September, 2009.
This document is