Wednesday, August 1, 2012

ZoneMinder vs. motion

In the homebrew security project, we're now to trying to tie video cameras into the system with motion detection. After running ZoneMinder for a week, it has only been capturing at 3 fps. That is utterly awful. I needed something better. I decided to try the older Linux package, "motion". Unfortunately, CentOS doesn't include a pre-packaged RPM, so I have to build it. I grabbed the source from http://sourceforge.net/projects/motion/files/, extracted it (tar -xzf motion-3.2.12.tar.gz), and then ran a simple configure (./configure) just to see if it picked up the ffmpeg libraries any easier than ZoneMinder.

It found everything. I thought, "this is going to be a cinche!" - but after make :
ffmpeg.c: In function ‘fmpeg_open’
ffmpeg.c:365: warning: ‘v_new_stream’is deprecated (declared at /usr/include/ffmpeg/libavformat/avformat.h:1730)
ffmpeg.c:380: error: ‘ODEC_TYPE_VIDEO’undeclared (first use in this function)
ffmpeg.c:380: error: (Each undeclared identifier is reported only once
ffmpeg.c:380: error: for each function it appears in.)
ffmpeg.c:420: warning: ‘v_set_parameters’is deprecated (declared at /usr/include/ffmpeg/libavformat/avformat.h:1766)
ffmpeg.c:446: warning: ‘vcodec_open’is deprecated (declared at /usr/include/ffmpeg/libavcodec/avcodec.h:4155)
ffmpeg.c:501: warning: ‘rl_fopen’is deprecated (declared at /usr/include/ffmpeg/libavformat/avio.h:323)
ffmpeg.c:511: warning: ‘rl_fopen’is deprecated (declared at /usr/include/ffmpeg/libavformat/avio.h:323)
ffmpeg.c:532: warning: ‘v_write_header’is deprecated (declared at /usr/include/ffmpeg/libavformat/avformat.h:1802)
ffmpeg.c: In function ‘fmpeg_close’
ffmpeg.c:599: warning: ‘rl_fclose’is deprecated (declared at /usr/include/ffmpeg/libavformat/avio.h:324)
ffmpeg.c: In function ‘fmpeg_put_frame’
ffmpeg.c:649: error: ‘KT_FLAG_KEY’undeclared (first use in this function)
make: *** [ffmpeg.o] Error 1
Bugger. However, from the dung that I dealt with in ZoneMinder, I specifically remembered that things had to be patched. I didn't see one readily available, so I took the 20 minutes necessary to build one. It's available at http://svn.silverhawk.net/files/motion-3.2.12-ffmpeg-0.10.2-3.patch. That means, here's the process for installing motion :

  1. Download the source for motion from http://sourceforge.net/projects/motion/files/.
  2. Download the patch :
    wget http://www.silverhawk.net/files/motion-3.2.12-ffmpeg-0.10.2-3.patch
  3. Extract the source :
    tar -xzf motion-3.2.12.tar.tg
  4. CD into the source :
    cd motion-3.2.12
  5. Patch :
    patch <../motion-3.2.12-ffmpeg-0.10.2-3.patch
  6. Configure the compile :
    ./configure
  7. Compile :
    make
  8. Install :
    make install
  9. Get a basic configuration :
    cp /usr/local/etc/motion-dist.conf /usr/local/etc/motion.conf

Next up, figure out your camera URL's, and configure the app using the new configuration file /usr/local/etc/motion.conf . Note that this is for a SINGLE camera - for multiples, you will need a separate configuration file for each one! In the configuration file, there are a few settings you should be looking at :
  • process_id_file - if using multiple cameras and multiple files, change this to something with the camera name in it. Otherwise, ensure that this points to an appropriate location on your filesystem. In my case, it defaulted to /var/run/motion/, I reset it to /var/run/ and motion-cameraname (e.g. "/var/run/motion-northside.pid")
  • width - this should be the width of the camera image. For the TrendNet TV-IP322P, this is 1280 (SXGA setting).
  • height - this should be the height. For the TrendNet TV-IP322P, this is 1024.
  • framerate - this should be the rate from your camera. For the TrendNet TV-IP322P, this was 15 (at the SXGA setting).
  • netcam_url - This is the URL to grab an image from your camera. Again, for the TV-IP322P, I used http://ipaddressofcamera/cgi/jpg/image.cgi - I couldn't use the rtsp:// protocol because the motion package doesn't support H264 or MPEG4 (bummer).
  • netcam_userpass - set as "username:password".
  • pre_capture - This is the number of frames before a capture event happens that you want recorded, I set it to 30, or two seconds at our frame rate.
  • post_capture - This is the number of frames after a capture event that you want recorded. I set it to 30 (or two seconds at our frame rate).
  • max_mpeg_time - This is the number of seconds that a generated video file can have (it will start a new one if needed). For this, I set it to 240, or four minutes.
  • output_normal - This tells motion to write individual jpegs or images out. I set this to "off", since I wanted videos.
  • text_right - Since I want to use multiple cameras, I slightly altered this to include the camera name.
  • target_dir - This is where image stills and resulting videos are going to be stored. I tossed this into an apache-enabled directory so that I could easily grab the files from anywhere.
  • webcam_port - I set this to 0 to disable the webcam serving - I didn't want the webcam because I can grab the videos via a Nagios alert link (no need to browse).
  • control_port - I set this to 0 to disable control - the TrendNet TV-IP322P cameras don't have pan and tilt, so it's pointless to try and control it.
  • on_event_start - This is a command line to run when it starts to capture. I used this to run a command that sent a passive check to Nagios stating motion had been detected.
  • on_event_end - This is yet another command line event, but happens when capturing stops. I used this to send the passive check to Nagios to change the status back to "no motion detected, all is OK".
  • on_movie_end - I use this hook to try and maintain the directory. Basically, once a capture stops, it fires a hook to clean out that target directory of anything more than a week old.
  • on_camera_lost - I use this one to fire off another Nagios passive service check indicating we didn't get an image from the camera. This can be used to notify if we lose the camera.
One thing to be aware of is that motion can even be detected when leaves move, or even shadows on concrete change positions. Because of this, another thing to do is to introduce a "mask" for the capture area. To do this :
  1. Grab a frame from the camera (also called an image still - you can get one with the TrendNet TV-IP322P by using the http://ipaddressofcamera/cgi/jpg/image.cgi and saving that image).
  2. Open the saved image in an image editing program (e.g. Gimp), and use the selection tools to surround all areas you wish to be sensitive to movement.
  3. Fill the entire selection with white.
  4. Invert the selection.
  5. Fill the newly selected area with black.
  6. Save the new black-and-white image in a 'pgm' format (e.g. "camera-north.pgm").
  7. Copy the image to the server running motion.
  8. Edit the configuration file for this camera, and find the mask_file option, uncomment and set this to the full path of the image you just created.

Make sure you set motion to start on boot (in case of a power outage), and also make sure you start it up and get the video files, and congrats, video notifications of motion detection!