ZoneMinder on the ODROID-XU4



After the disappointing results of the Orange Pi Plus 2, I got serious and shelled out more dollars for something that appears to be in the top of its class. Let's find out!

PROS: CONS:
  • USB 3.0!!!!!
  • Relatively expensive
  • 2GB DDR3 RAM
  • GPIO pins are 1.8v
  • Dedicated Gigabit Ethernet!
  • Must purchase level shifter shield to get compatibility with 3.3 or 5 volt i/o
  • Four 2GHZ and four 1.4GHz cores
  • No audio input or output
  • Well documented, 83 page manual
  • Pluggable eMMC modules are expensive
As you probably can guess, most of what I've listed as a Con doesn't affect us for this project. They are, however, something to keep in mind if you ever plan to do anything else with this device.

List of Materials

Upgrade Legacy Drive to SSD

Install Armbian

First, head on over to the Armbian Download page. Download then write the appropriate OdroidXU4 image onto your sd card. You want the image without a desktop. Boot from the sd card and follow the first time use instructions.

Once you get to a command prompt, get all your updates:
sudo apt-get update
sudo apt-get upgrade

Initial Prep


We want to use ntpdate instead of ntp since ntpdate does not reside in memory:
sudo apt-get purge ntp
sudo apt-get install ntpdate

Now add the following to the root crontab:
0 0 * * * /usr/sbin/ntpdate-debian
Make sure the jessie-backports repo is enabled in the file /etc/apt/sources.list. In my case, this was already enabled:
deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
We will need the following packages:
sudo apt-get install git php5-common php5-gd php5-mysql php-pear mariadb-server fcgiwrap php5-fpm nginx gdebi-core dh-make-perl ssl-cert ffmpeg libvlc-dev libvlccore-dev vlc
The Debian Jessie repos are missing a required perl library, so let's build that now. Note that the dh-make-perl tool uses your git configuration to create an entry in the package changelog. It will abort if your name and email are not set. So let's set that!
git config --global user.email "your_email@email.com"
git config --global user.name "Your Name Here"
dh-make-perl --build --cpan  Sys::MemInfo
sudo gdebi libsys-meminfo-perl_0.99-1_armhf.deb
I was asked several questions during the dh-make-perl build process above and just accepted the defaults, with one exception. One step asked to put a bunch of junk into my .profile, which I said no to.

Build a ZoneMinder package from Master

We have created a build script for you so this is far easier than it might sound.

wget https://raw.githubusercontent.com/ZoneMinder/ZoneMinder/master/utils/do_debian_package.sh
chmod a+x do_debian_package.sh
./do_debian_package.sh `lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`  `date +%Y%m%d`01 local master
sudo gdebi zoneminder_1.30.0-jessie-2016121001_armhf.deb zoneminder-doc_1.30.0-jessie-2016121001_all.deb
Naturally, the name of the resulting package will vary.

Followup Configuration

Now that ZoneMinder is installed, we have several configuration steps to follow. Create the file /etc/php5/fpm/pool.d/zoneminder.conf with the following contents:


1
2
3
4
5
[www]
# These settings prioritize memory conservation over performance
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s

Remove the default nginx configuration file:
sudo rm /etc/nginx/sites-enabled/default
Create an unsigned certificate for the site:
sudo make-ssl-cert generate-default-snakeoil
Add our own configuration and save it as /etc/nginx/conf.d/zoneminder.conf:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
server {
    listen 80 default_server;
    listen [::]:80 default_server;
 
    # SSL configuration
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    include snippets/snakeoil.conf;
 
    root /usr/share/nginx/html;
    index index.php;
    autoindex off;
    server_tokens off;
    sendfile on;
    large_client_header_buffers 4 32k;
 
    # Auto-redirect HTTP requests to HTTPS
    if ($scheme != "https") {
        rewrite ^/?(zm)(.*)$        https://$host/$1$2 permanent;
    }
 
    location / {
        return 301 zm;
    }
 
    location /zm/cgi-bin {
        gzip off;
        alias /usr/lib/zoneminder/cgi-bin;
 
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        fastcgi_pass  unix:/var/run/fcgiwrap.socket;
    }
 
    location /zm {
        gzip off;
        alias                   /usr/share/zoneminder/www;
        index                   index.php;
 
        location ~ \.php$ {
            if (!-f $request_filename) { return 404; }
            expires             epoch;
            include             /etc/nginx/fastcgi_params;
            fastcgi_param       SCRIPT_FILENAME         $request_filename;
            fastcgi_index       index.php;
            fastcgi_pass        unix:/var/run/php5-fpm.sock;
        }
 
        location ~ \.(jpg|jpeg|gif|png|ico)$ {
            access_log          off;
            expires         33d;
        }
 
        location /zm/api/ {
            alias                   /usr/share/zoneminder/www/api;
            rewrite ^/zm/api(.+)$ /zm/api/index.php?p=$1 last;
        }
    }
}


Edit the file /etc/php5/fpm/php.ini and set your timezone:

1
2
3
4
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = America/New_York

Fix the zoneminder config file:
sudo chgrp www-data /etc/zm/zm.conf
Fix the API:
sudo ln -s /tmp /usr/share/zoneminder/www/api/app/tmp

Stop mariadb and move the sql files onto the hard disk:
sudo su
systemctl stop mysql
cd /mnt
mkdir sda1
mount /dev/sda1 /mnt/sda1
chown mysql:mysql /mnt/sda1
mv /var/lib/mysql/* /mnt/sda1/
umount /mnt/sda1
exit
Now create the file /etc/systemd/system/var-lib-mysql.mount and add the following to it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# systemd mount unit for mysql storage
 
[Unit]
Description=systemd mount unit for mysql storage
Before=mysql
 
[Mount]
What=/dev/disk/by-uuid/put_the_uuid_of_mmcblk1_here
Where=/var/lib/mysql
Type=ext4
Options=defaults,noatime,commit=120,data=writeback
 
[Install]
WantedBy=multi-user.target

Enable and start the mount unit:
sudo systemctl enable var-cache-zoneminder-events.mount
sudo systemctl start var-cache-zoneminder-events.mount
Now, let's move the ZoneMinder events folder to the hard drive as well. Create the file /etc/systemd/system/var-cache-zoneminder-events.mount and add the following to it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# systemd mount unit for ZoneMinder event storage
 
[Unit]
Description=systemd mount unit for ZoneMinder event storage
Before=zoneminder
 
[Mount]
What=/dev/disk/by-uuid/put_the_uuid_of_sda1_here
Where=/var/cache/zoneminder/events
Type=ext4
Options=defaults,noatime,commit=120,data=writeback
 
[Install]
WantedBy=multi-user.target

Edit /etc/fstab, comment out the existing swap file, and add the swap partition:


1
2
3
4
5
# Original swapfile shipped with Armbian
#/var/swap none swap sw 0 0
 
# Use a swap partition on the ssd instead
UUID=put_the_uuid_of_sda2_here    swap    swap    defaults    0 0

Disable the old swap file then enable the new swap partition:
sudo swapoff -a
sudo swapon -a
Almost done, enable and start your services:
sudo systemctl enable fcgiwrap
sudo systemctl enable php5-fpm
sudo systemctl enable nginx
sudo systemctl enable zoneminder
sudo systemctl restart php5-fpm
sudo systemctl restart nginx
Now from another machine, point your web browser to the ip or hostname of the Odroid. Your browser will warn you the site's certificate is unsigned.

From the ZoneMinder web console, click Options -> Paths and set PATH_ZMS to /zm/cgi-bin/nph-zms.

Congratulations. You're done. You can begin adding cameras now.


Performance

Here is a summary using sysbench:
  • cpu - 223.7535s
  • event storage drive - 974.36Kb/sec
  • database - 307.77 transactions per sec
Compared to the data from the Raspberry Pi 3, the cpu time is what I was hoping for, but the hard disk transfer rate is not. This tells me I need to switch to an SSD. I've got the new parts on order and will present the data here in the coming weeks.

UPDATE: After replacing the legacy hard drive with the same make and model SSD I used in the Raspberry Pi 3, sysbench reported the transfer rate increased to 7.906Mb/sec. This is certainly an improvement, but the Raspberry Pi 3, with its USB 2.0 bus, still got a sysbench rating more than twice that. This will likely remain a mystery as my attention is now on other priorities.

Cameras

Thanks to 2GB of RAM on the ODROID XU4, we don't have to worry quite as much about memory usage. Thanks to the faster cpu in the XU4, the values in the table below are much more reasonable for a 1080p camera, when compare to the same data from the Raspberry Pi 3.

cameraformatresolutionfpsring buffer (frames)mmap (MB)Max swap buffer (MB)Zma (%)Zmc (%)Zms (%)
Airlink777WmjpegVGA5354224201015
raspicamh264720p53512474tbdtbdtbd
USGLBH245S400h2641080P535277176504035

Using this data we can see that two 1080p cameras are quite doable, and three are possible after increasing the size of the /dev/shm ramdisk to make a little more headroom. I'm testing two 1080p cameras right now and they are working quite well.

Conclusion

If you intend to stream 1080p resolution cameras then the ODROID XU4 is the way to go.

One can think of the XU4 as the "gaming pc" of single board computers in the sense that you do have to pay a premium for the extra performance, and one could rightly argue that the total cost of ownership is on par with a low end desktop pc. However if small size, no noise, and low power consumption are desirable to you, then this would be an excellent solution.