Shell Scripting a Camera Server
The Swedish company Axis Communications AB introduced a new concept several years ago, when it launched its line of camera servers. Perhaps recognizing that its line of network cameras (one of which is reviewed in LJ, September 2000) could not fill all the niches of old-style analog surveillance cameras, it also offered its control and digitizing electronics in a separate, rugged, fanless enclosure. Available in versions with one or four analog video inputs and one pass-through output, camera servers now have offered companies a way to modernize their surveillance systems incrementally for most of a decade.
Naturally, the appliances run Linux these days, on rather specialized hardware. We came into contact with this server because a client had needs that could not be met by Axis' own cameras, but its Web-browser-based interface was well liked by both us and the client. So we were quoted some pro-level third-party hardware from a traditional surveillance camera supplier that was tested to work well with the camera server.
Like computer hardware, cameras and their optics are money sinks. Depending on desired sophistication, it is possible to spend any given sum you might have available.
For our purposes, we needed a remote-controllable pan-tilt-zoom (PTZ) camera for outdoor use, with a respectable amount of magnification. You can find PTZ cameras for a few hundred dollars for basic indoor versions, and a few thousand dollars for variants tolerant of outdoor climate, direct sunlight and minor vandalism.
We went with a pendant-mount enclosed clear dome system at the time—the type you might see at modern airports. For outdoor winter use in Norway, we needed a heated enclosure to avoid ice build-up. Here are the specifications:
Pan movement: 360 degrees continuous.
Vertical tilt: +2 to -92 degrees.
Image sensor: 1/4 inch CCD (3.2 x 2.4mm).
Zoom: 22X optical, focal length 4 to 88mm.
Sensitivity: 0.07 lux at 1/1.5 s shutter speed.
Shutter speed: 1/1.5 to 1/30,000 s.
Minimum F-stop: f/1.6
Operating environment: -40 to +50 degrees Celsius, sustained.
Now, part of the idea in using a PTZ camera for this project was periodically imaging several fixed points and uploading these images to a Webserver. Here we encountered a problem. The stock software could do periodic imaging and FTP just fine. However, we had no way to tell it to go to a PTZ position before snapping the image. The functionality was not essential for our first customer, so delivery went ahead while we researched the issue.
The software in this appliance is open to modification in a few ways. Source code is available for all open-source components of the firmware image—so the administration interface CGI is missing, but the rest is mostly available for inspection and modification. The source code for a specific firmware release is not downloadable, though; you must request it in writing, from the Axis IPR Department. They will send you the source code on a CD for a nominal fee.
We had more customers in the pipeline, so research went ahead. That work eventually yielded several APIs that could be mined for functionality.
Here are the available APIs:
The normal admin interface (Web browser).
GCC SDK for Linux/cris.
As with most embedded devices, there are some restrictions and inconveniences. First and foremost, the severely limited space. Less than 100kb-writable filesystem space is available for third-party modifications, out of the 4MB Flash storage. All standard software is on a read-only filesystem, not replaceable without creating a custom firmware image.
No SSH server or client was available at the time, so custom shell scripts had to be triggered by timer or run from a PHP script. There was a telnet server available for development use, however. And nowadays, Dropbear SSH has been ported to the architecture.
We wanted to make do without an additional server just for automation, if we could, so our effort went toward some internal shell scripting, triggered at a set interval by the task scheduler utask. Incidentally, this task scheduler has some extra capabilities compared to a vanilla cron—it can react to external events, like a digital input low-high transition, or loss of video signal on camera #2.
Exposing functionality always is an issue in embedded development—how much effort should be put into easy customization? In this case, much effort has gone into it—no two surveillance installations are alike.
The browser-based interface is easy to use, feature-full and meant for human consumption. It is often not especially well suited for machine automation.
The HTTP API is a simple request-response API, made for automation, offering most options available in the browser-based interface. It generally returns just a status code or the bare requested object, like a snapshot or video stream. This lends itself well to remote control, and several third-party vendors sell software that uses this API for controlling tens to hundreds of cameras from one or a few central nodes.
The PHP3 lite dialect is the easiest option for adding custom dynamic pages to the browser-based interface, and it can also be used for general-purpose internal scripting.
Shell scripting, via the provided sh-compatible shell and attendant utilities, is flexible and quick when some local intelligence is needed—for example, reacting to the push of a doorbell by snapping a picture, then opening a gate via a relay.
And if some advanced local smarts is needed, the GCC SDK for the platform is available.
As previously mentioned, we chose shell scripting for the issue at hand. In the relevant firmware revision, we had quite a few programs worth using:
BusyBox: including sash shell as /bin/sh.
mish: minix sh-compatible shell.
utask: task scheduler, not cron-compatible.
bufferd: image capture/buffering.
sftpclient: simple FTP client.
shttpclient: simple HTTP client.
smtpclient: simple mailer.
Of special note is the unobtrusive shttpclient. This allows us to use the HTTP API from internal shell scripts, which we needed for PTZ control. It also could be used in many other ways, of course—signalling events to another Webserver or video server, uploading pictures via HTTP, and other things not needing more than basic authentication. It is a simple HTTP client, after all, not wget or cURL.
Problems we encountered when pushing this into production use mostly turned out to have nothing to do with the scripting. We had some intermittent failures to upload images—these turned out to be caused by a climbing vine colonizing one of the antennas for the wireless bridge.
All good things end. I'll leave you now, but first, the final script we cobbled together is shown in Listing 1. Not particularly elegant, granted, but small and not that hard to write, thanks to the consideration of the original embedded developers. That's a fair lesson to take away from this, isn't it?
Listing 1. Final Script
#!/bin/mish PATH=/bin:/sbin:/usr/bin:/usr/sbin # stop button pressed? skip the rest. stopp=`ls /tmp/stopp` ; if [ "x$stopp" = "x" ]; then # clear working directory bufferd -reset -buffername BILDE ; # read configured positions, '~' signifies a unset # position grep -v '~' /etc/sysconfig/presetpos.conf \ >/tmp/presets ; # pos 1-10 reserved for non-public views num=11; while [ $num -lt 21 ]; do # another stop button check - break the loop stopp=`ls /tmp/stopp` ; if [ "x$stopp" = "x" ]; then grep Pos$num /tmp/presets >/tmp/canptz ; canptz=`cat /tmp/canptz` ; if [ "x$canptz" != "x" ]; then # go to position shttpclient "http://127.0.0.1/axis-cgi/\ com/ptz.cgi?camera=1&gotoserverpresetno=$num"; # wait for picture to settle sleep 6 ; # save picture to /tmp/BILDE/ bufferd -start -buffername BILDE -snapshot\ -pre 1 -format snapshot_pos$num.jpg -uri\ 'ftp://127.0.0.1/jpg/1/704x576.jpg' ; bufferd -stop -buffername BILDE ; tmpwait=20 ; while [ $tmpwait -gt 0 ]; do sleep 2; expr $tmpwait - 2 >/tmp/tmpwait ; tmpwait=`cat /tmp/tmpwait` ; if [ $tmpwait -eq 0 ]; then # timeout creating jpeg, kill process and # settle for potentially incomplete picture logger "timeout waiting for bufferd -stop,\ killing image_buffer and continuing." # ps is only available as a builtin # command in /bin/sh /bin/sh -c ps >/tmp/kverk ; grep image_buffer /tmp/kverk >/tmp/kverk2 ; imbuf_pid=`cut -b 0-5 /tmp/kverk2` ; rm /tmp/kverk; rm /tmp/kverk2; kill $imbuf_pid ; fi; if [ -f /tmp/BILDE/status ]; then # status file appeared - picture complete rm /tmp/BILDE/status ; tmpwait=0; fi; done; rm /tmp/tmpwait ; fi; fi; expr $num + 1 >/tmp/A ; num=`cat /tmp/A` ; rm /tmp/A; done; rm /tmp/presets ; rm /tmp/canptz ; # batch ftp all the pictures from this round sftpclient -L -m 10.0.0.1 -k /tmp/BILDE -c \ /var/www/pictures/c2 -u web -w P2SsW1Rd -t \ /var/www/pictures/c2/temp_c2.jpg ; fi;
Resources for this article: /article/8695.
Erik Inge Bolso is a UNIX consultant and epee fencer who lives in Molde, Norway, and has been running Linux since 1996. Another of his hobbies can be found via a Google search for “balrog genealogy”, and he can be reached at firstname.lastname@example.org.