An assortment of indigestible things

Using ZoneMinder with a cheap CCTV camera

One of the server rooms I look after has an old CCTV camera in the ceiling, and I decided to press it into service to enhance security for that room. I now get alerts from Nagios when motion is detected, so that I can go and see who’s been poking around. Here’s how I got there.

Connecting the camera with baluns

The balun at the camera end, with the power connection visible above it.

Most simple CCTV cameras have two connections: one for power (usually 12 volts), and another providing a composite video signal. I was lucky to find that my camera was still connected to a power supply, but its signal cable—which terminates in a BNC plug—was disconnected. I didn’t want to spend much money on this, so my plan was somehow to route the signal to an ESXi box so that I could process it in an Ubuntu VM. At first I considered running coaxial cable from the camera to the server rack, but then I found that I could use an existing CAT5e cable run with a balun at each end (don’t ask me to explain what a balun does or why they’re necessary—it’s a long time since I learned about that stuff).

A balun connected to the Hauppauge's composite input. Note the BNC-to-phono adapter.

A CCTV balun looks like a small black box with two screw terminals at one end, and a BNC plug coming out of the other. I’ve seen a few different ways of wiring them up, but I followed this video which involves using one of the four pairs in the CAT5e cable. I don’t know if it’s important to get the polarity right at both ends, but I used the striped wire to connect to the ‘positive’ or ‘tip’ terminal, and the solid-coloured wire for the other terminal. If you don’t already have power on your camera, that video also mentioned that you can use the other three pairs joined together to feed power along the same cable run.

Getting the signal into the Ubuntu VM

It was a bit of a struggle to pin down exactly what video capture hardware is supported by Ubuntu. I wanted a USB stick so that I didn’t have to shut down the server to install or fiddle with it. Unfortunately, the V4L project’s list of compatible hardware is very short and looks a bit stale. Eventually and after much Googling, I settled on the Hauppauge WinTV USB-Live2: it has a trailing cable with composite, audio and S-video inputs (although I’d only need the composite). ‘Hauppauge’ is a bit of a mouthful—I seem to remember reading somewhere that it’s pronounced ‘hop-hog’—so hereinafter it shall be known as the ‘capture card’.

Configuration of the ZoneMinder VM showing the USB controller and attached device

I built my VM on the ESXi box’s internal disks (it could never run anywhere else because the card wouldn’t be there), making sure that I’d added a virtual USB controller. Then I plugged in the capture card, and ‘virtually’ connected it to the VM with the vSphere client. Finally and with fingers firmly crossed, I booted the VM and checked the kernel logs to see which driver had been loaded for the card. Sadly, the device had not been picked up by any driver.

After a little more fun with search engines, I found that the drivers for my device aren’t actually shipped with Ubuntu—it should be able to use the cx231xx driver in the kernel, but it would seem that the driver isn’t really finished and doesn’t recognise my card. Thankfully, I found that it’s supported by the V4L-DVB driver set (even though my little dongle isn’t really a DVB card at all). If your card was recognised and the driver loaded successfully, you can skip this bit.

There are some detailed instructions on how to install the V4L-DVB driver set, but for lazy people like me who don’t want to move the mouse, you can do

git clone git://
cd media_build
sudo make install

(note that you might have to sudo aptitude install git if you don’t already have it)

Checking the kernel logs after I did this told me that things were looking much better. Lots and lots of messages, including this one:

cx231xx #0: New device Hauppauge Hauppauge Device @ 480 Mbps (2040:c200) with 5 interfaces

Now we’re getting somewhere.

Installing and configuring ZoneMinder

ZoneMinder is a free application for monitoring CCTV cameras. It’s even in the Ubuntu repositories, making things really easy. It does, however, have a somewhat bare-bones user interface, which is perfectly usable once things are running, but can make setup a little daunting. The thing to remember is that pretty much everything in ZoneMinder’s UI is clickable.

Since I only have one camera, I need—in ZoneMinder terminology—one monitor. I clicked Add new monitor and had a vague guess at some of the parameters, in particular the device name: the default was /dev/video but I only had a /dev/video0. I left the ‘function’ as Monitor like it says in the documentation. No joy yet though, with these messages appearing in the system log:

zmc_dvideo0[6949]: INF [Starting Capture]
zmc_dvideo0[6949]: FAT [Failed to open video device /dev/video0: Permission denied]

It turned out that the device was owned by group video with mode 660, so the ZoneMinder process couldn’t open it. I noted that the processes ran as the webserver user www-data, so I added that user to the right group in /etc/group, and restarted ZoneMinder. This time, still no video, but different messages:

zmc_dvideo0[7078]: FAT [Failed to set video format: Invalid argument]
zmdc[7055]: ERR ['zmc -d /dev/video0' exited abnormally, exit status 255]

ZoneMinder video parameters

It turns out that the video parameters under the ‘Source’ tab are absolutely critical in getting the capture card to work. In particular, you can’t just enter any old resolution and hope that it’ll work. I was using 640×480 as a reasonable guess, but this extract from v4l-info output told me what I needed to know:

fmt.pix.width : 720
fmt.pix.height : 480
fmt.pix.pixelformat : 0x56595559 [YUYV]
fmt.pix.field : INTERLACED
fmt.pix.bytesperline : 1440
fmt.pix.sizeimage : 691200
fmt.pix.colorspace : SMPTE170M
fmt.pix.priv : 0

As I’m in the UK, I chose PAL as the device format, and took the resolution and palette parameters from the output above. I guessed at channel 0 being the composite input (this card also has an S-video socket in case I feel like going back to 1997). I committed the changes, clicked on the monitor name on the ZoneMinder console, held my breath again, and… success!!! I had a slightly fuzzy, but perfectly serviceable, live video feed of the server room doors.

Getting motion detection to work

The next thing I wanted was to generate some sort of alarm when the system detected motion. In my opinion this is definitely ZoneMinder’s strong point, even if the interface is a little less than intuitive. At the simplest level, all you need to do is click on the ‘function’ of the monitor and change it from Monitor to Modect. With all settings at their defaults, I found this actually worked pretty well, and when I got back to my desk after tidying up the wiring I found a couple of video clips of me opening and closing the doors. ZoneMinder calls these events, and clicking on the numbers of events brings up a list. It will even show you single frames with areas highlighted where motion was detected, which is a nice touch.

The next day, however, I found that there were a few events that had been detected in my absence. Had someone been poking around? Well, no: it was just the people in the office outside the room turning the lights on and off. The meagre light coming through the glass in the doors was enough to light up the front of the room and set off the motion detection.

Defining bits of the image to ignore

Thankfully, ZoneMinder has a features called zones where you can single out specific areas of the image for special treatment. As the camera never moves, the windows will always be in the same place, so I decided to exclude them from the motion detection algorithm. As you can see from the picture, I marked both the windows and also a triangle at the bottom where light leaks through the doors and splays across the floor. These three zones are marked Inactive and appear white on the image. There are lots more options for zones, but for a simple setup like this, the ability to exclude troublesome parts of the image is very useful indeed.

Incidentally, in case (like me) you can’t figure out how to draw your zones on the image, once you click Add new zone you’ll have to look closely to see the four small green ‘points’ at the corners of the image. You drag these points, adding and deleting them as necessary with the controls below the image, until you have a box surrounding the relevant area. Don’t forget to set the ‘type’ to Inactive before you save your new zone.

(as it turned out this wasn’t quite enough for my setup, and I ended up sticking cardboard over the windows, but I still needed the inactive zones to stop it triggering on light leaking around the edges)

Sound an alarm

The last piece of the puzzle was to make sure that I get notified when an alarm occurs. Thanks to my existing Nagios setup, this was a piece of proverbial. I already monitor all my system logs for unpleasant messages, so I simply added alarm start as a string on which to generate an alarm. This triggers on these messages, which get spat into the system log whenever ZoneMinder detects motion:

zma_m4[3842]: INF [Doors: 224 - Opening new event 25, alarm start]

Now I get an email whenever such an event is generated, plus of course it pops up on my big Nagios status board.

It’s working, but this thing is running awfully hot…

As Columbo said,

Just one more thing.

I noticed that my new CCTV VM was running awfully hot: its CPU usage was pegged at 100%. It quickly occurred to me that I’d set the frames-per-second parameters to 25, not considering what effect this might have on system performance. ZoneMinder has two parameters for this: Maximum FPS and Alarm Maximum FPS. The former sets the frame rate when nothing’s happening, while the latter is used when motion is detected and recording begins. I kept this one at 25, but lowered the ‘normal’ maximum to 5. The effect on CPU usage was immediate:

ZoneMinder CPU usage maxed out at 25fps, then drops back down at 5fps

Eventually I lowered it further to 2 frames per second: I figured that it’s impossible to open, go through, and close the door in the 0.5 seconds between frames! At this frame rate, CPU usage is steady at 5-7%.

All done!

This wasn’t as much work as it sounds: once I had the parts, it took a few hours from start to finish, including troubleshooting. I hope this turns out to be useful, and if it does, I’d really appreciate a comment or two 🙂


Restoring datastore performance graphs after upgrading to vCenter 5.0U1


Creationism in British schools, the Big Bang, and a small Mouse


  1. Thanks, your experience helped me solve my 100% cpu problem… I used the max FPS settings.

    The strange thing is, if I use 10 and 10 FPS (for max and alarm max) everything is fine. If I use 5 and 10 respectively, the image gets distorted after a few seconds and alarms are triggered…


    • flup

      That is very strange. Does anything get written into the logs (zoneminder or syslog)?

      • Can’t look into it right now… other problems (image coming from one of the cameras freezing up after a few hours) keep me busy and also spam the logs!

        Maybe I’ll be back if I have time to look into it.


  2. Peter

    Your Experience helped me solve my no video problem…”Failed to set video format: Invalid argument”


  3. skarmoutsosv

    Adding www-data to video group in file /etc/group as you suggested, solved permission denied at my syslog.
    Careful selecting capture parameters as you suggested gave me a nice video feed.
    I should also mention that while trying to find the right parameters for my camera I used the command zmu -d /dev/video0 -q -v as described at

    Thank you for your post.

  4. sethuper

    I have followed step by step in this tutorial, everything is describe, how to install and configure ZoneMinder on Debian/Ubuntu.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Powered by WordPress & Theme by Anders Norén