Walter Eastes
The previous page, Part I, described setting up a continuously running video surveillance system that one could dial into by telephone from a remote computer to view the surveillance images. That works nicely, but getting the images is really slow at telephone speeds. Connecting to high speed internet changes all that and allows one to view the images just about as fast as one can on the machine that is running the cameras. As usual, there are lots of ways to do this, but here is what I did.
About the time I got high speed internet, the computer capturing surveillance images, an old 400 MHz Intel Pentium called toad4, started getting disk errors and soon would not even boot up. So I broke down and replaced it with a new computer, a PowerSpec N101 from Micro Center with a 2 GHz Intel Celeron, costing all of $150 after all rebates and discounts for this discontinued item. I'm calling it toad20. Slackware 11.0 was installed with the 2.6.17.13 Linux kernel. The 160 GB hard drive was partitioned into 56 GB for the system mounted as /, 100 GB for images mounted as /ext, and 4 GB swap.
On this machine, the video capture card is detected at boot up and the bttv driver is loaded automatically. The v4lctl program from xawtv works fine and so does motion with the same configuration files as used on toad4. Likewise, the Apache web server runs as before.
We need this feature because there will not always be somebody around to push the front panel button when the electricity comes back on. The BIOS on toad20 is "American Megatrends" and is entered by pressing the "Delete" key during bootup. The cryptic entry to change is "PWRON After Pwr-Fail" under "Power Management Setup". It was changed to "Power On" from "Power Off".
I had basically a hell of a time getting an ethernet connection to work, which surprizes me, since it has been pretty trivial with every other computer. The internet connection is through a Motorola cable modem provided by Time Warner Roadrunner to which I had connected a Dlink DIR-625 wireless router. A CAT5 cable connects the router to toad20's built-in ethernet port. The ethernet interface is detected as a RealTek RTL8139 and becomes eth0, automatically loading the 8139too driver. It was configured for a dynamic IP address and the rc.inet1 script in /etc/rc.d calls dhcpcd right after setting up the loopback interface.
This setup has worked flawlessly with other computers, but here, dhcpcd printed the eth0 MAC and timed out after the default 60 s with /var/log/syslog reporting
dhcpcd times out waiting for a valid DHCP server responseDoing "ifconfig -a" showed no inet address. Logging into the Dlink router with another computer showed that an IP address was assigned, but toad20 appeared to know nothing about it. Immediately I suspected that something was wrong with the CAT5 cable I had strung through the floor and across the rafters to toad20. I lugged another computer down and hooked it up to the same cable. It worked fine with an internet connection and for browsing at high speed on the internet. So it is not the wire, but rather the ethernet interface that is misconfigured.
Since a web search turned up many complaints that the 8139 card stopped working upon upgrading from kernel 2.4 to 2.6, I set up LILO with a Linux 2.4 option and booted that. It didn't help: dchcpcd timed out just as before. I tried a whole bunch of ideas from a web search to get this ethernet interface to work, including the following:
append pci=routeirq
/etc/rc.d/rc.inet1 eth0_restartto stop and start just the ethernet interface, leaving loopback alone. dhcpcd still times out. One can ping the Dlink and it gets errors on half or more of the RX packets.
Good web pages were www.scrounge.org/linux/nics.htm, www/debian.help.org/node/7222, and www/brennan.id.au/04-Network_Configuration.html.
Reluctantly I gave up on this interface, installed the card that was bought for toad4, and booted Linux 2.6 with no extra options. Now dhcpcd runs and displays the new MAC and IP address right away. lspci has both ethernet interfaces with the new one as eth0, which is convenient. It is an RTL 8169 on irq 11, which interestingly enough is shared among libata, uhci_hcd:usb3, and eth0. In spite of the fact that sharing the eth0 interrupt was supposed to cause problems, it works just fine.
One must conclude that the Linux 8139 driver is in a really sorry state.
Actually, it was a coincidence that the new ethernet card was assigned eth0. It is in fact random. It turned out that I was accidently running udev on toad20, because /etc/rc.d/rc.udev was executable. I must have missed it when installing Slackware. But this is why the bttv driver was loaded automatically whereas it had to be modprobe'd explicitly on toad4. But since udev is running everything well, I'll stay with it.
Anyway, to get the etho assignment every time, the /etc/udev/rules.d/network-devices.rules file was edited to uncomment these lines:
KERNEL=="eth?", SYSFS{address}=="00:08:...", NAME="eth0" KERNEL=="eth?", SYSFS{address}=="00:1e:...", NAME="eth1"The comments in this file show what to do. The clue to look here was in the CHANGES_AND_HINTS.TXT file on the first Slackware CD.
The Dlink wireless router was configured to pass ports 22 (ssh) and 80 (http) through to toad20 on the reserved internal IP address. Now the surveillance images can be seen by pointing a web browser at the external IP address that is assigned at the time.
With the web server for surveillance images open to the internet, we need password protection for it, as there is no reason to allow just anyone to view them. That was done by adding these statements to /etc/apache/httpd.conf:
Then the password file was created withAuthType Basic AuthName "Surveillance Camera Images" AuthUserFile /etc/apache/passwd Require valid-user
htpasswd -c /etc/apache/passwdadding a user name and a password. Now when a web browser is pointed to http://localhost on this machine or to the internal IP address of this machine from any computer on the local network, or to the external IP address of the cable modem, then it asks for a user name and password.
The surveillance web pages were revamped to remove the delete option, since these web pages are not strongly enough protected to risk having that option available.
I want to be able to log into toad20 securely from the internet to be able to change the configuration, just as was possible by dial up. That is why the router was configured to forward port 22. For this purpose, a user ppp with a robust password was added and the line
AllowUsers pppwas added to /etc/ssh/sshd_config. This change allows one to do
ssh ppp@11.22.33.44from the local network or from the internet, giving either the local network or the external IP address. Login by ssh is restricted to user ppp by two mechanisms: One is that only ppp is allowed in the configuration file. The other is that ssh cannot handle users with no password as are the other accounts on this machine. As discussed for the dial up case, this is no restriction, since user ppp can su - to any other user.
The above configuration works fine for plain command line access, but to use X programs, a bit more is needed. On both toad20 and on the remote machine, /etc/ssh/sshd_config was edited to uncomment these lines:
X11Forwarding yes X11DisplayOffset 10 X11UseLocalHost yesThen /etc/ssh/ssh_config was edited to uncomment this:
ForwardX11 yesA couple of HOWTO's on the web suggested that we need
ForwardX11Trusted yesand that was added to ssh_config on both machines. Then one does
/etc/rc.d/rc.sshd restarton both machines to reread the ssh configuration files.
With these changes, one can login to toad20 with ssh and use gv and xpdf, for example, just as one does on the local computer. The -X and -Y options are not needed. (If the ForwardX11Trusted statement is left out, then gv and xpdf work, but -Y is needed for xfig and some other programs.)
By the way, when perl or C code is discussed in web pages like these, I like the code to appear right in the text, not as a link to the source file that may or may not display conveniently in my browser. That means that the code needs to be edited a bit so that it will render the way it looked originally. Placing it between <pre> and </pre> tags goes a long way toward getting the browser to leave it alone, but a little more is usually needed. In particular, characters like < and > need to be changed, which is easy to do in a text editor, but gets tedious fairly quickly. Here is a sed script that takes care of the usual issues:
#!/bin/bash # File name: code2html # Converts Perl, C, and even html to text that can go between # <pre> and </pre> tags and display properly. # Walter Eastes: September, 2009 # sed -e "s/&/\&/g" -e "s/</\</g" -e "s/>/\>/g" $1I used this script on itself to get it into this web page. Here is another script that goes in the opposite direction, in case you might want to snip out some code from these web pages and use it:
#!/bin/bash # File name: html2code # Converts code displayed in html back to code, reversing what # code2html does # Walter Eastes: September, 2009 # sed -e "s/</</g" -e "s/\>/>/g" -e "s/&/\&/g" $1
Since the IP address of the Time Warner Roadrunner cable modem is dynamically assigned, we need some way to find out what it is from a remote location. The cable modem knows its IP address at all times, of course, and the computer running the surveillance cameras can find it out. There are a lot of schemes described in some excellent web pages for checking the IP address periodically and sending email or updating a web page on another server when it changes. But here it is even easier. First of all, there are organizations that provide free domain names that will resolve to my present IP address and can be updated when it changes. The router manufacturer Dlink even provides one. But I chose to use dyndns.org, which has been providing this service for ten years now. I registered at their web site, chosing from one of their domain names, a user name and password. They send an email, which when I confirm it by going to the link in the email and checking out the shopping cart at no charge, makes the host name available on the name servers.
The second step is to arrange for the IP address to be updated whenever it changes, but not to update it if it has not changed. In addition, it can be updated even if it has not changed once every 28 days, and it must be updated within 30 days or they drop you. Most conveniently, the Dlink router contains an update client for several such services including this one. It is under Tools -> Dynamic DNS where one fills out the information I got by email from dyndns.org and a timeout of 672, which is 28 days in hours. This configuration is supposed to update my selected URL with the new IP address whenever it changes and every 28 days even if it has not changed to keep the registration current.
The web pages are a good way to view the surveillance images because they may be inspected with any web browser running on any operating system connected to the internet. But with an ssh client and X11 forwarding, we can do a lot more.
It is convenient to select and view images with scripts written in Tcl and Tk. Suppose the variable $imgfile has the name of a jpeg file that contains a surveillance image. It can be displayed with this sequence:
# Use the djpeg program to expand the jpeg image into # ppm in the variable $imgdata set imgdata [exec djpeg $imgfile] # Create a new photo image with it called "img" image create photo img -data $imgdata # Set up a canvas with the size we already know canvas .image -width 720 -height 480 # Create an image in this canvas and display the previously # created photo "img" in it .image create image 0 0 -image img -anchor nw pack .image ...
To display another surveillance image in place of this one, all you need to do is to execute the first two statements again with a new file name in $imgfile. It will replace the photo image img and that replaces what is displayed.
To display a sequence of surveillance images one after the other like a movie, just do this, where $filelist is a list of image file names:
foreach file $filelist \ { set imgdata [exec djpeg $file] image create photo img -data $imgdata update after 50 }The purpose of the update statement is to allow the screen to update and show the newly created image before clobbering it with the next one. Without it, you don't see any image until the foreach loop is finished and it shows the last one. The after statement waits 50 ms before continuing. Since motion images are taken twice a second (500 ms between them), a 50 ms delay shows them at 10 times actual speed. Without an after statement, a fast computer zips through them so quick it makes your head spin.
A really slick thing to do is to make the delay a variable set in a slider or scale object. Then you can adjust the speed as you watch. A variable speed movie! Here's the code for a slider:
scale .speed -from 1 -to 50 -variable speed pack .speed ...Now change the after statement to this:
after [expr 500/$speed]The scale shows the speedup factor from 1 times or actual speed to 50 times actual in this example.
Here is a complete program that allows one to select one or more image files for display individually or as a movie.
#!/usr/bin/wish # File name: winnow # Displays a list of image files named on the command line and allows # one to inspect them individually or to show them as a movie or to delete # selected files # Walter Eastes: April, 2009 # Use: winnow files... # # 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. ############## # PARAMETERS # ############## set font "*-Helvetica-Bold-R-Normal-*-14-*" set monofont "*-Courier-Bold-R-Normal-*-14-*" set bigfont "*-Helvetica-Bold-R-Normal-*-17-*" tk_setPalette gray50 ############### # SUBROUTINES # ############### #SHOWIMAGE: Displays the imgfile on the photo image on the canvas proc showimage { imgfile } \ { set imgdata [exec djpeg $imgfile] image create photo img -data $imgdata } ######## # MAIN # ######## # List all the files on the command line set imagelist { } foreach arg $argv { lappend imagelist $arg } if { [llength $imagelist] <= 0 } { exit } # Show the first file listed showimage [lindex $imagelist 0] frame .left frame .buttons # The ALL button selects all the files so you don't have to stroke # through all of them set butwid 6 button .all -text "ALL" -font $font -width $butwid -command \ { .images.l selection set 0 end } # The SHOW button shows a movie of all the selected images button .show -text "SHOW" -font $font -width $butwid -command \ { set filelist [.images.l curselection] set cancel 0 foreach ndx $filelist \ { showimage [lindex $imagelist $ndx] update if { $cancel } { break } after [expr 500/$speed] } } # The CANCEL button stops a movie immediately without waiting for it # to run through button .cancel -text "CANCEL" -font $font -width $butwid -command \ { set cancel 1 } # The QUIT button or the "q" key ends the program button .quit -text "QUIT" -font $font -width $butwid -command { exit } # The DELETE button deletes all the selected image files, not just from # the listbox, but also off the disk! button .delete -text "DELETE" -font $font -width $butwid -command \ { # Remove each selected file set ndxlist [.images.l curselection] foreach ndx $ndxlist \ { exec su -c "rm [lindex $imagelist $ndx]" } # Go through the set of image files, saving only those not in the list # to remove set imagetmp { } for { set ndx 0 } { $ndx < [llength $imagelist] } { incr ndx } \ { if { [lsearch -exact $ndxlist $ndx] < 0 } \ { lappend imagetmp [lindex $imagelist $ndx] } } set imagelist [concat $imagetmp] } pack .all .show .cancel .quit -in .buttons -side left -padx 10 pack .delete -in .buttons -side left -padx 50 pack .buttons -in .left -side top -pady 20 # Slider to set the delay between images set speed 5 scale .speed -from 1 -to 50 -variable speed -orient horizontal \ -length 250 -label "Speedup factor" -font $font pack .speed -in .left -side top -pady 20 canvas .image -width 720 -height 480 .image create image 0 0 -image img -anchor nw pack .image -in .left -side top # Scrollable listbox with the list of image files frame .listbox frame .images label .images.t -text "Image Files" -font $bigfont listbox .images.l -listvariable imagelist -yscrollcommand ".images.s set" \ -selectmode extended -font $monofont -width 40 -height 40 scrollbar .images.s -command ".images.l yview" pack .images.t -side top -fill x pack .images.s -side right -fill y pack .images.l -side left pack .images -in .listbox -side top pack .left .listbox -side left ############ # BINDINGS # ############ bind . q { .quit invoke } # Clicking on an image displays that one bind .images.l <ButtonRelease-1> \ { set filendx [.images.l curselection] showimage [lindex $imagelist [lindex $filendx 0]] }The image files listed on the command line are displayed in a listbox for single or multiple selection. You might call it like this
winnow /ext/motion/20090918/10*-front.jpgAs the name suggests, the program is intended to allow one to go through a set of images to select a set to keep or with which to make a movie.
Once you have captured an interesting sequence of motion images, it is fun to make it into a movie to entertain the people who appeared in it. For this purpose, the ffmpeg program is extremely convenient. All it takes is
ffmpeg -f image2 -i something%03d.jpg something.mpg
There are two minor issues with it though. The first is that it wants the individual frames specified with a printf format in files named like something000.jpg, something001.jpg, and so on, which is not what we have. The other issue is that frames taken at 2 per second run way too fast for standard mpeg viewers that may not have an option to slow them down. One solution is to duplicate the image enough times to get an mpeg movie that runs the way you want. Kind of gross, but it works. This little perl script takes care of both these issues. It is given the number of duplicates to make (4 or 5 is usually enough), a printf format, and a list of files. It copies each one the specified number of times into a file with the next name in the sequence. The printf format can contain a directory specification too, but be sure to make the directory first.
#!/usr/bin/perl # File name: mkframes # Copies each inputfile into a file named sequentially with the # outputformat (as in printf), duplicating each one dups times, # default 1 # Walter Eastes: March, 2009 # # Example with input files in tmp/*.jpg # mkframes -4 fr%03d.jpg tmp/*.jpg # ffmpeg -f image2 -i fr%03d.jpg fr.mpg $use = "Use: mkframes [-dups] outputformat inputfiles...\n"; # Get the number of times to dup each input file $#ARGV >= 0 or die $use; $nfill = 1; if ($ARGV[0] =~ /^-(\d+)$/) { $nfill = $1; shift @ARGV; } # Get the output file format specification $#ARGV >= 0 or die $use; $fmt = $ARGV[0]; # Copy each file the specified number of times $n = 0; for ($i = 1; $i <= $#ARGV; $i++) { for ($k = 0; $k < $nfill; $k++) { $out = sprintf($fmt,$n++); system "cp $ARGV[$i] $out"; } }
With the extra cards removed and the second hard drive reformatted and error checked, toad4 is now working fine. So I connected it to toad20, the machine running the surveillance cameras and connected to the router to the internet, with the serial cable that I used before to test remote login. Now I no longer have any need to login to toad20 from toad4, but it would be nice to use toad4 as a closely connected backup to toad20. For that, I need to enable login over the serial line on toad4. The line
s1:12345:respawn:/usr/local/sbin/mgetty -r -s 115200 ttyS0was still there in /etc/inittab.
Now minicom can be used on toad20 to login to toad4, but minicom is hopelessly inconvenient to transfer files, especially a bunch of them. Of course I could run pppd on both machines and use ssh the way I do over the internet, but this seems to be overdoing it for this purpose.
Therefore I investigated the old standby, kermit. It has the disadvantage that it is almost, but not quite free software, sort of. But I downloaded it from Columbia University, compiled, and installed it. The documentation is extensive partly because it explains everything in tedious detail and partly because it contains long rants against competing software and against any suggestion that kermit may possibly have any deficiency at all. The supplied makefile has an install target but they explain that you don't need it. All you have to do is copy the kermit executable, obscurely named wermit, over to your search path, and the man page too, equally obscurely called ckuker.nr. You don't want to use the install target anyway because it has more errors than the makefiles I write. In particular, it fails to make the directories into which it copies stuff, doesn't use the DESTDIR consistently, and and makefile is inconsistent with the makefile extract in the documentation, both of which are inconsistent with the documentation. Yuck! But the make linux target compiles it just fine.
To login to toad4 from toad20, one needs to start kermit and issue a few commands to set up the serial line, which can be done on the kermit command line, manually, or put into a file like this:
#!/usr/local/bin/kermit # File name: toad4 # Starts up kermit and sets up the serial line set modem type none set line /dev/ttyS0 set flow rts/cts set speed 115200 connect
After chmod +x this file and running it on toad20, one can login to toad4 as some user. To download files from toad4 to toad20, issue
kermit -s files...This is kermit's send command, the sense of which is reversed from what one might think, since we are running it on toad4. Therefore one can use wildcards in the files..., as they are interpreted on toad4.
To upload files from toad20 to toad4, just do
kermit -g files...Now the files... list is not interpreted on toad4, on which kermit was just executed, and thus one must put them in quotes if they contain wildcards.
The "quit" command to kermit exits.
This document is