This was done on a Supermicro embedded system with:
Code: Select all
Intel Core i3-7100U
Intel HD Graphics 620
FreeBSD 15.x
VNET jails
Dahua 8MP IP cameras
Code: Select all
FreeBSD host
-> Intel i915kms DRM driver
-> /dev/dri/renderD128
-> jailnvr VNET jail
-> ZoneMinder
-> nginx
-> PHP-FPM
-> fcgiwrap
-> MySQL 8.4
-> FFmpeg
-> Intel VAAPI iHD driver
-> Dahua RTSP cameras
1. Enable Intel iGPU on the FreeBSD host
The first step was getting the Intel iGPU visible on the FreeBSD host.
Installed the Intel DRM driver and Kaby Lake firmware:
Code: Select all
pkg install drm-kmod gpu-firmware-intel-kmod-kabylake
Code: Select all
kldload i915kms
Code: Select all
ls -l /dev/dri
Code: Select all
card0 -> ../drm/0
renderD128 -> ../drm/128
Code: Select all
/dev/dri/renderD128
Code: Select all
sysrc kld_list+="i915kms"
2. Create the jailnvr VNET jail
Created a thick jail:
Code: Select all
bsdinstall jail /usr/local/jails/jailnvr
Code: Select all
cp /etc/resolv.conf /usr/local/jails/jailnvr/etc/resolv.conf
Code: Select all
/etc/jail.conf
Code: Select all
jailnvr {
host.hostname = "jailnvr";
path = "/usr/local/jails/jailnvr";
vnet;
vnet.interface = "jailnvr0";
exec.clean;
exec.system_user = "root";
exec.jail_user = "root";
exec.prestart = "ifconfig epair13 create";
exec.prestart += "ifconfig epair13a descr jailnvr-host";
exec.prestart += "ifconfig epair13a up";
exec.prestart += "ifconfig bridge0 addm epair13a";
exec.prestart += "ifconfig epair13b name jailnvr0";
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.poststop = "ifconfig bridge0 deletem epair13a";
exec.poststop += "ifconfig epair13a destroy";
mount.devfs;
devfs_ruleset = 13;
persist;
allow.raw_sockets = 0;
allow.set_hostname = 0;
# Needed for ZoneMinder PHP streaming/semaphores.
allow.sysvipc = 1;
allow.mount = 0;
allow.mount.devfs = 0;
allow.mount.nullfs = 0;
allow.mount.procfs = 0;
allow.mount.tmpfs = 0;
allow.chflags = 0;
enforce_statfs = 2;
children.max = 0;
}
Code: Select all
allow.sysvipc = 1;
Code: Select all
Failed to get semaphore for key ...
It is likely that your php does not have the sysv semaphore extension either installed or enabled.
3. Configure devfs rules for DHCP and GPU passthrough
The jail needs bpf devices for DHCP and /dev/dri for GPU access.
Edited:
Code: Select all
/etc/devfs.rules
Code: Select all
[devfsrules_jaildb=10]
add include $devfsrules_jail
add path 'bpf*' unhide
[devfsrules_jailwww=11]
add include $devfsrules_jail
add path 'bpf*' unhide
[devfsrules_jailmon=12]
add include $devfsrules_jail
add path 'bpf*' unhide
[devfsrules_jailnvr=13]
add include $devfsrules_jail
add path 'bpf*' unhide
add path 'dri' unhide
add path 'dri/*' hide
add path 'dri/card0' unhide mode 0666
add path 'dri/renderD128' unhide mode 0666
add path 'drm' unhide
add path 'drm/*' hide
add path 'drm/0' unhide mode 0666
add path 'drm/128' unhide mode 0666
Code: Select all
service devfs restart
Code: Select all
jexec jailnvr ls -l /dev/dri
jexec jailnvr ls -l /dev/drm
Code: Select all
/dev/dri/card0 -> ../drm/0
/dev/dri/renderD128 -> ../drm/128
/dev/drm/0
/dev/drm/128
4. Configure jailnvr rc.conf
Edited:
Code: Select all
/usr/local/jails/jailnvr/etc/rc.conf
Code: Select all
hostname="jailnvr"
ifconfig_jailnvr0="SYNCDHCP"
sshd_enable="NO"
sendmail_enable="NONE"
syslogd_flags="-ss"
clear_tmp_enable="YES"
dumpdev="NO"
moused_nondefault_enable="NO"
Code: Select all
sysrc jail_list+=" jailnvr"
Code: Select all
service jail start jailnvr
Code: Select all
jls
jexec jailnvr hostname
jexec jailnvr ifconfig jailnvr0
Code: Select all
192.168.11.236
5. Install FFmpeg, VAAPI tools, and Intel media driver inside jailnvr
Entered the jail:
Code: Select all
jexec jailnvr /bin/sh
Code: Select all
pkg install ffmpeg libva-utils
Code: Select all
pkg install libva-intel-media-driver
Verified FFmpeg hardware acceleration support:
Code: Select all
ffmpeg -hide_banner -hwaccels
Code: Select all
vaapi
drm
Code: Select all
env LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128
6. Test Dahua RTSP video acceleration
Tested one Dahua 4K H.265 main stream:
Code: Select all
env LIBVA_DRIVER_NAME=iHD ffmpeg \
-hide_banner \
-rtsp_transport tcp \
-hwaccel vaapi \
-hwaccel_device /dev/dri/renderD128 \
-i 'rtsp://USER:[email protected]:554/cam/realmonitor?channel=1&subtype=0' \
-f null -
Then tested with VAAPI output surfaces:
Code: Select all
env LIBVA_DRIVER_NAME=iHD ffmpeg \
-hide_banner \
-rtsp_transport tcp \
-hwaccel vaapi \
-hwaccel_device /dev/dri/renderD128 \
-hwaccel_output_format vaapi \
-i 'rtsp://USER:[email protected]:554/cam/realmonitor?channel=1&subtype=0' \
-f null -
Software decode was around 90% CPU.
So the VAAPI path was definitely working.
7. Install ZoneMinder
Installed ZoneMinder first and let pkg pull in dependencies:
Code: Select all
pkg install zoneminder-php84
Code: Select all
pkg info | egrep -i 'zoneminder|nginx|php84|mysql84|fcgiwrap|ffmpeg|libva'
Installed the missing pieces:
Code: Select all
pkg install nginx fcgiwrap mysql84-server
Code: Select all
sysrc mysql_enable=YES
sysrc nginx_enable=YES
sysrc php_fpm_enable=YES
sysrc fcgiwrap_enable=YES
sysrc zoneminder_enable=YES
Code: Select all
service mysql-server start
Code: Select all
mysql -e "SELECT VERSION();"
8. Create ZoneMinder database
Located the schema:
Code: Select all
find /usr/local -name 'zm_create.sql'
Code: Select all
/usr/local/share/zoneminder/db/zm_create.sql
Code: Select all
mysql <<'EOF'
CREATE DATABASE zm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'zmuser'@'localhost' IDENTIFIED BY 'REDACTED_PASSWORD';
GRANT ALL PRIVILEGES ON zm.* TO 'zmuser'@'localhost';
FLUSH PRIVILEGES;
EOF
Code: Select all
mysql zm < /usr/local/share/zoneminder/db/zm_create.sql
Code: Select all
mysql zm -e "SHOW TABLES;"
Code: Select all
mysql -u zmuser -p zm -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='zm';"
9. Configure ZoneMinder database settings
Edited:
Code: Select all
/usr/local/etc/zm.conf
Code: Select all
ZM_DB_HOST=localhost
ZM_DB_NAME=zm
ZM_DB_USER=zmuser
ZM_DB_PASS=REDACTED_PASSWORD
ZM_WEB_USER=www
ZM_WEB_GROUP=www
Code: Select all
chown root:www /usr/local/etc/zm.conf
chmod 640 /usr/local/etc/zm.conf
10. Configure PHP
Copied production PHP config:
Code: Select all
cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
Code: Select all
/usr/local/etc/php.ini
Code: Select all
date.timezone = America/Chicago
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
cgi.fix_pathinfo = 0
Code: Select all
service php_fpm start
Code: Select all
127.0.0.1:9000
11. Configure fcgiwrap to run as www
Initially fcgiwrap ran as nobody, which caused issues with ZoneMinder streaming.
Set rc variables:
Code: Select all
sysrc fcgiwrap_user=www
sysrc fcgiwrap_group=www
sysrc fcgiwrap_socket_owner=www
sysrc fcgiwrap_socket_group=www
sysrc fcgiwrap_socket_mode=0775
Code: Select all
service fcgiwrap restart
Code: Select all
sockstat | grep fcgiwrap
ls -l /var/run/fcgiwrap/fcgiwrap.sock
Code: Select all
www fcgiwrap ...
/var/run/fcgiwrap/fcgiwrap.sock owned by www
12. Configure nginx
The final working nginx config used:
Code: Select all
user www;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
error_log /var/log/nginx-zm-error.log info;
access_log /var/log/nginx-zm-access.log;
server {
listen 80;
server_name jailnvr 192.168.11.236;
location = / {
return 302 /zm/;
}
location /zm/cache/ {
alias /var/cache/zoneminder/;
}
location /zm/ {
alias /usr/local/www/zoneminder/;
index index.php index.html;
try_files $uri $uri/ /zm/index.php?$query_string;
}
location ~ ^/zm/(.+\.php)$ {
alias /usr/local/www/zoneminder/$1;
if (!-f $request_filename) {
return 404;
}
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param SCRIPT_NAME /zm/$1;
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/cgi-bin/(.*)$ {
gzip off;
fastcgi_buffering off;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/www/zoneminder/cgi-bin/$1;
fastcgi_param SCRIPT_NAME /cgi-bin/$1;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_read_timeout 300;
fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
}
location ~ /\.(ht|git|svn) {
deny all;
}
}
}
ZoneMinder’s console thumbnails requested:
Code: Select all
/cgi-bin/nph-zms
Code: Select all
/zm/cgi-bin/nph-zms
Code: Select all
open() "/usr/local/etc/nginx/html/cgi-bin/nph-zms" failed
Tested nginx:
Code: Select all
nginx -t
Code: Select all
service nginx restart
13. Start ZoneMinder
Started ZoneMinder:
Code: Select all
service zoneminder start
Code: Select all
service zoneminder status
Code: Select all
ps aux | grep -i '[z]m'
Code: Select all
http://jailnvr/zm/
14. Camera setup
Added Dahua cameras using the main stream:
Code: Select all
rtsp://CAMERA_IP:554/cam/realmonitor?channel=1&subtype=0
For my use case I decided I did not want recording or motion detection, only viewing.
Final style of monitor settings:
Code: Select all
Function: Monitor
Enabled: 1
Capturing: Always
Analysing: None
Recording: None
Decoding: KeyFrames+Ondemand
Decoder: hevc_vaapi
DecoderHWAccelName: vaapi
DecoderHWAccelDevice: /dev/dri/renderD128
Method: TCP
Width: 3840
Height: 2160
Code: Select all
mysql zm -e 'SELECT Id,Name,`Function`,Enabled,Capturing,Analysing,Recording,Decoding,Width,Height FROM Monitors;'
Code: Select all
Analysing = None
Recording = None
Confirmed disk was not growing:
Code: Select all
du -sh /var/db/zoneminder
Code: Select all
13K /var/db/zoneminder
15. Problems encountered
Problem: /dev/dri missing on host
Fix:
Code: Select all
pkg install drm-kmod gpu-firmware-intel-kmod-kabylake
kldload i915kms
ls -l /dev/dri
Problem: VAAPI failed
Initial vainfo output showed:
Code: Select all
va_openDriver() returns -1
Code: Select all
pkg install libva-intel-media-driver
Code: Select all
env LIBVA_DRIVER_NAME=iHD vainfo --display drm --device /dev/dri/renderD128
Problem: thumbnail/live stream did not work
Symptoms:
Code: Select all
ZoneMinder UI worked but thumbnails did not.
Logs showed nph-zms / zms socket errors.
Code: Select all
1. Run fcgiwrap as www.
2. Enable allow.sysvipc=1 in jailnvr.
3. Add nginx location for /cgi-bin/nph-zms.
Problem: PHP semaphore errors
Log:
Code: Select all
Failed to get semaphore for key ...
It is likely that your php does not have the sysv semaphore extension either installed or enabled.
Code: Select all
php84-sysvsem was already installed.
The real issue was the jail itself:
allow.sysvipc = 1;
Problem: ZoneMinder requested /cgi-bin/nph-zms, not /zm/cgi-bin/nph-zms
nginx log:
Code: Select all
GET /cgi-bin/nph-zms?scale=1&mode=jpeg&frames=1&monitor=1...
Code: Select all
location ~ ^/cgi-bin/(.*)$ {
gzip off;
fastcgi_buffering off;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/www/zoneminder/cgi-bin/$1;
fastcgi_param SCRIPT_NAME /cgi-bin/$1;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_read_timeout 300;
fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
}
16. Performance notes
With 7 Dahua 8MP main streams configured for viewing only, top showed the system mostly idle:
Code: Select all
CPU roughly 90% idle
Each zmc process around 1-2% CPU
No swap usage
No meaningful storage growth
This is different from the raw FFmpeg VAAPI test, where decoded frames stayed on the GPU.
The best FFmpeg hardware acceleration test was:
Code: Select all
env LIBVA_DRIVER_NAME=iHD ffmpeg \
-hide_banner \
-rtsp_transport tcp \
-hwaccel vaapi \
-hwaccel_device /dev/dri/renderD128 \
-hwaccel_output_format vaapi \
-i 'rtsp://USER:PASSWORD@CAMERA_IP:554/cam/realmonitor?channel=1&subtype=0' \
-f null -
ZoneMinder live browser viewing is heavier because it must produce web-viewable output.
17. Current final state
The final working stack:
Code: Select all
FreeBSD host
i915kms loaded
/dev/dri/renderD128 available
jailnvr
VNET jail
DHCP address 192.168.11.236
/dev/dri/renderD128 exposed
ZoneMinder 1.38.1
nginx
PHP-FPM
fcgiwrap running as www
MySQL 8.4
FFmpeg 8.1
Intel iHD VAAPI driver
7 Dahua cameras configured
Final view-only monitor concept:
Code: Select all
Capturing: Always
Analysing: None
Recording: None
Decoding: KeyFrames+Ondemand