Another home project of mine was to set up cameras around my house to keep annoying peeps away. But instead of buying an expensive NVR (Network Video Recorder) and camera’s, I decided to build my own NVR, so I could play around with some AI on image recognition and trigger alarms that worked into my home automation. I bought the following cameras (after checking some YouTube reviews) on a Chinese website;
3 x Hikvision DS-2CD1141-I (outside my house)
2 x Hikvision DS-2CD2443G0-IW (for the front door)
2 x Hikvision DS-2CD2142FWD-I (to monitor the kids)
For NVR I tried various software programs; Kerberos.Io, iSpy, Shinobi, ZoneMinder and Orchid VMS where the latter was almost won the battle. As I was looking to host this on a virtual machine, thus a Linux NVR, ZoneMinder got most of my points. Below the installation and setup.
I recently bought a new machine to run my VM’s on (with Proxmox as virtualization platform) but there is a lot to read on memory requirements. If you don’t size this correctly, you will end up with a lot of errors or captures with artifacts etc. Below my calculation that I got from the wiki on ZM (ZoneMinder). It starts with the breakdown of the data required to build the calculation
image-width and image-height are the width and height of images that your camera is configured for (in my case, 1920×1080).
image buffer size is the # of images ZM will keep in memory. This value is in the buffers tab for each monitor (in my case 50)
target color space is the color depth – 8bit, 24bit or 32bit. It’s again in the source tab of each monitor (in my case 32)
The 1.2 at the (almost) end is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate)
At the end I will double this number as ZM, by default, only maps half of the memory. (there are ways around that but I wanted a default Ubuntu template)
1920*1080 = 2,073,600 (bytes) --> Image-width and image-height
2,073,600 * 4 (4bytes=32 bits) = 8.294.400 (bytes) --> target color space
8.294.400 * 50 = 414.720.000 (bytes) --> Image Buffer Size
414.720.00 * 6 = 2.488.320.000 (bytes) --> number of cams
2.488.320.000 / 1024 = 2.430.000 kb --> bytes to kilobytes
2.430.000 / 1024 = 2.373,04 Mb --> kilobytes to Megabytes
2.373,04 / 1024 = 2,317 --> Megabytes to Gigabytes
2,317 * 1,2 = 2,780 Gb --> a bit of overhead
2,780 * 2 = 5,560 Gb --> Memory mapping item
As you can see on the screen-shot, my calculation got confirmed by the amount of memory allocated for my VM. One item that greatly improved in the latest version is the “buffer” setting in the camera. There you can also see the required amount of memory if you change that value. More on that in the camera settings section.
Now over to the fun part; the installation. I discovered a repository that is building the stable Ubuntu release of ZM (thanks Isaac Connor) so we started by adding that to my VM
Next I optimized Apache a bit to run smoother than the default installation. Depending on the version, some settings where already enabled. But it could never hurt to check 😉 Dont restart Apache afterwards as we do have another item to cover
albert@zoneminder:~$ sudo a2enmod rewrite
albert@zoneminder:~$ sudo a2enconf zoneminder
albert@zoneminder:~$ sudo a2enmod expires
albert@zoneminder:~$ sudo a2enmod headers
Enabling module headers.
To activate the new configuration, you need to run:
systemctl restart apache2
The last step is to change the timezone in the php.ini file. It depends on the Ubuntu release or timeframe you install this, at this time version 7.4 was active thus I use nano to modify the file that was located at [/etc/php/7.4/apache2/php.ini] Make sure you use [sudo nano] as you need root rights to modifile the file. You can use [CTRL + w] to search for “date”. Make sure you remove the [;] before the [date.timezone=] (you can check this site for your timezone) As my timezone is Europe/Amsterdam, my full string is [date.timezone = Europe/Amsterdam]
albert@zoneminder:~$ sudo nano /etc/php/7.4/apache2/php.ini
albert@zoneminder:~$ systemctl restart apache2
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to restart 'apache2.service'.
Authenticating as: albert
==== AUTHENTICATION COMPLETE ===
The last step is to [enable] and [start] zoneminder. Afterwards you can go to http://[yourhostname]/zm or go directly to the [getVersion.json] [http://zoneminder/zm/api/host/getVersion.json]
So now on to the Camera Configuration Part; adding the camera’s. As you will need RTSP streams, you will first need to discover the complete stream URL. This is different for every single brand of camera though the majority has this listed in the configuration of the actual camera itself. For Hikvision, the first page is already showing the live feed and if you “inspect” using your browser tools, you can see the complete URL (Seach for [target=] as you can see in the screen-shot below) There are also some other tools that can discover the URL for your (ONVIF Device Manager but they have to be ONVIF compatible). Below is my URL for a the Hikvision cams I have.
If you are unable to find your URL somehow, google is your friend! Just use the keywords “RTSP” in combination with your camera brand and you should be fine. You can use VLC to test the URL (use the “file” “open” “network” option and paste your URL there).
Now hit the “add” button in the Console view to add the camera. In the [General] section I just changed two things; Name and Function. For the name its obvious and I use the location and for the Function there are a few options to take into consideration that I have listed below
None – The monitor is currently disabled and nothing is recorded.
Monitor – The monitor is only available for live streaming.
Modect – or MOtion DEteCTtion. All captured images will be analyzed and events generated with recorded video where motion is detected.
Record – The monitor will be continuously recorded.
Mocord – The monitor will be continuously recorded, with any motion highlighted.
Nodect – or No DEteCTtion. This is a special mode designed to be used with external triggers. In Nodect no motion detection takes place but events are recorded if external triggers require it.
I use [Modect] on my externally placed cams and [Monitor] for the indoor part. But you can change it to suit your needs. The Source Type is FFmpeg by default as the majority of the cameras use that. The FPS part is to be left alone unless you know what you are doing, in short; lowering the load of your system if high FPS cameras are attached to it.
In the [Source] tab you have to add the RTSP Stream URL in the [Source Path] and you will have to match the Resolution and color space to your camera settings.
Storage we will change later on (see the next section). For now the last part of this puzzle is the [Buffer] where we have to set the “image Buffer Size” in Frames. We calculate 50 frames in our calculation and this has a direct impact on the (shared) memory consumption of the VM. 50 is also the default and enough for most setups. In the newer versions of ZM (I am running 1.35.14) you can see an estimation on how much memory is actually required when setting this value. For 50 and 1080p with 32bit colors, its 395.5MB.
When you hit [safe] you are being redirected to the Console and you can see a thumbnail and the camera in “green” if everything was added correctly. The last step in this section is to create a [zone] and set a field of recording. In the [Console] view, you can set a “zone” all the way to the right. If you hit the “1” (default zone) you will see a capture of the camera and you can set a zone. It works by clicking and dragging the red piece. Make sure you select a “preset” (I use “Best, Medium Sensitivity”) and hit the save right after.
Adding NFS Storage
As I am using a VM on Proxmox, the storage for this VM is just 32Gb and I want to store quite a few more events from all my camera’s on my FreeNAS to go back in time. So I created a NFS export on my NAS with a Quota of 200G to start with. Below my steps that I did on my FreeNAS installation;
Add “DataSet” on my existing Pool
Used the “Advanced” to set the “Quota” to 200G
Edit the “Permissions” of the new DataSet and enabled “write” (that was disabled by default). I also set the root user and group to my own user/group that I use for everything.
Created a NFS Export using the “Sharing” menu
Added a share that points to the new DataSet and, again, mapped the root user and group to match my own user/group
I first did a quick test to see if everything was working. So I installed [nfs-comon] to see if the export existed. After that I created a folder and mounted the new export as you can see in the terminal output below;
albert@zoneminder:/mnt$ sudo apt-get install nfs-common
Reading package lists… Done
Building dependency tree
Reading state information… Done
nfs-common is already the newest version (1:1.3.4-2.5ubuntu3.3).
0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded.
albert@zoneminder:~$ sudo showmount -e freenas
Export list for freenas:
albert@zoneminder:~$ sudo mkdir /mnt/freenas
albert@zoneminder:~$ sudo mount freenas:/mnt/RaidZ/ZoneMinder /mnt/freenas/
From the Zoneminder console I went to [Options] and selected [Storage] to “ADD NEW STORAGE” and just pointed to the new directory where we mounted the FreeNAS export as you can see in the screenshot.
After I hit “Save” I could see the new storage and also saw the [Quota] right away. The last step was to assign this storage space to the camera’s itself. I went back to the “Console” and selected the source of the camera, in the “Storage” section I selected the newly created Storage [FreeNAS] and hit Save. Please be aware that the [mount] is temporary and will disappear after a reboot. Thus, we need to add the mount point to the [etc/fstab] file to be consistent after a reboot of the ZM Virtual Machine. Just use your favorite editor (mine is vi) and add your mount point. Below is my line
Just make sure you stop ZoneMinder [sudo service zoneminder stop] before you [sudo umount /mnt/freenas] and add the line to the [etc/fstab]. Afterwards you can run [sudo mount -a] and start zoneminder with [sudo service zoneminder start].
Upgrading ZoneMinder is an easy step if you use the repository that we added in the beginning. I check every other weekend if the VM has updates ready to be installed. You can even check to what version you can upgrade. Below my Console output where I started with [update] to search for upgrades and afterwards an [–upgradable] to see what is going to be upgraded. As you can see I will upgrade ZoneMinder from 1.34.22 to 1.35.14