Merge branch 'master' of github.com:ZoneMinder/ZoneMinder

This commit is contained in:
Isaac Connor 2018-03-03 09:25:34 -08:00
commit 34dec24ebd
65 changed files with 2514 additions and 920 deletions

View File

@ -5,6 +5,8 @@ You should only file an issue if you found a bug. Feature and enhancement reque
**Do not post feature or enhancement requests, general discussions or support questions here.**
Docker related issues should be posted here: https://github.com/ZoneMinder/zmdockerfiles
Make sure you are running the latest version of ZoneMinder before reporting an issue.
**ZoneMinder Version (`zmaudit.pl -v`):**

View File

@ -23,17 +23,27 @@ addons:
- curl
- sshfs
- sed
- binfmt-support
- qemu
- qemu-user-static
install:
- update-binfmts --enable qemu-arm
env:
global:
- SMPFLAGS=-j4
matrix:
- OS=el DIST=6
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
- OS=el DIST=7
- OS=fedora DIST=25
- OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack
- OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack
- OS=ubuntu DIST=trusty
- OS=ubuntu DIST=xenial
- OS=ubuntu DIST=trusty ARCH=i386
- OS=ubuntu DIST=xenial ARCH=i386
- OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
compiler:
- gcc
services:

View File

@ -1,111 +0,0 @@
# ZoneMinder, you need the GIT repository code and submodules (git submodule update --init --recursive)
FROM ubuntu:xenial
MAINTAINER Markos Vakondios <mvakondios@gmail.com>
# Resynchronize the package index files
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
apache2 \
build-essential \
cmake \
dh-autoreconf \
dpatch \
libapache2-mod-php \
libarchive-zip-perl \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavresample-dev \
libav-tools \
libavutil-dev \
libbz2-dev \
libcurl4-openssl-dev \
libdate-manip-perl \
libdbd-mysql-perl \
libdbi-perl \
libdevice-serialport-perl \
libjpeg-turbo8 \
libjpeg-turbo8-dev \
libmime-lite-perl \
libmime-perl \
libmp4v2-dev \
libmysqlclient-dev \
libnetpbm10-dev \
libpcre3 \
libpcre3-dev \
libpolkit-gobject-1-dev \
libpostproc-dev \
libssl-dev \
libswscale-dev \
libsys-mmap-perl \
libtheora-dev \
libtool \
libv4l-dev \
libvlc5 \
libvlccore8 \
libvlccore-dev \
libvlc-dev \
libvorbis-dev \
libvpx-dev \
libwww-perl \
libx264-dev \
mysql-client \
mysql-server \
php \
php-cli \
php-mysql \
vlc-data \
yasm \
zip \
&& rm -rf /var/lib/apt/lists/*
# Copy local code into our container
ADD cmake /ZoneMinder/cmake/
ADD db /ZoneMinder/db/
ADD misc /ZoneMinder/misc/
ADD onvif /ZoneMinder/onvif/
ADD scripts /ZoneMinder/scripts/
ADD src /ZoneMinder/src/
ADD umutils /ZoneMinder/umutils/
ADD web /ZoneMinder/web/
ADD cmakecacheimport.sh CMakeLists.txt version zm.conf.in zmconfgen.pl.in zmlinkcontent.sh.in zoneminder-config.cmake /ZoneMinder/
ADD conf.d /ZoneMinder/conf.d
# Change into the ZoneMinder directory
WORKDIR /ZoneMinder
# Setup the ZoneMinder build environment
#RUN aclocal && autoheader && automake --force-missing --add-missing && autoconf
# Configure ZoneMinder
#RUN ./configure --with-libarch=lib/$DEB_HOST_GNU_TYPE --disable-debug --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --with-mysql=/usr --with-webdir=/var/www/zm --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-mmap=yes --enable-onvif ZM_SSL_LIB=openssl ZM_DB_USER=zm ZM_DB_PASS=zm
RUN cmake .
# Build & install ZoneMinder
RUN make && make install
# ensure writable folders
RUN ./zmlinkcontent.sh
# Adding the start script
ADD utils/docker/start.sh /tmp/start.sh
# Settings rights for /usr/local/share/zoneminder/
RUN chown -R www-data:www-data /usr/local/share/zoneminder/
# Adding apache virtual hosts file
RUN cp misc/apache.conf /etc/apache2/sites-available/000-default.conf
# Expose http port
EXPOSE 80
VOLUME /var/lib/zoneminder/images /var/lib/zoneminder/events /var/lib/mysql /var/log/zm
# To speed up configuration testing, we put it here
ADD utils/docker /ZoneMinder/utils/docker/
CMD /ZoneMinder/utils/docker/setup.sh && /ZoneMinder/utils/docker/start.sh >/var/log/start.log 2>&1 & /bin/bash
# Run example docker run -it -p 1080:80 -e PHP_TIMEZONE='Europe/Paris' -v /disk/zoneminder/events:/var/lib/zoneminder/events -v /disk/zoneminder/images:/var/lib/zoneminder/images -v /disk/zoneminder/mysql:/var/lib/mysql -v /disk/zoneminder/logs:/var/log/zm --name zoneminder zoneminder/zoneminder

View File

@ -13,6 +13,10 @@ ZoneMinder is an integrated set of applications which provide a complete surveil
Before creating an issue in our github forum, please read our posting rules:
https://github.com/ZoneMinder/ZoneMinder/wiki/Github-Posting-Rules
## Our Dockerfile has moved
Please file issues against the ZoneMinder Dockerfile here:
https://github.com/ZoneMinder/zmdockerfiles
## Installation Methods
### Building from Source is Discouraged
@ -31,6 +35,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
- Fedora via [RPM Fusion](http://rpmfusion.org)
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
- Mageia from their default repository
- Arch via the [AUR](https://aur.archlinux.org/packages/zoneminder/)
- Gentoo from their [default repository](https://packages.gentoo.org/packages/www-misc/zoneminder)
If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own.

View File

@ -291,7 +291,7 @@ CREATE TABLE `MonitorPresets` (
`Name` varchar(64) NOT NULL default '',
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
`Device` tinytext,
`Channel` tinytext,
`Channel` tinyint(3) unsigned default NULL,
`Format` int(10) unsigned default NULL,
`Protocol` varchar(16) default NULL,
`Method` varchar(16) default NULL,
@ -597,77 +597,81 @@ INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',0,0,1,0,0,
INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Libvlc','IPCC7210W', 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
--
-- Add some monitor preset values
--
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp','rtpUni',NULL,NULL,NULL,'<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp','rtpMulti',NULL,NULL,NULL,'<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp','rtpRtsp',NULL,NULL,NULL,'<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http','simple',NULL,NULL,NULL,'<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@<host/address>:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>','<?>',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>','<?>',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>','<?>',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>','<?>',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>','<?>',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>','<?>',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>','<?>',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>','<?>',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>','<?>',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>','<?>',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>','<?>',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>','<?>',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>','<?>',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>','<?>',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>','<?>',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>','<?>',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','<ip-address>',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=<monitor-id>&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,704,576,0,NULL,1,'10','<admin_pwd>','<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,640,480,0,NULL,1,'11','<admin_pwd>','<ip-address>',100,100);
@ -675,8 +679,8 @@ INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',N
INSERT INTO MonitorPresets VALUES (NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<ip-address>','80','/videostream.cgi?user=<username>&pwd=<password>&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','','<username>:<pwd>@<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<username>:<password>@<ip-address>','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','','<username>:<pwd>@<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video<?>','0',255,'','rtpMulti','','80','rtsp://<ip-address>:554/11','',1920,1080,0,0.00,1,'16','-speed=64','<ip-address>:<port>',100,33);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp','rtpRtsp',255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp','rtpRtsp',255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100);
--
-- Add some zone preset values

View File

@ -83,6 +83,10 @@ BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel
# Required for mp4 container support
BuildRequires: libmp4v2-devel
BuildRequires: x264-devel
%{?with_nginx:Requires: nginx}
%{?with_nginx:Requires: fcgiwrap}
%{?with_nginx:Requires: php-fpm}
@ -110,6 +114,7 @@ Requires: perl(Net::SMTP)
Requires: perl(Net::FTP)
Requires: perl(LWP::Protocol::https)
Requires: ca-certificates
Requires: zip
%{?with_init_systemd:Requires(post): systemd}
%{?with_init_systemd:Requires(post): systemd-sysv}

View File

@ -6,7 +6,6 @@ Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev
,libboost1.55-dev
,libbz2-dev
,libgcrypt-dev
,libcurl4-gnutls-dev

View File

@ -38,6 +38,10 @@ if [ "$1" = "configure" ]; then
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
else
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi

View File

@ -73,6 +73,9 @@ if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then
# Run the ZoneMinder update tool
zmupdate.pl --nointeractive
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
fi
#DEBHELPER#

View File

@ -6,7 +6,6 @@ Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
,libx264-dev, libmp4v2-dev
,libboost-dev
,libavdevice-dev (>= 6:10~)
,libavcodec-dev (>= 6:10~)
,libavformat-dev (>= 6:10~)
@ -17,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libcurl4-gnutls-dev
,libgnutls-openssl-dev
,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev
,libmysqlclient-dev
,default-libmysqlclient-dev | libmysqlclient-dev
,libpcre3-dev
,libpolkit-gobject-1-dev
,libv4l-dev (>= 0.8.3) [!hurd-any]

View File

@ -47,6 +47,10 @@ if [ "$1" = "configure" ]; then
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
else
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi

View File

@ -71,22 +71,22 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
::
1280*960 = 1,228,800 (bits)
1,228,800 * 24 = 2,359,296,000 (bits)
2,359,296,000 * 50 = 5,898,240,000 (bits)
5,898,240,000 * 4 = 7,077,888,000 (bits)
7,077,888,000 / 8 = 884,736,000 (bytes)
884,736,000 / 1000 = 884,736 (Kilobytes)
884,736 / 1000 = 864 (Megabytes)
864 / 1000 = 0.9 (Gigabyte)
1280*960 = 1,228,800 (bytes)
1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes)
3,686,400 * 50 = 184,320,000 (bytes)
184,320,000 * 4 = 737,280,000 (bytes)
737,280,000 / 1024 = 720,000 (Kilobytes)
720,000 / 1024 = 703.125 (Megabytes)
703.125 / 1024 = 0.686 (Gigabytes)
Around 900MB of memory.
Around 700MB of memory.
So if you have 2GB of memory, you should be all set. Right? **Not, really**:
* This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in.
* You also need to account for other processes not related to ZM running in your box
* You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory
* If you are using H264 encoding, that buffers a lot of frames in memory as well.
So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well)
@ -128,15 +128,14 @@ So, for example:
::
384x288 capture resolution, that makes: 110 592 pixels
in 24 bit color that's x24 = 2 654 208 bits per frame
by 80 frames ring buffer x80 = 212 336 640 bits per camera
by 4 cameras x4 = 849 346 560 bits.
Plus 10% overhead = 934 281 216 bits
That's 116 785 152 bytes, and
= 114 048 kB, respectively 111.38 MB.
If my shared memory is set to 134 217 728, which is exactly 128MB,
in 24 bit color that's x 3 = 331,776 bytes per frame
by 80 frames ring buffer x80 = 26,542,080 bytes per camera
by 4 cameras x4 = 106,168,320 bytes.
Plus 10% overhead = 116,785,152 bytes
Thats 114,048 kB, respectively 111.38 MB.
If my shared memory is set to 134,217,728, which is exactly 128MB,
that means I shouldn't have any problem.
(Note that 1 byte = 8 bits and 1kbyte = 1024bytes, 1MB = 1024 kB)
(Note that 1kbyte = 1024bytes, 1MB = 1024 kB)
If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here <http://www.redhat.com/docs/manuals/database/RHDB-2.1-Manual/admin_user/kernel-resources.html>`__

View File

@ -1,4 +1,4 @@
All Distros - A Simpler Way to Build ZoneMinder
All Distros - A Docker Way to Build ZoneMinder
===============================================
.. contents::
@ -27,6 +27,8 @@ Procedure
- If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here.
- If the desired distro architecture is arm, refer to `Appendix A - Enable Qemu On the Host`_ to enable qemu emulation on your amd64 host machine.
**Step 2:** Install Docker.
You need to have a working installation of Docker so head over to the `Docker site <https://docs.docker.com/engine/installation/>`_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following:
@ -44,7 +46,7 @@ Clone the ZoneMinder project if you have not done so already.
::
git clone ZoneMinder
git clone https://github.com/ZoneMinder/ZoneMinder
cd ZoneMinder
Alternatively, if you have already cloned the repo and wish to update it, do the following.
@ -99,7 +101,27 @@ For advanced users who really want to go out into uncharted waters, it is theore
Building arm packages in this manner has not been tested by us, however.
Appendix A - Enable Qemu On the Host
------------------------------------
If you intend to build ZoneMinder packages for arm on an amd64 host, then Debian users can following these steps to enable transparent Qemu emulation:
::
sudo apt-get install binfmt-support qemu qemu-user-static
Verify arm emulation is enabled by issuing:
::
sudo update-binfmts --enable qemu-arm
You may get a message stating emulation for this processor is already enabled.
More testing needs to be done for Redhat distros but it appears Fedora users can just run:
::
sudo systemctl start systemd-binfmt
TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome.

View File

@ -15,9 +15,11 @@ achieve the same result by running:
::
tasksel install lamp-server
sudo tasksel install lamp-server
During installation it will ask you to set up a master/root password for the MySQL.
Installing LAMP is not ZoneMinder specific so you will find plenty of resources to
guide you with a quick search.
**Step 1:** Either run commands in this install using sudo or use the below to become root
::
@ -28,9 +30,14 @@ During installation it will ask you to set up a master/root password for the MyS
.. topic :: Latest Release
ZoneMinder 1.29.0 is now part of the current standard Ubuntu repository. But
if you wish to install the later releases of ZoneMinder you will need
to add the iconnor/zoneminder PPA.
ZoneMinder is now part of the current standard Ubuntu repository, but
sometimes the official repository can lag behind. To find out check our
`releases page <https://github.com/ZoneMinder/zoneminder/releases>`_ for
the latest release.
Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately
following a new release of ZoneMinder. To use this repository instead of the
official Ubuntu repository, enter the following from the command line:
::

View File

@ -8,7 +8,7 @@
ServerAdmin webmaster@localhost
DocumentRoot "@WEB_PREFIX@"
Alias /zm/ "@WEB_PREFIX@/"
Alias /zm "@WEB_PREFIX@"
<Directory "@WEB_PREFIX@">
Options -Indexes +FollowSymLinks
AllowOverride All

View File

@ -0,0 +1,390 @@
# Modified on 2017-11-17 by Ognyan Bankov
# ==========================================================================
#
# ZoneMinder Floureon 1080p IP Control Protocol Module, $Date: 2017-11-17 09:20:00 +0000 $, $Revision: 0001 $
# Copyright (C) 2017 Ognyan Bankov
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
#
# This module contains the implementation of the Floureon 1080p 18x (Model: BT-HD54F) IP camera control
# protocol. It should work with other Floureon cameras too.
#
#
package MyAgent;
use base 'LWP::UserAgent';
package ZoneMinder::Control::Floureon;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our $VERSION = $ZoneMinder::Base::VERSION;
# ==========================================================================
#
# Floureon IP Control Protocol
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
our $stop_command;
sub open
{
my $self = shift;
$self->loadMonitor();
$self->{ua} = MyAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/" );
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd"."&".$self->{Monitor}->{ControlDevice});
print ("Sending $req\n");
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error REALLY check failed:'".$res->status_line()."'" );
Error ("Cmd:".$req);
}
return( $result );
}
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
my $cmd = "reboot.cgi?";
$self->sendCmd( $cmd );
}
# PP - in all move operations, added auto stop after timeout
#Up Arrow
sub moveConUp
{
my $self = shift;
my $params = shift;
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Up" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=1&PanSpeed=6&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Down Arrow
sub moveConDown
{
my $self = shift;
my $params = shift;
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Down" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=2&PanSpeed=6&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Left Arrow
sub moveConLeft
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
Debug( "Move Left" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=3&PanSpeed=$panspeed&TiltSpeed=6";
$self->sendCmd( $cmd );
$self->autoStop($panspeed);
}
#Right Arrow
sub moveConRight
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
Debug( "Move Right" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=4&PanSpeed=$panspeed&TiltSpeed=6";
$self->sendCmd( $cmd );
$self->autoStop($panspeed);
}
#Diagonally Up Right Arrow
sub moveConUpRight
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Diagonally Up Right" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=7&PanSpeed=$panspeed&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Diagonally Down Right Arrow
sub moveConDownRight
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Diagonally Down Right" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=8&PanSpeed=$panspeed&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Diagonally Up Left Arrow
sub moveConUpLeft
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Diagonally Up Left" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=5&PanSpeed=$panspeed&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Diagonally Down Left Arrow
sub moveConDownLeft
{
my $self = shift;
my $params = shift;
my $panspeed = $self->getParam( $params, 'panspeed' );
my $tiltspeed = $self->getParam( $params, 'tiltspeed' );
Debug( "Move Diagonally Down Left" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Direction=6&PanSpeed=$panspeed&TiltSpeed=$tiltspeed";
$self->sendCmd( $cmd );
$self->autoStop($tiltspeed);
}
#Stop
sub moveStop
{
my $self = shift;
Debug( "Move Stop" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Stop=0";
$self->sendCmd( $cmd );
}
sub zoomConTele
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Zoom=1";
$self->sendCmd( $cmd );
$self->autoStop();
}
sub zoomConWide
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Zoom=0";
$self->sendCmd( $cmd );
$self->autoStop();
}
sub focusConNear
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Focus=1";
$self->sendCmd( $cmd );
$self->autoStop();
}
sub focusConFar
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Focus=0";
$self->sendCmd( $cmd );
$self->autoStop();
}
sub irisConOpen
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Iris=1";
$self->sendCmd( $cmd );
$self->autoStop();
}
sub irisConClose
{
my $self = shift;
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Iris=0";
$self->sendCmd( $cmd );
$self->autoStop();
}
#Set preset
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&PresetNumber=1&Preset=0";
$self->sendCmd( $cmd );
}
#Goto preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&PresetNumber=1&Preset=1";
$self->sendCmd( $cmd );
}
sub autoStop
{
my $self = shift;
my $timeout = shift;
if ($timeout)
{
if ($timeout > 1) {
usleep(100000*$timeout);
}
}
Debug( "Auto Stop" );
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&Stop=0";
$self->sendCmd( $cmd );
}
1;
__END__
=head1 Floureon
ZoneMinder::Database - Perl extension for Floureon 1080P
=head1 SYNOPSIS
Control script for Floureon 1080P IP camera
=head1 DESCRIPTION
When setuping you monitor in the "Control" tab:
1. Select "Control type": Floureon 1080P
2. Leave "Control device" empty
3. Fill "Control Address" like username:password@ip/domain. Example: admin:admin123@192.168.1.110
=head2 EXPORT
None by default.
=head1 SEE ALSO
=head1 AUTHOR
Ognyan Bankov, E<lt>ogibankov@gmail.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2017 Ognyan Bankov
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -139,7 +139,7 @@ sub moveConUp
my $self = shift;
my $params = shift;
Debug( "Move Up" );
my $cmd = "decoder_control.cgi?command=0&onestep=1&";
my $cmd = "decoder_control.cgi?command=2&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -155,7 +155,7 @@ sub moveConDown
my $self = shift;
my $params = shift;
Debug( "Move Down" );
my $cmd = "decoder_control.cgi?command=2&onestep=1&";
my $cmd = "decoder_control.cgi?command=0&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -203,7 +203,7 @@ sub moveConUpRight
my $self = shift;
my $params = shift;
Debug( "Move Diagonally Up Right" );
my $cmd = "decoder_control.cgi?command=91&onestep=1&";
my $cmd = "decoder_control.cgi?command=93&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -219,7 +219,7 @@ sub moveConDownRight
my $self = shift;
my $params = shift;
Debug( "Move Diagonally Down Right" );
my $cmd = "decoder_control.cgi?command=93&onestep=1&";
my $cmd = "decoder_control.cgi?command=91&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -235,7 +235,7 @@ sub moveConUpLeft
my $self = shift;
my $params = shift;
Debug( "Move Diagonally Up Left" );
my $cmd = "decoder_control.cgi?command=90&onestep=1&";
my $cmd = "decoder_control.cgi?command=92&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -251,7 +251,7 @@ sub moveConDownLeft
my $self = shift;
my $params = shift;
Debug( "Move Diagonally Down Left" );
my $cmd = "decoder_control.cgi?command=92&onestep=1&";
my $cmd = "decoder_control.cgi?command=90&onestep=1&";
$self->sendCmd( $cmd );
my $autostop = $self->getParam( $params, 'autostop', 0 );
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
@ -275,7 +275,7 @@ sub presetHome
{
my $self = shift;
Debug( "Home Preset" );
my $cmd = "decoder_control.cgi?command=25&onestep=0&";
my $cmd = "decoder_control.cgi?command=4&onestep=0&";
$self->sendCmd( $cmd );
}

View File

@ -0,0 +1,630 @@
# ==========================================================================
#
# ZoneMinder Reolink IP Control Protocol Module, Date: 2016-01-19
# Converted for use with Reolink IP Camera by Chris Swertfeger
# Copyright (C) 2016 Chris Swertfeger
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the first implementation of the Reolink IP camera control
# protocol
#
package ZoneMinder::Control::Reolink;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our %CamParams = ();
# ==========================================================================
#
# Reolink IP Control Protocol
# This script sends ONVIF compliant commands and may work with other cameras
# that require authentication
#
# The script was developed against a RLC-423 and RLC-420.
#
# Basic preset functions are supported, but more advanced features, which make
# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc)
# may or may not work.
#
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:pass@10.1.2.1:8899
# admin:password@10.0.100.1:8899
#
# Use port 8000 by default for Reolink cameras
#
# Make sure and place a value in the Auto Stop Timeout field.
# Recommend starting with a value of 1 second, and adjust accordingly.
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
use MIME::Base64;
use Digest::SHA;
use DateTime;
my ($username,$password,$host,$port);
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
$self->loadMonitor();
#
# Extract the username/password host/port from ControlAddress
#
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ )
{ # user:pass@host...
$username = $1;
$password = $2;
$host = $3;
}
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )
{ # user@host...
$username = $1;
$host = $2;
}
else { # Just a host
$host = $self->{Monitor}{ControlAddress};
}
# Check if it is a host and port or just a host
if( $host =~ /([^:]+):(.+)/ )
{
$host = $1;
$port = $2;
}
else
{
$port = 80;
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $msg = shift;
my $content_type = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $server_endpoint = "http://".$host.":".$port."/$cmd";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => $content_type);
$req->header('Host' => $host.":".$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" );
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $host.":".$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
# We should really use an xml or soap library to parse the xml tags
my $content = $res->decoded_content;
if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
$CamParams{$1} = $2;
}
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $autostop = shift;
if( $autostop ) {
Debug( "Auto Stop" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep( $autostop );
$self->sendCmd( $cmd, $msg, $content_type );
}
}
# Reset the Camera
sub reset
{
Debug( "Camera Reset" );
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $cmd = "";
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Up Arrow
sub moveConUp
{
Debug( "Move Up" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Down Arrow
sub moveConDown
{
Debug( "Move Down" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Left Arrow
sub moveConLeft
{
Debug( "Move Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Right Arrow
sub moveConRight
{
Debug( "Move Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom In
sub zoomConTele
{
Debug( "Zoom Tele" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom Out
sub zoomConWide
{
Debug( "Zoom Wide" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
Debug( "Move Diagonally Up Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
Debug( "Move Diagonally Down Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
Debug( "Move Diagonally Up Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
Debug( "Move Diagonally Down Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Stop
sub moveStop
{
Debug( "Move Stop" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Set Camera Preset
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Recall Camera Preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $num = sprintf("%03d", $preset);
$num=~ tr/ /0/;
Debug( "Goto Preset $preset" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$num.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Horizontal Patrol
#To be determined if this camera supports this feature
sub horizontalPatrol
{
Debug( "Horizontal Patrol" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
}
#Horizontal Patrol Stop
#To be determined if this camera supports this feature
sub horizontalPatrolStop
{
Debug( "Horizontal Patrol Stop" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
}
# Increase Brightness
sub irisAbsOpen
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Brightness'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Brightness'} += $step;
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Decrease Brightness
sub irisAbsClose
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Brightness'} -= $step;
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Increase Contrast
sub whiteAbsIn
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Contrast'} += $step;
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
# Decrease Contrast
sub whiteAbsOut
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Contrast'} -= $step;
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
1;

View File

@ -8,6 +8,13 @@ exit;
fi
DEBUILD=`which debuild`;
if [ "$DEBUILD" == "" ]; then
echo "You must install the devscripts package. Try sudo apt-get install devscripts";
exit;
fi
for i in "$@"
do
case $i in

View File

@ -1,47 +1,4 @@
# Overview
## Docker Support Files
[Docker](https://www.docker.io/) allows you to quickly spin up application containers,
which are similar to very lightweight virtual machines. The ZoneMinder Dockerfile will
start an Ubuntu 12.04 container with MySql, Apache, and PHP properly configured, and
will then compile and install ZoneMinder.
It will also start an SSH server that you can use to log into the container.
This is still a bit of a work in progress.
## How To Use
1. Install [Docker](https://www.docker.io/)
2. Build ZoneMinder container
```sudo docker build -t yourname/zoneminder github.com/ZoneMinder/ZoneMinder```
3. Run it
```CID=$(sudo docker run -d -p 222:22 -p 8080:80 --name zoneminder yourname/zoneminder)```
4. Use it -- you can now SSH to port 222 on your host as user root with password root.
You can also browse to your host on port 8080 to access the zoneminder web interface
## Developing With Docker
If you wish to contribute to ZoneMinder, Docker can be helpful. By re-running
```docker build``` in your working directory, any code modifications you have
made will be pulled into a new container, compiled, and started, all without
modifying your base system.
Development is not totally without annoyances, as any change
to the project will require a full rebuild of all C++. Docker notices that the
directory which has been ADD'ed is now different, and therefore all steps after
the ADD command must be recomputed. A fix for this is to update the Dockerfile to
move the configure and make commands into start.sh, and then use a volume mount
to cache the build directory (I think it's ```/tmp```) on your host filesystem.
This would be really useful for a developer, and would remove the annoying build
problem, but some of the Docker push/pull benefits would be lost.
Docker containers can be both CPU and memory limited, so this can be a practical
method to compile or run multiple development builds of ZoneFinder simultaneously
without taxing your host system.
## Use Cases
## TODO
- Describe how to connect to monitors by mounting devices
- Create a 'development' dockerfile to remove the need to rebuild the entire project
after each small change
Docker support files have been moved to the zmdockerfiles repo:
https://github.com/ZoneMinder/zmdockerfiles

View File

@ -1,5 +0,0 @@
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = GMT

View File

@ -1,75 +0,0 @@
#!/bin/bash
setup_mysql_first_time(){
if [ "$(ls /var/lib/mysql)" ]; then
return
fi
# Set MySQL in the volume
rm -rf /var/lib/mysql/*
chown -R mysql:mysql /var/lib/mysql
mysqld --initialize-insecure
# Start MySQL
# For Xenial the following won't start mysqld
#/usr/bin/mysqld_safe &
# Use this instead:
service mysql start
# Give MySQL time to wake up
SECONDS_LEFT=120
while true; do
sleep 1
mysqladmin ping
if [ $? -eq 0 ];then
break; # Success
fi
let SECONDS_LEFT=SECONDS_LEFT-1
# If we have waited >120 seconds, give up
# ZM should never have a database that large!
# if $COUNTER -lt 120
if [ $SECONDS_LEFT -eq 0 ];then
return -1;
fi
done
# Create the ZoneMinder database
mysql -u root < db/zm_create.sql
# Add the ZoneMinder DB user
mysql -u root -e "grant insert,select,update,delete,lock tables,alter on zm.* to 'zmuser'@'localhost' identified by 'zmpass';"
# Shut down mysql cleanly:
kill $(cat /var/run/mysqld/mysqld.pid)
sleep 5
}
setup_mysql() {
# To configure MySQL if no container did it before
setup_mysql_first_time
# Add configuration to avoid SQL error when adding monitor
echo "sql_mode=NO_ENGINE_SUBSTITUTION" >> /etc/mysql/mysql.conf.d/mysqld.cnf
}
setup_php() {
# Activate CGI
a2enmod cgi
# Activate modrewrite
a2enmod rewrite
# Setting timezone
sed -i "s#;date.timezone =#date.timezone = $PHP_TIMEZONE#" /etc/php/7.0/apache2/php.ini
# Settings rights for volume
chown -R www-data:www-data /var/lib/zoneminder/events
chown -R www-data:www-data /var/lib/zoneminder/images
}
setup_mysql
setup_php
exit 0

View File

@ -1,48 +0,0 @@
#!/bin/bash
# Prepare proper amount of shared memory
# For H.264 cameras it may be necessary to increase the amount of shared memory
# to 2048 megabytes.
umount /dev/shm
mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=512M tmpfs /dev/shm
# Start MySQL
test -e /var/run/mysqld || install -m 755 -o mysql -g root -d /var/run/mysqld
su - mysql -s /bin/sh -c "/usr/bin/mysqld_safe > /dev/null 2>&1 &"
# Ensure we shut down mysql cleanly later:
trap close_mysql SIGTERM
# Give MySQL time to wake up
SECONDS_LEFT=120
while true; do
sleep 1
mysqladmin ping
if [ $? -eq 0 ];then
break; # Success
fi
let SECONDS_LEFT=SECONDS_LEFT-1
# If we have waited >120 seconds, give up
# ZM should never have a database that large!
# if $COUNTER -lt 120
if [ $SECONDS_LEFT -eq 0 ];then
return -1;
fi
done
# Restart apache
service apache2 restart
# Start ZoneMinder
/usr/local/bin/zmpkg.pl start && echo "Zone Minder started"
while :
do
sleep 3600
done
function close_mysql {
kill $(cat /var/run/mysqld/mysqld.pid)
sleep 5
}

View File

@ -0,0 +1,22 @@
From 634281a4204467b9a3c8a1a5febcc8dd9828e0f6 Mon Sep 17 00:00:00 2001
From: Andy Bauer <knnniggett@hotmail.com>
Date: Thu, 22 Feb 2018 08:53:50 -0600
Subject: [PATCH] don't run lintian checks to speed up build
---
pack/deb.mk | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pack/deb.mk b/pack/deb.mk
index de4a0b7..bddf9df 100644
--- a/packpack/pack/deb.mk
+++ b/packpack/pack/deb.mk
@@ -130,7 +130,7 @@ $(BUILDDIR)/$(DPKG_CHANGES): $(BUILDDIR)/$(PRODUCT)-$(VERSION)/debian \
@echo "Building Debian packages"
@echo "-------------------------------------------------------------------"
cd $(BUILDDIR)/$(PRODUCT)-$(VERSION) && \
- debuild --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \
+ debuild --no-lintian --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \
-Z$(TARBALL_COMPRESSOR) -uc -us $(SMPFLAGS)
rm -rf $(BUILDDIR)/$(PRODUCT)-$(VERSION)/
@echo "------------------------------------------------------------------"

View File

@ -0,0 +1,57 @@
From 62a98b36fd62d328956503bc9427ae128bb811af Mon Sep 17 00:00:00 2001
From: Andrew Bauer <zonexpertconsulting@outlook.com>
Date: Mon, 26 Feb 2018 10:05:02 -0600
Subject: [PATCH] fix 32bit rpm builds
---
pack/rpm.mk | 2 +-
packpack | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packpack/pack/rpm.mk b/packpack/pack/rpm.mk
index c74e942..9a6b016 100644
--- a/packpack/pack/rpm.mk
+++ b/packpack/pack/rpm.mk
@@ -124,7 +124,7 @@ package: $(BUILDDIR)/$(RPMSRC)
@echo "-------------------------------------------------------------------"
@echo "Building RPM packages"
@echo "-------------------------------------------------------------------"
- rpmbuild \
+ setarch $(ARCH) rpmbuild \
--define '_topdir $(BUILDDIR)' \
--define '_sourcedir $(BUILDDIR)' \
--define '_specdir $(BUILDDIR)' \
diff --git a/packpack/packpack b/packpack/packpack
index 6f4c80f..c329399 100755
--- a/packpack/packpack
+++ b/packpack/packpack
@@ -125,7 +125,7 @@ chmod a+x ${BUILDDIR}/userwrapper.sh
#
# Save defined configuration variables to ./env file
#
-env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \
+env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=|^ARCH=" \
> ${BUILDDIR}/env
#
diff --git a/packpack/packpack b/packpack/packpack
index c329399..6ffaa9c 100755
--- a/packpack/packpack
+++ b/packpack/packpack
@@ -19,11 +19,11 @@ DOCKER_REPO=${DOCKER_REPO:-packpack/packpack}
if [ -z "${ARCH}" ]; then
# Use uname -m instead of HOSTTYPE
case "$(uname -m)" in
- i*86) ARCH="i386" ;;
- arm*) ARCH="armhf" ;;
- x86_64) ARCH="x86_64"; ;;
- aarch64) ARCH="aarch64" ;;
- *) ARCH="${HOSTTYPE}" ;;
+ i*86) export ARCH="i386" ;;
+ arm*) export ARCH="armhf" ;;
+ x86_64) export ARCH="x86_64"; ;;
+ aarch64) export ARCH="aarch64" ;;
+ *) export ARCH="${HOSTTYPE}" ;;
esac
fi

View File

@ -108,6 +108,18 @@ commonprep () {
patch -p1 < utils/packpack/packpack-rpm.patch
fi
# Skip deb lintian checks to speed up the build
patch --dry-run --silent -f -p1 < utils/packpack/nolintian.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/nolintian.patch
fi
# fix 32bit rpm builds
patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch
if [ $? -eq 0 ]; then
patch -p1 < utils/packpack/setarch.patch
fi
# The rpm specfile requires we download the tarball and manually move it into place
# Might as well do this for Debian as well, rather than git submodule init
CRUDVER="3.0.10"
@ -295,7 +307,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
execpackpack
# Steps common to Debian based distros
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
echo "Begin ${OS} ${DIST} build..."
setdebpkgname

View File

@ -10,7 +10,7 @@ configure_file(includes/config.php.in "${CMAKE_CURRENT_BINARY_DIR}/includes/conf
# Install the web files
install(DIRECTORY api ajax css fonts graphics includes js lang skins tools views DESTINATION "${ZM_WEBDIR}" PATTERN "*.in" EXCLUDE PATTERN "*Make*" EXCLUDE PATTERN "*cmake*" EXCLUDE)
install(FILES index.php DESTINATION "${ZM_WEBDIR}")
install(FILES index.php robots.txt DESTINATION "${ZM_WEBDIR}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" DESTINATION "${ZM_WEBDIR}/includes")
# Install the api config files (if its not in the source directory)

View File

@ -136,6 +136,17 @@ $statusData = array(
//"Path" => array( "postFunc" => "getEventPath" ),
),
),
"frames" => array(
"permission" => "Events",
"table" => "Frames",
"selector" => "EventId",
"elements" => array(
"EventId" => true,
"FrameId" => true,
"Type" => true,
"Delta" => true,
),
),
"frame" => array(
"permission" => "Events",
"table" => "Frames",
@ -367,7 +378,7 @@ function getNearEvents() {
else
$midSql = '';
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc');
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc') . ' LIMIT 2';
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
@ -376,7 +387,7 @@ function getNearEvents() {
}
}
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder";
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn $sortOrder LIMIT 2";
$result = dbQuery( $sql );
while ( $id = dbFetchNext( $result, 'Id' ) ) {
if ( $id == $eventId ) {
@ -388,8 +399,10 @@ function getNearEvents() {
$result = array( 'EventId'=>$eventId );
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id']));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id']));
return( $result );
}

View File

@ -76,11 +76,10 @@ public function beforeFilter() {
);
$events = $this->Paginator->paginate('Event');
// For each event, get its thumbnail data (path, width, height)
// For each event, get the frameID which has the largest score
foreach ($events as $key => $value) {
//$thumbData = $this->createThumbnail($value['Event']['Id']);
$thumbData = "";
$events[$key]['thumbData'] = $thumbData;
$maxScoreFrameId = $this->getMaxScoreAlarmFrameId($value['Event']['Id']);
$events[$key]['Event']['MaxScoreFrameId'] = $maxScoreFrameId;
}
@ -278,6 +277,32 @@ public function beforeFilter() {
));
}
public function getMaxScoreAlarmFrameId($id = null) {
$this->Event->recursive = -1;
if (!$this->Event->exists($id)) {
throw new NotFoundException(__('Invalid event'));
}
$event = $this->Event->find('first', array(
'conditions' => array('Id' => $id)
));
// Find the max Frame for this Event. Error out otherwise.
$this->loadModel('Frame');
if (! $frame = $this->Frame->find('first', array(
'conditions' => array(
'EventId' => $event['Event']['Id'],
'Score' => $event['Event']['MaxScore']
)
))) {
throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
}
return $frame['Frame']['Id'];
}
// Create a thumbnail and return the thumbnail's data for a given event id.
public function createThumbnail($id = null) {
$this->Event->recursive = -1;
@ -301,6 +326,7 @@ public function beforeFilter() {
throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id']));
}
$this->loadModel('Config');
// Get the config options required for reScale and getImageSrc

View File

@ -122,7 +122,7 @@ public function beforeFilter() {
if (!$this->Monitor->exists($id)) {
throw new NotFoundException(__('Invalid monitor'));
}
if ($this->Session->Read('systemPermission') != 'Edit')
if ($this->Session->Read('monitorPermission') != 'Edit')
{
throw new UnauthorizedException(__('Insufficient privileges'));
return;

View File

@ -212,14 +212,12 @@ if ( !empty($action) ) {
deleteEvent( $markEid );
$refreshParent = true;
}
if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
if ( !empty($_REQUEST['Id']) ) {
dbQuery( 'DELETE FROM Filters WHERE Id=?', array( $_REQUEST['Id'] ) );
if ( !empty($_REQUEST['fid']) ) {
dbQuery( 'DELETE FROM Filters WHERE Name=?', array( $_REQUEST['fid'] ) );
//$refreshParent = true;
}
}
}
}
// Monitor control actions, require a monitor id and control view permissions for that monitor
if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {

View File

@ -452,7 +452,8 @@ function getEventPath( $event ) {
}
function getEventDefaultVideoPath( $event ) {
return ZM_DIR_EVENTS . '/' . getEventPath($event) . '/' . $event['DefaultVideo'];
$Event = new Event( $event );
return $Event->getStreamSrc( array( "mode"=>"mpeg", "format"=>"h264" ) );
}
function deletePath( $path ) {
@ -992,7 +993,7 @@ function zmaCheck( $monitor ) {
}
function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false ) {
$eventPath = getEventPath( $event );
$eventPath = ZM_DIR_EVENTS . '/' . getEventPath( $event );
if ( !is_array($frame) )
$frame = array( 'FrameId'=>$frame, 'Type'=>'' );
@ -1504,6 +1505,66 @@ function getDiskBlocks() {
return( $space );
}
function systemStats() {
$load = getLoad();
$diskPercent = getDiskPercent();
$pathMapPercent = getDiskPercent(ZM_PATH_MAP);
$cpus = getcpus();
$normalized_load = $load / $cpus;
# Colorize the system load stat
if ( $normalized_load <= 0.75 ) {
$htmlLoad=$load;
} elseif ( $normalized_load <= 0.9 ) {
$htmlLoad="<span class=\"warning\">$load</span>";
} elseif ( $normalized_load <= 1.1 ) {
$htmlLoad="<span class=\"error\">$load</span>";
} else {
$htmlLoad="<span class=\"critical\">$load</span>";
}
# Colorize the disk space stat
if ( $diskPercent < 98 ) {
$htmlDiskPercent="$diskPercent%";
} elseif ( $diskPercent <= 99 ) {
$htmlDiskPercent="<span class=\"warning\">$diskPercent%</span>";
} else {
$htmlDiskPercent="<span class=\"error\">$diskPercent%</span>";
}
# Colorize the PATH_MAP (usually /dev/shm) stat
if ( $pathMapPercent < 90 ) {
if ( disk_free_space(ZM_PATH_MAP) > 209715200 ) { # have to always have at least 200MiB free
$htmlPathMapPercent="$pathMapPercent%";
} else {
$htmlPathMapPercent="<span class=\"warning\">$pathMapPercent%</span>";
}
} elseif ( $pathMapPercent < 100 ) {
$htmlPathMapPercent="<span class=\"warning\">$pathMapPercent%</span>";
} else {
$htmlPathMapPercent="<span class=\"critical\">$pathMapPercent%</span>";
}
$htmlString = translate('Load').": $htmlLoad - ".translate('Disk').": $htmlDiskPercent - ".ZM_PATH_MAP.": $htmlPathMapPercent";
return( $htmlString );
}
function getcpus() {
if (is_readable("/proc/cpuinfo") ) { # Works on Linux
preg_match_all('/^processor/m', file_get_contents('/proc/cpuinfo'), $matches);
$num_cpus = count($matches[0]);
} else { # Works on BSD
$matches = explode(":", shell_exec("sysctl hw.ncpu"));
$num_cpus = trim($matches[1]);
}
return( $num_cpus );
}
// Function to fix a problem whereby the built in PHP session handling
// features want to put the sid as a hidden field after the form or
// fieldset tag, neither of which will work with strict XHTML Basic.
@ -2245,6 +2306,7 @@ function getStreamMode( ) {
$streamMode = 'single';
Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' );
}
return $streamMode;
} // end function getStreamMode
function folder_size($dir) {

View File

@ -37,7 +37,7 @@ function loadLanguage( $prefix="" )
$fallbackLangFile = $prefix.'lang/en_gb.php';
$systemLangFile = $prefix.'lang/'.ZM_LANG_DEFAULT.'.php';
if ( isset($user['Language']) )
$userLangFile = $prefix.$user['Language'].'.php';
$userLangFile = $prefix.'lang/'.$user['Language'].'.php';
if ( isset($userLangFile) && file_exists( $userLangFile ) )
return( $userLangFile );

2
web/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@ -12,6 +12,19 @@
float: right;
}
#systemStats .warning {
color: orange;
}
#systemStats .error {
color: red;
}
#systemStats .critical {
color: red;
text-decoration: blink;
}
#monitorSummary {
float: left;
text-align: left;

View File

@ -1,3 +1,34 @@
#content .vjsMessage {
width: 100%;
position: absolute;
left: 0;
z-index: 10;
margin: 0;
font-size: 200%;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.25em;
text-align: left;
margin: 0 auto 0 auto;
border-radius: 0 0 .3em .3em;
}
.alarmCue span {
background-color:red;
height: 100%;
display: inline-block;
border-radius: 0;
}
span.noneCue {
background: none;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -6,6 +37,7 @@
#dataBar #dataTable {
width: 100%;
table-layout: fixed;
}
#dataBar #dataTable td {
@ -13,6 +45,11 @@
padding: 2px;
}
#eventVideo {
display: inline-block;
position: relative;
}
#menuBar1 {
width: 100%;
padding: 3px 0;
@ -83,6 +120,8 @@
}
#imageFeed {
display: inline-block;
position: relative;
text-align: center;
}
@ -147,39 +186,29 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.25em;
height: 1.25em;
margin: 0 auto -1.25em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
transition: width .1s;
height: 100%;
background: rgba(170, 170, 170, .7);
border-radius: 0 0 .3em .3em;
}
#eventStills {
width: 100%;
position: relative;
}
#eventThumbsPanel {
position: relative;
width: 100%;
margin: 4px auto;
z-index: 1;
}
#eventThumbs {
margin: 0 auto;
width: 100%;
overflow: hidden;
height: 300px;
}
@ -198,14 +227,19 @@
}
#eventImagePanel {
display: none;
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
}
#eventImageFrame {
border: 2px solid gray;
background-color: white;
padding: 4px;
display: inline-block;
}
#eventImage {
@ -215,6 +249,14 @@
margin-top: 2px;
}
#eventImageBar::after {
visibility: hidden;
display: block;
content: "";
clear: both;
height: 0;
}
#eventImageStats {
float: left;
}
@ -230,6 +272,7 @@
#eventImageNav {
position: relative;
margin: 0 0 4px 0;
}
#eventImageNav input {
@ -238,20 +281,20 @@
}
#thumbsSliderPanel {
width: 400px;
margin: 4px auto 0;
background: #888888;
padding: 1px;
width: 80%;
margin: 0px auto 4px auto;
}
#thumbsSlider {
width: 400px;
height: 10px;
background: #dddddd;
width: 100%;
height: 1.25em;
position: relative;
top: -1.25em;
margin: 0 0 -1.25em 0;
}
#thumbsKnob {
width: 8px;
height: 10px;
background-color: #444444;
width: 1em;
height: 100%;
background-color: #999999;
}

View File

@ -1,33 +1,16 @@
#scaleControl {
float: right;
}
#controls {
width: 80%;
text-align: center;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
#controls a {
width: 40px;
margin-left: -20px;
}
#firstLink {
position: absolute;
left: 13%;
}
#prevLink {
position: absolute;
left: 37%;
}
#nextLink {
position: absolute;
left: 63%;
}
#lastLink {
position: absolute;
left: 87%;
}

View File

@ -16,6 +16,10 @@
#monitors .alert {
color: #ffa500;
margin: 0;
padding: 0;
border-radius: 0;
border: none;
}
#monitors .imageFeed {

View File

@ -12,6 +12,19 @@
float: right;
}
#systemStats .warning {
color: orange;
}
#systemStats .error {
color: red;
}
#systemStats .critical {
color: red;
text-decoration: blink;
}
#monitorSummary {
float: left;
text-align: left;

View File

@ -1,3 +1,39 @@
#content .vjsMessage {
width: 100%;
position: absolute;
left: 0;
z-index: 10;
margin: 0;
font-size: 200%;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.25em;
text-align: left;
margin: 0 auto 0 auto;
border-radius: 0 0 .3em .3em;
}
.alarmCue span {
background-color:red;
height: 100%;
display: inline-block;
border-radius: 0;
}
span.noneCue {
background: none;
}
#eventVideo {
display: inline-block;
position: relative;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -6,6 +42,7 @@
#dataBar #dataTable {
width: 100%;
table-layout: fixed;
}
#dataBar #dataTable td {
@ -66,6 +103,8 @@
}
#imageFeed {
display: inline-block;
position: relative;
text-align: center;
}
@ -130,39 +169,30 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.25em;
height: 1.25em;
margin: 0 auto -1.25em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
transition: width .1s;
height: 100%;
background: rgba(170, 170, 170, .7);
border-radius: 0 0 .3em .3em;
}
#eventStills {
width: 100%;
position: relative;
}
#eventThumbsPanel {
position: relative;
width: 100%;
margin: 4px auto;
z-index: 1;
}
#eventThumbs {
margin: 0 auto;
width: 100%;
overflow: hidden;
height: 300px;
}
@ -181,14 +211,19 @@
}
#eventImagePanel {
display: none;
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
}
#eventImageFrame {
border: 2px solid gray;
background-color: white;
padding: 4px;
display: inline-block;
}
#eventImage {
@ -198,6 +233,14 @@
margin-top: 2px;
}
#eventImageBar::after {
visibility: hidden;
display: block;
content: "";
clear: both;
height: 0;
}
#eventImageStats {
float: left;
}
@ -213,6 +256,7 @@
#eventImageNav {
position: relative;
margin: 0 0 4px 0;
}
#eventImageNav input {
@ -222,19 +266,25 @@
#thumbsSliderPanel {
width: 400px;
margin: 4px auto 0;
background: #888888;
padding: 1px;
width: 80%;
margin: 0px auto 4px auto;
}
#thumbsSlider {
width: 400px;
height: 10px;
background: #dddddd;
width: 100%;
height: 1.25em;
position: relative;
top: -1.25em;
margin: 0 0 -1.25em 0;
}
#eventVideo {
display: inline-block;
position: relative;
}
#thumbsKnob {
width: 8px;
height: 10px;
background-color: #444444;
width: 1em;
height: 100%;
background-color: #999999;
}

View File

@ -1,33 +1,16 @@
#scaleControl {
float: right;
}
#controls {
width: 80%;
text-align: center;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
#controls a {
width: 40px;
margin-left: -20px;
}
#firstLink {
position: absolute;
left: 13%;
}
#prevLink {
position: absolute;
left: 37%;
}
#nextLink {
position: absolute;
left: 63%;
}
#lastLink {
position: absolute;
left: 87%;
}

View File

@ -12,6 +12,10 @@
#monitors .alert {
color: #ffa500;
margin: 0;
padding: 0;
border-radius: 0;
border: none;
}
#monitors .imageFeed {

View File

@ -12,6 +12,19 @@
float: right;
}
#systemStats .warning {
color: orange;
}
#systemStats .error {
color: red;
}
#systemStats .critical {
color: red;
text-decoration: blink;
}
#monitorSummary {
float: left;
text-align: left;

View File

@ -1,3 +1,34 @@
#content .vjsMessage {
width: 100%;
position: absolute;
left: 0;
z-index: 10;
margin: 0;
font-size: 200%;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.25em;
text-align: left;
margin: 0 auto 0 auto;
border-radius: 0 0 .3em .3em;
}
.alarmCue span {
background-color:red;
height: 100%;
display: inline-block;
border-radius: 0;
}
span.noneCue {
background: none;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -6,6 +37,7 @@
#dataBar #dataTable {
width: 100%;
table-layout: fixed;
}
#dataBar #dataTable td {
@ -64,7 +96,10 @@
clear: both;
visibility: hidden;
}
#imageFeed {
display: inline-block;
position: relative;
text-align: center;
}
@ -135,39 +170,30 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.25em;
height: 1.25em;
margin: 0 auto -1.25em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
transition: width .1s;
height: 100%;
background: rgba(170, 170, 170, .7);
border-radius: 0 0 .3em .3em;
}
#eventStills {
width: 100%;
position: relative;
}
#eventThumbsPanel {
position: relative;
width: 100%;
margin: 4px auto;
margin: 0;
z-index: 1;
}
#eventThumbs {
margin: 0 auto;
width: 100%;
overflow: hidden;
height: 300px;
}
@ -186,14 +212,19 @@
}
#eventImagePanel {
display: none;
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
}
#eventImageFrame {
border: 2px solid gray;
background-color: white;
padding: 4px;
display: inline-block;
}
#eventImage {
@ -216,8 +247,17 @@
float: right;
}
#eventImageBar::after {
visibility: hidden;
display: block;
content: "";
clear: both;
height: 0;
}
#eventImageNav {
position: relative;
margin: 0 0 4px 0;
}
#eventImageNav input {
@ -226,24 +266,25 @@
}
#thumbsSliderPanel {
width: 400px;
margin: 4px auto 0;
background: #888888;
padding: 1px;
width: 80%;
margin: 0px auto 4px auto;
}
#thumbsSlider {
width: 400px;
height: 10px;
background: #dddddd;
width: 100%;
height: 1.25em;
position: relative;
top: -1.25em;
margin: 0 0 -1.25em 0;
}
#thumbsKnob {
width: 8px;
height: 10px;
background-color: #444444;
width: 1em;
height: 100%;
background-color: #999999;
}
#eventVideo {
display: inline-block;
position: relative;
}
@ -274,10 +315,6 @@
);
}
#eventVideo:hover #video-controls {
opacity: .9;
}
button {
background: rgba(0,0,0,.5);
border: 0;

View File

@ -6,29 +6,11 @@
width: 80%;
text-align: center;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
#controls a {
width: 40px;
margin-left: -20px;
}
#firstLink {
position: absolute;
left: 13%;
}
#prevLink {
position: absolute;
left: 37%;
}
#nextLink {
position: absolute;
left: 63%;
}
#lastLink {
position: absolute;
left: 87%;
}

View File

@ -12,6 +12,10 @@
#monitors .alert {
color: #ffa500;
margin: 0;
padding: 0;
border-radius: 0;
border: none;
}
#monitors .imageFeed {

View File

@ -31,6 +31,7 @@ $rates = array(
);
$scales = array(
"auto" => translate("Scale to Fit"),
"400" => "4x",
"300" => "3x",
"200" => "2x",
@ -43,6 +44,8 @@ $scales = array(
"12.5" => "1/8x",
);
if (isset($_REQUEST['view']) && ($_REQUEST['view'] == 'montage')) unset($scales['auto']); //Remove auto if we aren't using event view
$bwArray = array(
"high" => translate('High'),
"medium" => translate('Medium'),

View File

@ -834,7 +834,7 @@ function exportFileList( $eid, $exportDetail, $exportFrames, $exportImages, $exp
$filesLeft = array();
foreach ( $files as $file )
{
if ( preg_match( "/\.(?:mpg|mpeg|avi|asf|3gp)$/", $file ) )
if ( preg_match( '/\.(?:mpg|mpeg|mov|swf|mp4|mkv|avi|asf|3gp)$/', $file ) )
{
$exportFileList[$file] = $eventPath."/".$file;
}

View File

@ -92,6 +92,7 @@ var $j = jQuery.noConflict();
<?php } else if ( $title == 'Event' ) {
?>
<link href="skins/<?php echo $skin ?>/js/video-js.css" rel="stylesheet">
<link href="skins/<?php echo $skin ?>/js/video-js-skin.css" rel="stylesheet">
<script src="skins/<?php echo $skin ?>/js/video.js"></script>
<script src="./js/videojs.zoomrotate.js"></script>
<script src="skins/<?php echo $skin ?>/js/moment.min.js"></script>

View File

@ -262,6 +262,17 @@ function convertLabelFormat(LabelFormat, monitorName){
}
function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTime){
//This is a hacky way to handle changing the texttrack. If we ever upgrade vjs in a revamp replace this. Old method preserved because it's the right way.
let cues = vid.textTracks()[0].cues();
let labelFormat = convertLabelFormat(LabelFormat, monitorName);
startTime = moment(startTime);
for (let i = 0; i <= duration; i++) {
cues[i] = {id: i, index: i, startTime: i, Ca: i+1, text: startTime.format(labelFormat)};
startTime.add(1, 's');
}
}
/*
var labelFormat = convertLabelFormat(LabelFormat, monitorName);
var webvttformat = 'HH:mm:ss.SSS', webvttdata="WEBVTT\n\n";
@ -283,3 +294,37 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
video.appendChild(track);
}
*/
var resizeTimer;
function endOfResize(e) {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(changeScale, 250);
}
function scaleToFit (baseWidth, baseHeight, scaleEl, bottomEl) {
$j(window).on('resize', endOfResize) //set delayed scaling when Scale to Fit is selected
let ratio = baseWidth / baseHeight;
let container = $j('#content');
let viewPort = $j(window);
// jquery does not provide a bottom offet, and offset dows not include margins. outerHeight true minus false gives total vertical margins.
let bottomLoc = bottomEl.offset().top + (bottomEl.outerHeight(true) - bottomEl.outerHeight()) + bottomEl.outerHeight(true);
let newHeight = viewPort.height() - (bottomLoc - scaleEl.outerHeight(true))
let newWidth = ratio * newHeight;
if (newWidth > container.innerWidth()) {
newWidth = container.innerWidth();
newHeight = newWidth / ratio;
}
let autoScale = Math.round(newWidth / baseWidth * SCALE_BASE);
let scales = $j('#scale option').map(function() {return parseInt($j(this).val());}).get();
scales.shift();
let closest;
$j(scales).each(function () { //Set zms scale to nearest regular scale. Zoom does not like arbitrary scale values.
if (closest == null || Math.abs(this - autoScale) < Math.abs(closest - autoScale)) {
closest = this.valueOf();
}
});
autoScale = closest;
return {width: Math.floor(newWidth), height: Math.floor(newHeight), autoScale: autoScale};
}

View File

@ -39,4 +39,4 @@ var refreshParent = <?php echo !empty($refreshParent)?'true':'false' ?>;
var focusWindow = <?php echo !empty($focusWindow)?'true':'false' ?>;
var imagePrefix = "<?php echo viewImagePath( "", '&' ) ?>";
var imagePrefix = "<?php echo "?view=image&eid=" ?>";

View File

@ -0,0 +1,65 @@
.vjs-tech {
pointer-events: none;
transition: transform .25s;
}
.vjs-captions-button {
display: none;
}
.vjs-tt-cue {
margin-bottom: .75em;
}
.vjs-menu {
z-index: 5;
}
.vjs-default-skin .vjs-playback-rate.vjs-menu-button .vjs-menu .vjs-menu-content {
width: 5em;
}
.vjs-default-skin.vjs-user-inactive:hover .vjs-progress-control {
font-size: .3em;
}
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
visibility: visible;
opacity: 1;
bottom: -2em;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-o-transition: all .2s;
transition: all .2s
}
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control,
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-time-divider {
display: none;
}
.vjs-default-skin .vjs-progress-holder .vjs-play-progress, .vjs-default-skin .vjs-progress-holder .vjs-load-progress {
height: 1em;
}
.vjs-progress-holder.vjs-slider {
background-color: transparent;
z-index: 1;
}
.vjs-progress-control.vjs-control {
background-color: rgba(7, 20, 30, 0.8);
height: 1.3em;
top: -1.3em;
display: block !important;
}
.vjs-control div.alarmCue {
position: relative;
top: -1.3em;
background: none;
}
.vjs-control .alarmCue {
height: 1.3em;
}

View File

@ -154,7 +154,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
<input type="hidden" name="action" value=""/>
<div id="header">
<h3 id="systemTime"><?php echo preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG ) ?></h3>
<h3 id="systemStats"><?php echo translate('Load') ?>: <?php echo getLoad() ?> - <?php echo translate('Disk') ?>: <?php echo getDiskPercent() ?>% - <?php echo ZM_PATH_MAP ?>: <?php echo getDiskPercent(ZM_PATH_MAP) ?>%</h3>
<h3 id="systemStats"><?php echo systemStats() ?></h3>
<h2 id="title">
<a href="http://www.zoneminder.com" target="ZoneMinder">ZoneMinder</a> <?php echo translate('Console') ?> -
<?php echo makePopupLink( '?view=state', 'zmState', 'state', $status, canEdit( 'System' ) ) ?> -

View File

@ -43,6 +43,8 @@ else
if ( isset( $_REQUEST['scale'] ) ) {
$scale = validInt($_REQUEST['scale']);
} else if ( isset( $_COOKIE['zmEventScaleAuto'] ) ) { //If we're using scale to fit use it on all monitors
$scale = 'auto';
} else if ( isset( $_COOKIE['zmEventScale'.$Event->MonitorId()] ) ) {
$scale = $_COOKIE['zmEventScale'.$Event->MonitorId()];
} else {
@ -61,6 +63,7 @@ if ( isset( $_REQUEST['streamMode'] ) )
else
$streamMode = 'video';
$replayMode = '';
if ( isset( $_REQUEST['replayMode'] ) )
$replayMode = validHtmlStr($_REQUEST['replayMode']);
if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) )
@ -84,10 +87,6 @@ parseSort();
parseFilter( $_REQUEST['filter'] );
$filterQuery = $_REQUEST['filter']['query'];
$panelSections = 40;
$panelSectionWidth = (int)ceil(reScale($Event->Width(),$scale)/$panelSections);
$panelWidth = ($panelSections*$panelSectionWidth-1);
$connkey = generateConnKey();
$focusWindow = true;
@ -126,24 +125,25 @@ if ( canEdit( 'Events' ) ) {
?>
<div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?php echo translate('Delete') ?></a></div>
<div id="editEvent"><a href="#" onclick="editEvent()"><?php echo translate('Edit') ?></a></div>
<div id="archiveEvent" class="hidden"><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
<div id="unarchiveEvent" class="hidden"><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
<div id="archiveEvent"<?php echo $Event->Archived == 1 ? ' class="hidden"' : '' ?>><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
<div id="unarchiveEvent"<?php echo $Event->Archived == 0 ? ' class="hidden"' : '' ?>><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
<?php
} // end if can edit Events
if ( $Event->DefaultVideo() ) { ?>
<div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc()?>">Download MP4</a></div>
?>
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?php echo translate('Frames') ?></a></div>
<div id="streamEvent" class="hidden"><a href="#" onclick="showStream()"><?php echo translate('Stream') ?></a></div>
<div id="stillsEvent"><a href="#" onclick="showStills()"><?php echo translate('Stills') ?></a></div>
<?php
if ( $Event->DefaultVideo() ) {
?>
<div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc(array('mode'=>'mp4'))?>" download>Download MP4</a></div>
<?php
} else {
?>
<div id="videoEvent"><a href="#" onclick="videoEvent();"><?php echo translate('Video') ?></a></div>
<?php
} // end if Event->DefaultVideo
?>
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?php echo translate('Frames') ?></a></div>
<?php
if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
?>
<div id="stillsEvent"<?php if ( $streamMode == 'still' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStills()"><?php echo translate('Stills') ?></a></div>
<?php
} // has frames or analysis
?>
<div id="videoEvent"><a href="#" onclick="videoEvent();"><?php echo translate('Video') ?></a></div>
<div id="exportEvent"><a href="#" onclick="exportEvent();"><?php echo translate('Export') ?></a></div>
</div>
<div id="eventVideo" class="">
@ -151,23 +151,17 @@ if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
if ( $Event->DefaultVideo() ) {
?>
<div id="videoFeed">
<video id="videoobj" class="video-js vjs-default-skin" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "playbackRates": [0.5, 1, 1.5, 2, 4, 8, 16, 32, 64, 128, 256], "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
<video id="videoobj" class="video-js vjs-default-skin" style="transform: matrix(1, 0, 0, 1, 0, 0)" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
<source src="<?php echo $Event->getStreamSrc( array( 'mode'=>'mpeg','format'=>'h264' ) ); ?>" type="video/mp4">
<track id="monitorCaption" kind="captions" label="English" srclang="en" src='data:plain/text;charset=utf-8,"WEBVTT\n\n 00:00:00.000 --> 00:00:01.000 ZoneMinder"' default>
Your browser does not support the video tag.
</video>
</div>
<!--script>includeVideoJs();</script-->
<script>
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
var monitorName = "<?php echo validJsStr($Monitor->Name())?>";
var duration = <?php echo $Event->Length() ?>, startTime = '<?php echo $Event->StartTime() ?>';
addVideoTimingTrack(document.getElementById('videoobj'), LabelFormat, monitorName, duration, startTime);
</script>
</div><!--videoFeed-->
<?php
} // end if DefaultVideo
?>
<div id="imageFeed" <?php if ( $Event->DefaultVideo() ) { ?>class="hidden"<?php } ?> >
<?php if (!$Event->DefaultVideo()) { ?>
<div id="imageFeed">
<?php
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
$streamSrc = $Event->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode ) );
@ -181,34 +175,30 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
}
} // end if stream method
?>
</div>
<div id="alarmCue" class="alarmCue"></div>
<div id="progressBar" style="width: <?php echo reScale($Event->Width(), $scale);?>px;">
<div class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
</div><!--progressBar-->
</div><!--imageFeed-->
<?php } /*end if !DefaultVideo*/ ?>
<p id="dvrControls">
<input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
<input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true );"/>
<input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" onclick="streamFastRev( true );"/>
<input type="button" value="&lt;" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true );"/>
<input type="button" value="||" id="pauseBtn" title="<?php echo translate('Pause') ?>" class="inactive" onclick="pauseClicked();"/>
<input type="button" value="|>" id="playBtn" title="<?php echo translate('Play') ?>" class="active" disabled="disabled" onclick="playClicked();"/>
<input type="button" value="&gt;" id="slowFwdBtn" title="<?php echo translate('StepForward') ?>" class="unavail" disabled="disabled" onclick="streamSlowFwd( true );"/>
<input type="button" value="&gt;&gt;" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="inactive" disabled="disabled" onclick="streamFastFwd( true );"/>
<input type="button" value="&ndash;" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="avail" onclick="streamZoomOut();"/>
<input type="button" value="&gt;&gt;" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="inactive" onclick="streamFastFwd( true );"/>
<input type="button" value="&ndash;" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="unavail" disabled="disabled" onclick="streamZoomOut();"/>
<input type="button" value="+&gt;" id="nextBtn" title="<?php echo translate('Next') ?>" class="inactive" onclick="streamNext( true );"/>
</p>
<div id="replayStatus">
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue">&nbsp;</span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue"></span>s</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue">Replay</span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"><?php echo $rate/100 ?></span>x</span>
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue">0</span>s</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue">1</span>x</span>
</div>
<div id="progressBar" class="invisible">
<?php for ( $i = 0; $i < $panelSections; $i++ ) { ?>
<div class="progressBox" id="progressBox<?php echo $i ?>" title=""></div>
<?php } ?>
</div>
</div>
</div>
<?php
if ($Event->SaveJPEGs() & 3) { // frames or analysis
?>
</div><!--eventVideo-->
<div id="eventStills" class="hidden">
<div id="eventThumbsPanel">
<div id="eventThumbs">
@ -225,6 +215,13 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
</div>
</div>
<div id="eventImageNav">
<div id="thumbsSliderPanel">
<div id="alarmCue" class="alarmCue"></div>
<div id="thumbsSlider">
<div id="thumbsKnob">
</div>
</div>
</div>
<div id="eventImageButtons">
<div id="prevButtonsPanel">
<input id="prevEventBtn" type="button" value="&lt;E" onclick="prevEvent()" disabled="disabled"/>
@ -235,18 +232,12 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
<input id="nextEventBtn" type="button" value="E&gt;" onclick="nextEvent()" disabled="disabled"/>
</div>
</div>
<div id="thumbsSliderPanel">
<div id="thumbsSlider">
<div id="thumbsKnob">
</div>
</div>
</div>
</div>
</div>
<?php
} // end if SaveJPEGs() & 3 Analysis or Jpegs
} // end if Event exists
?>
</div><!--content-->
</div><!--page-->
</body>
</html>

View File

@ -51,7 +51,7 @@ $lastFid = $maxFid;
$alarmFrame = $Frame->Type()=='Alarm';
if ( isset( $_REQUEST['scale'] ) ) {
$scale = validInt($_REQUEST['scale']);
$scale = $_REQUEST['scale'];
} else if ( isset( $_COOKIE['zmWatchScale'.$Monitor->Id()] ) ) {
$scale = $_COOKIE['zmWatchScale'.$Monitor->Id()];
} else if ( isset( $_COOKIE['zmWatchScale'] ) ) {

View File

@ -88,7 +88,7 @@ $j( function() {
function applySort(event, ui) {
var monitor_ids = $j(this).sortable('toArray');
var ajax = new Request.JSON( {
url: '/index.php?request=console',
url: 'index.php?request=console',
data: { monitor_ids: monitor_ids, action: 'sort' },
method: 'post',
timeout: AJAX_TIMEOUT

View File

@ -1,31 +1,179 @@
var vid = null;
function vjsReplay() {
let endTime = (Date.parse(eventData.EndTime)).getTime();
switch(replayMode.value) {
case 'none':
break;
case 'single':
player.play();
break;
case 'all':
if (nextEventId == 0) {
let overLaid = $j("#videoobj");
overLaid.append('<p class="vjsMessage" style="height: '+overLaid.height()+'px; line-height: '+overLaid.height()+'px;">No more events</p>');
} else {
let nextStartTime = nextEventStartTime.getTime(); //nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse
if (nextStartTime <= endTime) {
streamNext( true );
return;
}
let overLaid = $j("#videoobj");
vid.pause();
overLaid.append('<p class="vjsMessage" style="height: '+overLaid.height()+'px; line-height: '+overLaid.height()+'px;"></p>');
let gapDuration = (new Date().getTime()) + (nextStartTime - endTime);
let messageP = $j(".vjsMessage");
let x = setInterval(function() {
let now = new Date().getTime();
let remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11,8);
messageP.html(remainder + ' to next event.');
if (remainder < 0) {
clearInterval(x);
streamNext( true );
}
}, 1000);
}
break;
case 'gapless':
streamNext( true );
break;
}
}
$j.ajaxSetup ({timeout: AJAX_TIMEOUT }); //sets timeout for all getJSON.
var cueFrames = null; //make cueFrames availaible even if we don't send another ajax query
function initialAlarmCues (eventId) {
$j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues); //get frames data for alarmCues and inserts into html
}
function setAlarmCues (data) {
cueFrames = data.frames;
alarmSpans = renderAlarmCues(vid ? $j("#videoobj") : $j("#evtStream"));//use videojs width or zms width
$j(".alarmCue").html(alarmSpans);
}
function renderAlarmCues (containerEl) {
if (cueFrames) {
var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100);
var minAlarm = Math.ceil(1/cueRatio);
var spanTimeStart = 0;
var spanTimeEnd = 0;
var alarmed = 0;
var alarmHtml = "";
var pixSkew = 0;
var skip = 0;
for (let i = 0; i < cueFrames.length; i++) {
skip = 0;
frame = cueFrames[i];
if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
alarmed = 1;
if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
let pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);//average out the rounding errors.
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span.
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue noneCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
futNone = 0;
indexPlus = i+1;
if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < cueFrames.length) continue; //alarm is too short and there is more event
while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
if (indexPlus >= cueFrames.length) break; //check if end of event.
futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100);
if (cueFrames[indexPlus].Type == "Alarm") {
i = --indexPlus;
skip = 1;
break;
}
indexPlus++;
}
if (skip == 1) continue; //javascript doesn't support continue 2;
spanTimeEnd = frame.Delta *100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) {
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = Math.round(cueRatio * spanTime);
if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew);
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
}
}
return alarmHtml;
}
}
function setButtonState( element, butClass ) {
if ( element ) {
element.className = butClass;
element.disabled = (butClass != 'inactive');
if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) {
element.disabled = true;
} else {
element.disabled = false;
}
} else {
console.log("Element was null in setButtonState");
}
}
function changeScale() {
var scale = $('scale').get('value');
var baseWidth = eventData.Width;
var baseHeight = eventData.Height;
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
if ( vid ) {
// Using video.js
vid.width = newWidth;
vid.height = newHeight;
let scale = $j('#scale').val();
let newWidth;
let newHeight;
let autoScale;
let eventViewer;
let alarmCue = $j('div.alarmCue');
let bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus');
if (streamMode == 'stills') {
eventViewer = $j('#eventThumbs');
} else {
streamScale( scale );
var streamImg = document.getElementById('evtStream');
streamImg.style.width = newWidth + "px";
streamImg.style.height = newHeight + "px";
Cookie.write( 'zmEventScale'+eventData.MonitorId, scale, { duration: 10*365 } );
eventViewer = $j(vid ? '#videoobj' : '#evtStream');
}
if (scale == "auto") {
let newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl);
newWidth = newSize.width;
newHeight = newSize.height;
autoScale = newSize.autoScale;
} else {
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
newWidth = eventData.Width * scale / SCALE_BASE;
newHeight = eventData.Height * scale / SCALE_BASE;
}
if (!(streamMode == 'stills')) eventViewer.width(newWidth); //stills handles its own width
eventViewer.height(newHeight);
if ( !vid ) { // zms needs extra sizing
streamScale(scale == "auto" ? autoScale : scale);
drawProgressBar();
}
if (streamMode == 'stills') {
slider.autosize();
alarmCue.html(renderAlarmCues($j('#thumbsSliderPanel')));
} else {
alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call
}
if (scale == "auto") {
Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365});
}else{
Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365});
Cookie.dispose('zmEventScaleAuto');
}
}
@ -42,33 +190,38 @@ var streamCmdTimer = null;
var streamStatus = null;
var lastEventId = 0;
var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors( "getCmdResponse", respObj ) )
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
console.log('Got an error from getCmdResponse');
zmsBroke = true;
return;
}
zmsBroke = false;
if ( streamCmdTimer )
streamCmdTimer = clearTimeout( streamCmdTimer );
streamStatus = respObj.status;
if (streamStatus.progress >= Math.round(parseFloat(eventData.Length))) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
var eventId = streamStatus.event;
if ( eventId != lastEventId ) {
if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax
eventQuery( eventId );
initialAlarmCues(eventId);
lastEventId = eventId;
}
if (lastEventId == 0) lastEventId = eventId; //Only fires on first load.
if ( streamStatus.paused == true ) {
$('modeValue').set( 'text', 'Paused' );
$('rate').addClass( 'hidden' );
streamPause( );
} else {
$('modeValue').set( 'text', "Replay" );
$('rateValue').set( 'text', streamStatus.rate );
$('rate').removeClass( 'hidden' );
$j('#rateValue').html(streamStatus.rate);
streamPlay( );
}
$('progressValue').set( 'text', secsToTime( parseInt(streamStatus.progress) ) );
$('zoomValue').set( 'text', streamStatus.zoom );
$j('#progressValue').html(secsToTime(parseInt(streamStatus.progress)));
$j('#zoomValue').html(streamStatus.zoom);
if ( streamStatus.zoom == "1.0" )
setButtonState( $('zoomOutBtn'), 'unavail' );
else
@ -83,17 +236,29 @@ function getCmdResponse( respObj, respText ) {
streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth );
} // end if haev a new auth hash
streamCmdTimer = streamQuery.delay( streamTimeout );
streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display
}
var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } );
function pauseClicked( ) {
function pauseClicked() {
if (vid) {
vid.pause();
} else {
streamReq.send( streamParms+"&command="+CMD_PAUSE );
streamPause();
}
}
function vjsPause () {
stopFastRev();
streamPause();
}
// Called when stream becomes paused, just updates the button status
function streamPause( ) {
$j('#modeValue').html('Paused');
$j('#rateValue').html('0');
setButtonState( $('pauseBtn'), 'active' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'unavail' );
@ -103,13 +268,28 @@ function streamPause( ) {
}
function playClicked( ) {
if (vid) {
if (vid.paused()) {
vid.play();
} else {
vjsPlay(); //handles fast forward and rewind
}
} else {
streamReq.send( streamParms+"&command="+CMD_PLAY );
streamPlay();
}
}
function vjsPlay () { //catches if we change mode programatically
stopFastRev();
$j('#rateValue').html(vid.playbackRate());
streamPlay();
}
function streamPlay( ) {
$j('#modeValue').html('Replay');
setButtonState( $('pauseBtn'), 'inactive' );
if (streamStatus)
setButtonState( $('playBtn'), streamStatus.rate==1?'active':'inactive' );
setButtonState( $('playBtn'), 'active' );
setButtonState( $('fastFwdBtn'), 'inactive' );
setButtonState( $('slowFwdBtn'), 'unavail' );
setButtonState( $('slowRevBtn'), 'unavail' );
@ -119,35 +299,44 @@ function streamPlay( ) {
function streamFastFwd( action ) {
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'active' );
setButtonState( $('slowFwdBtn'), 'unavail' );
setButtonState( $('slowRevBtn'), 'unavail' );
setButtonState( $('fastRevBtn'), 'inactive' );
if (vid) {
if (revSpeed != .5) stopFastRev();
vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100);
if (rates.indexOf(vid.playbackRate()*100)-1 == -1) setButtonState($('fastFwdBtn'), 'unavail');
$j('#rateValue').html(vid.playbackRate());
} else {
streamReq.send( streamParms+"&command="+CMD_FASTFWD );
}
}
var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame.
var intervalRewind;
var revSpeed = .5;
function streamSlowFwd( action ) {
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'unavail' );
setButtonState( $('slowFwdBtn'), 'active' );
setButtonState( $('slowRevBtn'), 'inactive' );
setButtonState( $('fastRevBtn'), 'unavail' );
if (vid) {
vid.currentTime(vid.currentTime() + spf);
} else {
streamReq.send( streamParms+"&command="+CMD_SLOWFWD );
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('slowFwdBtn'), 'inactive' );
}
}
function streamSlowRev( action ) {
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'unavail' );
setButtonState( $('slowFwdBtn'), 'inactive' );
setButtonState( $('slowRevBtn'), 'active' );
setButtonState( $('fastRevBtn'), 'unavail' );
if (vid) {
vid.currentTime(vid.currentTime() - spf);
} else {
streamReq.send( streamParms+"&command="+CMD_SLOWREV );
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('slowRevBtn'), 'inactive' );
}
}
function stopFastRev () {
clearInterval(intervalRewind);
vid.playbackRate(1);
revSpeed = .5;
}
function streamFastRev( action ) {
@ -156,26 +345,121 @@ function streamFastRev( action ) {
setButtonState( $('fastFwdBtn'), 'inactive' );
setButtonState( $('slowFwdBtn'), 'unavail' );
setButtonState( $('slowRevBtn'), 'unavail' );
setButtonState( $('fastRevBtn'), 'inactive' );
setButtonState( $('fastRevBtn'), 'active' );
if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back.
revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100;
if (rates.indexOf(revSpeed*100) == 0) {
setButtonState( $('fastRevBtn'), 'unavail' );
}
clearInterval(intervalRewind);
$j('#rateValue').html(-revSpeed);
intervalRewind = setInterval(function() {
if (vid.currentTime() <= 0) {
clearInterval(intervalRewind);
vid.pause();
} else {
vid.playbackRate(0);
vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms.
}
}, 500); //500ms is a compromise between smooth reverse and realistic performance
} else {
streamReq.send( streamParms+"&command="+CMD_FASTREV );
}
}
function streamPrev( action ) {
if ( action )
streamReq.send( streamParms+"&command="+CMD_PREV );
function streamPrev(action) {
if (action) {
$j(".vjsMessage").remove();
if (vid && PrevEventDefVideoPath.indexOf("view_video") > 0) {
CurEventDefVideoPath = PrevEventDefVideoPath;
eventQuery(prevEventId);
} else if (zmsBroke || (vid && PrevEventDefVideoPath.indexOf("view_video") < 0) || $j("#vjsMessage").length || PrevEventDefVideoPath.indexOf("view_video") > 0) {//zms broke, leaving videojs, last event, moving to videojs
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
} else {
streamReq.send(streamParms+"&command="+CMD_PREV);
streamPlay();
}
}
}
function streamNext( action ) {
if ( action )
streamReq.send( streamParms+"&command="+CMD_NEXT );
function streamNext(action) {
if (action) {
$j(".vjsMessage").remove();//This shouldn't happen
if (nextEventId == 0) { //handles deleting last event.
pauseClicked();
let hideContainer = $j('#eventVideo');
let hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height());
hideContainer.prepend('<p class="vjsMessage" style="height: ' + hideStream + 'px; line-height: ' + hideStream + 'px;">No more events</p>');
if (vid == null) zmsBroke = true;
return;
}
if (vid && NextEventDefVideoPath.indexOf("view_video") > 0) { //on and staying with videojs
CurEventDefVideoPath = NextEventDefVideoPath;
eventQuery(nextEventId);
} else if (zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
} else {
streamReq.send(streamParms+"&command="+CMD_NEXT);
streamPlay();
}
}
}
function vjsPanZoom (action, x, y) { //Pan and zoom with centering where the click occurs
let outer = $j('#videoobj');
let video = outer.children().first();
let zoom = parseFloat($j('#zoomValue').html());
let zoomRate = .5;
let matrix = video.css('transform').split(',');
let currentPanX = parseFloat(matrix[4]);
let currentPanY = parseFloat(matrix[5]);
let xDist = outer.width()/2 - x //Click distance from center of view
let yDist = outer.height()/2 - y
if (action == 'zoomOut') {
zoom -= zoomRate;
if (x && y) {
x = (xDist + currentPanX)*((zoom-zoomRate)/zoom); // if ctrl-click Pan but use ratio of old zoom to new zoom for coords
y = (yDist + currentPanY)*((zoom-zoomRate)/zoom);
} else {
x = currentPanX*((zoom-zoomRate)/zoom); //Leave zoom centered where it was
y = currentPanY*((zoom-zoomRate)/zoom);
}
if (zoom <= 1) {
zoom = 1;
$j('#zoomOutBtn').attr('class', 'unavail').attr('disabled', 'disabled');
}
$j('#zoomValue').html(zoom);
} else if (action == 'zoom') {
zoom += zoomRate;
x = (xDist + currentPanX)*(zoom/(zoom-zoomRate)); //Pan but use ratio of new zoom to old zoom for coords. Center on mouse click.
y = (yDist + currentPanY)*(zoom/(zoom-zoomRate));
$j('#zoomOutBtn').attr('class', 'inactive').removeAttr('disabled');
$j('#zoomValue').html(zoom);
} else if (action == 'pan') {
x = xDist + currentPanX;
y = yDist + currentPanY;
}
let limitX = ((zoom*outer.width()) - outer.width())/2; //Calculate outer bounds of video
let limitY = ((zoom*outer.height()) - outer.height())/2;
x = Math.min(Math.max((x),-limitX),limitX); //Limit pan to outer bounds of video
y = Math.min(Math.max((y),-limitY),limitY);
video.css('transform', 'matrix('+zoom+', 0, 0, '+zoom+', '+x+', '+y+')');
}
function streamZoomIn( x, y ) {
if (vid) {
vjsPanZoom('zoom', x, y);
} else {
streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y );
}
}
function streamZoomOut() {
if (vid) {
vjsPanZoom('zoomOut');
} else {
streamReq.send( streamParms+"&command="+CMD_ZOOMOUT );
}
}
function streamScale( scale ) {
@ -183,7 +467,11 @@ function streamScale( scale ) {
}
function streamPan( x, y ) {
if (vid) {
vjsPanZoom('pan', x, y);
} else {
streamReq.send( streamParms+"&command="+CMD_PAN+"&x="+x+"&y="+y );
}
}
function streamSeek( offset ) {
@ -196,6 +484,8 @@ function streamQuery() {
var slider = null;
var scroll = null;
var currEventId = null;
var CurEventDefVideoPath = null;
function getEventResponse( respObj, respText ) {
if ( checkStreamForErrors( "getEventResponse", respObj ) )
@ -220,7 +510,7 @@ function getEventResponse( respObj, respText ) {
$('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames );
$('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore );
$('eventName').setProperty( 'value', eventData.Name );
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery);//if popup removed, check if this allows forward
if ( canEditEvents ) {
if ( parseInt(eventData.Archived) ) {
$('archiveEvent').addClass( 'hidden' );
@ -232,7 +522,18 @@ function getEventResponse( respObj, respText ) {
}
//var eventImg = $('eventImage');
//eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } );
if (vid && CurEventDefVideoPath) {
vid.src({type: 'video/mp4', src: CurEventDefVideoPath}); //Currently mp4 is all we use
initialAlarmCues(eventData.Id);//ajax and render, new event
addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime);
CurEventDefVideoPath = null;
$j('#modeValue').html('Replay');
$j('#zoomValue').html('1');
$j('#rateValue').html('1');
vjsPanZoom('zoomOut');
} else {
drawProgressBar();
}
nearEventsQuery( eventData.Id );
}
@ -245,6 +546,8 @@ function eventQuery( eventId ) {
var prevEventId = 0;
var nextEventId = 0;
var prevEventStartTime = 0;
var nextEventStartTime = 0;
var PrevEventDefVideoPath = "";
var NextEventDefVideoPath = "";
@ -253,6 +556,8 @@ function getNearEventsResponse( respObj, respText ) {
return;
prevEventId = respObj.nearevents.PrevEventId;
nextEventId = respObj.nearevents.NextEventId;
prevEventStartTime = Date.parse(respObj.nearevents.PrevEventStartTime);
nextEventStartTime = Date.parse(respObj.nearevents.NextEventStartTime);
PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath;
NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath;
@ -260,12 +565,14 @@ function getNearEventsResponse( respObj, respText ) {
if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId;
var nextEventBtn = $('nextEventBtn');
if ( nextEventBtn ) nextEventBtn.disabled = !nextEventId;
$j('#prevBtn').prop('disabled', prevEventId == 0 ? true : false).attr('class', prevEventId == 0 ? 'unavail' : 'inactive');
$j('#nextBtn').prop('disabled', nextEventId == 0 ? true : false).attr('class', nextEventId == 0 ? 'unavail' : 'inactive');
}
var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } );
function nearEventsQuery( eventId ) {
var parms = "view=request&request=status&entity=nearevents&id="+eventId;
var parms = "view=request&request=status&entity=nearevents&id="+eventId+filterQuery+sortQuery;
nearEventsReq.send( parms );
}
@ -277,7 +584,7 @@ function loadEventThumb( event, frame, loadImage ) {
console.error( "No holder found for frame "+frame.FrameId );
return;
}
var img = new Asset.image( imagePrefix+frame.Image.imagePath,
var img = new Asset.image( imagePrefix+frame.EventId+"&fid="+frame.FrameId,
{
'onload': ( function( loadImage ) {
thumbImg.setProperty( 'src', img.getProperty( 'src' ) );
@ -293,29 +600,6 @@ function loadEventThumb( event, frame, loadImage ) {
);
}
function updateStillsSizes( noDelay ) {
var containerDim = $('eventThumbs').getSize();
var containerWidth = containerDim.x;
var containerHeight = containerDim.y;
var popupWidth = parseInt($('eventImage').getStyle( 'width' ));
var popupHeight = parseInt($('eventImage').getStyle( 'height' ));
var left = (containerWidth - popupWidth)/2;
if ( left < 0 ) left = 0;
var top = (containerHeight - popupHeight)/2;
if ( top < 0 ) top = 0;
if ( popupHeight == 0 && !noDelay ) {
// image not yet loaded lets give it another second
updateStillsSizes.pass( true ).delay( 50 );
return;
}
$('eventImagePanel').setStyles( {
'left': left,
'top': top
} );
}
function loadEventImage( event, frame ) {
console.debug( "Loading "+event.Id+"/"+frame.FrameId );
var eventImg = $('eventImage');
@ -329,14 +613,6 @@ function loadEventImage( event, frame ) {
lastThumbImg.setOpacity( 1.0 );
}
eventImg.setProperties( {
'class': frame.Type=='Alarm'?'alarm':'normal',
'src': thumbImg.getProperty( 'src' ),
'title': thumbImg.getProperty( 'title' ),
'alt': thumbImg.getProperty( 'alt' ),
'width': event.Width,
'height': event.Height
} );
$('eventImageBar').setStyle( 'width', event.Width );
if ( frame.Type=='Alarm' )
$('eventImageStats').removeClass( 'hidden' );
@ -347,11 +623,18 @@ function loadEventImage( event, frame ) {
if ( eventImagePanel.getStyle( 'display' ) == 'none' ) {
eventImagePanel.setOpacity( 0 );
updateStillsSizes();
eventImagePanel.setStyle( 'display', 'block' );
eventImagePanel.setStyle( 'display', 'inline-block' );
new Fx.Tween( eventImagePanel, { duration: 500, transition: Fx.Transitions.Sine } ).start( 'opacity', 0, 1 );
}
eventImg.setProperties( {
'class': frame.Type=='Alarm'?'alarm':'normal',
'src': thumbImg.getProperty( 'src' ),
'title': thumbImg.getProperty( 'title' ),
'alt': thumbImg.getProperty( 'alt' ),
'height': $j('#eventThumbs').height() - $j('#eventImageBar').outerHeight(true)-10
} );
$('eventImageNo').set( 'text', frame.FrameId );
$('prevImageBtn').disabled = (frame.FrameId==1);
$('nextImageBtn').disabled = (frame.FrameId==event.Frames);
@ -392,13 +675,11 @@ function resetEventStills() {
fid = 1;
else if ( fid > eventData.Frames )
fid = eventData.Frames;
checkFrames( eventData.Id, fid );
checkFrames( eventData.Id, fid, ($j('#eventImagePanel').css('display')=='none'?'':'true'));
scroll.toElement( 'eventThumb'+fid );
}
} ).set( 0 );
}
if ( $('eventThumbs').getStyle( 'height' ).match( /^\d+/ ) < (parseInt(eventData.Height)+80) )
$('eventThumbs').setStyle( 'height', (parseInt(eventData.Height)+80)+'px' );
}
function getFrameResponse( respObj, respText ) {
@ -548,6 +829,7 @@ function actQuery( action, parms ) {
}
function deleteEvent() {
pauseClicked(); //Provides visual feedback that your click happened.
actQuery( 'delete' );
streamNext( true );
}
@ -577,20 +859,19 @@ function showEventFrames() {
createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames' );
}
function showVideo() {
function showStream() {
$('eventStills').addClass( 'hidden' );
$('imageFeed').addClass('hidden');
$('eventVideo').removeClass( 'hidden' );
$('stillsEvent').removeClass( 'hidden' );
$('videoEvent').addClass( 'hidden' );
$('streamEvent').addClass( 'hidden' );
streamMode = 'video';
if (scale == 'auto') changeScale();
}
function showStills() {
$('eventStills').removeClass( 'hidden' );
$('imageFeed').removeClass('hidden');
$('eventVideo').addClass( 'hidden' );
if (vid && ( vid.paused != true ) ) {
@ -603,11 +884,11 @@ function showStills() {
}
$('stillsEvent').addClass( 'hidden' );
$('videoEvent').removeClass( 'hidden' );
$('streamEvent').removeClass( 'hidden' );
streamMode = 'stills';
streamPause( true );
pauseClicked();
if ( !scroll ) {
scroll = new Fx.Scroll( 'eventThumbs', {
wait: false,
@ -618,7 +899,7 @@ function showStills() {
);
}
resetEventStills();
$(window).addEvent( 'resize', updateStillsSizes );
if (scale == 'auto') changeScale();
}
function showFrameStats() {
@ -630,184 +911,64 @@ function videoEvent() {
createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height );
}
// Called on each event load because each event can be a different width
function drawProgressBar() {
var barWidth = 0;
$('progressBar').addClass( 'invisible' );
var cells = $('progressBar').getElements( 'div' );
var cellWidth = parseInt( eventData.Width/$$(cells).length );
$$(cells).forEach(
function( cell, index ) {
if ( index == 0 )
$(cell).setStyles( { 'left': barWidth, 'width': cellWidth, 'borderLeft': 0 } );
else
$(cell).setStyles( { 'left': barWidth, 'width': cellWidth } );
var offset = parseInt((index*eventData.Length)/$$(cells).length);
$(cell).setProperty( 'title', '+'+secsToTime(offset)+'s' );
$(cell).removeEvent( 'click' );
$(cell).addEvent( 'click', function() { streamSeek( offset ); } );
barWidth += $(cell).getCoordinates().width;
}
);
$('progressBar').setStyle( 'width', barWidth );
$('progressBar').removeClass( 'invisible' );
let barWidth = $j('#evtStream').width();
$j('#progressBar').css( 'width', barWidth );
}
// Shows current stream progress.
function updateProgressBar() {
if ( eventData && streamStatus ) {
var cells = $('progressBar').getElements( 'div' );
var completeIndex = parseInt((($$(cells).length+1)*streamStatus.progress)/eventData.Length);
$$(cells).forEach(
function( cell, index ) {
if ( index < completeIndex ) {
if ( !$(cell).hasClass( 'complete' ) ) {
$(cell).addClass( 'complete' );
}
} else {
if ( $(cell).hasClass( 'complete' ) ) {
$(cell).removeClass( 'complete' );
}
} // end if
} // end function
);
//$('progressBar').setStyle( 'width', barWidth );
$('progressBar').removeClass( 'invisible' );
} // end if eventData && streamStatus
if ( ! ( eventData && streamStatus ) ) {
return;
} // end if ! eventData && streamStatus
var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100;
$j("#progressBox").css('width', curWidth + '%');
} // end function updateProgressBar()
// Handles seeking when clicking on the progress bar.
function progressBarNav (){
$j('#progressBar').click(function(e){
var x = e.offsetX;
var seekTime = (x / $j('#progressBar').width()) * parseFloat(eventData.Length);
streamSeek (seekTime);
});
}
function handleClick( event ) {
var target = event.target;
if (vid) {
if (target.id != 'videoobj') return; //ignore clicks on control bar
var x = event.offsetX;
var y = event.offsetY;
} else {
var x = event.page.x - $(target).getLeft();
var y = event.page.y - $(target).getTop();
}
if ( event.shift )
streamPan( x, y );
else
streamZoomIn( x, y );
}
function setupListener() {
// Buttons
var playButton = document.getElementById("play-pause");
var muteButton = document.getElementById("mute");
var fullScreenButton = document.getElementById("full-screen");
// Sliders
var seekBar = document.getElementById("seekbar");
var volumeBar = document.getElementById("volume-bar");
// Event listener for the play/pause button
playButton.addEventListener( "click", function() {
if (vid.paused == true) {
// Play the video
vid.play();
// Update the button text to 'Pause'
playButton.innerHTML = "Pause";
if (event.shift || event.shiftKey) {//handle both jquery and mootools
streamPan(x, y);
} else if (vid && event.ctrlKey) { //allow zoom out by control click. useful in fullscreen
vjsPanZoom('zoomOut', x, y);
} else {
// Pause the video
vid.pause();
// Update the button text to 'Play'
playButton.innerHTML = "Play";
streamZoomIn(x, y);
}
});
// Event listener for the mute button
muteButton.addEventListener("click", function() {
if (vid.muted == false) {
// Mute the video
vid.muted = true;
// Update the button text
muteButton.innerHTML = "Unmute";
} else {
// Unmute the video
vid.muted = false;
// Update the button text
muteButton.innerHTML = "Mute";
}
});
// Event listener for the full-screen button
fullScreenButton.addEventListener("click", function() {
if (vid.requestFullscreen) {
vid.requestFullscreen();
} else if (vid.mozRequestFullScreen) {
vid.mozRequestFullScreen(); // Firefox
} else if (vid.webkitRequestFullscreen) {
vid.webkitRequestFullscreen(); // Chrome and Safari
}
});
// Event listener for the seek bar
seekBar.addEventListener("change", function() {
// Calculate the new time
var time = vid.duration * (seekBar.value / 100);
// Update the video time
vid.currentTime = time;
});
// Update the seek bar as the video plays
vid.addEventListener("timeupdate", function() {
// Calculate the slider value
var value = (100 / vid.duration) * vid.currentTime;
// Update the slider value
seekBar.value = value;
});
// Pause the video when the seek handle is being dragged
seekBar.addEventListener("mousedown", function() {
vid.pause();
});
// Play the video when the seek handle is dropped
seekBar.addEventListener("mouseup", function() {
vid.play();
});
// Event listener for the volume bar
volumeBar.addEventListener("change", function() {
// Update the video volume
vid.volume = volumeBar.value;
});
}
function initPage() {
//FIXME prevent blocking...not sure what is happening or best way to unblock
if ( $('videoobj') ) {
vid = videojs("videoobj");
}
if ( vid ) {
/*
setupListener();
vid.removeAttribute("controls");
/* window.videoobj.oncanplay=null;
window.videoobj.currentTime=window.videoobj.currentTime-1;
window.videoobj.currentTime=window.videoobj.currentTime+1;//may not be symetrical of course
vid.onstalled=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
vid.onwaiting=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
vid.onloadstart=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
vid.onplay=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
vid.onplaying=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
//window.vid.hide();//does not help
var sources = window.videoobj.getElementsByTagName('source');
sources[0].src=null;
window.videoobj.load();
streamPlay(); */
if ($j('#videoobj').length) {
vid = videojs('videoobj');
addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime);
$j('.vjs-progress-control').append('<div class="alarmCue"></div>');//add a place for videojs only on first load
vid.on('ended', vjsReplay);
vid.on('play', vjsPlay);
vid.on('pause', vjsPause);
vid.on('click', function(event){handleClick(event);});
vid.on('timeupdate', function (){$j('#progressValue').html(secsToTime(Math.floor(vid.currentTime())))});
} else {
progressBarNav ();
streamCmdTimer = streamQuery.delay( 250 );
eventQuery.pass( eventData.Id ).delay( 500 );
if ( canStreamNative ) {
var streamImg = $('imageFeed').getElement('img');
if ( !streamImg )
@ -815,6 +976,9 @@ function initPage() {
$(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } );
}
}
nearEventsQuery(eventData.Id);
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues
if (scale == "auto") changeScale();
}
// Kick everything off

View File

@ -30,17 +30,25 @@ var eventData = {
MonitorId: '<?php echo $Event->MonitorId() ?>',
Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>',
Length: '<?php echo $Event->Length() ?>'
Length: '<?php echo $Event->Length() ?>',
StartTime: '<?php echo $Event->StartTime() ?>',
EndTime: '<?php echo $Event->EndTime() ?>',
Frames: '<?php echo $Event->Frames() ?>',
MonitorName: '<?php echo $Monitor->Name() ?>'
};
var filterQuery = '<?php echo isset($filterQuery)?validJsStr($filterQuery):'' ?>';
var sortQuery = '<?php echo isset($sortQuery)?validJsStr($sortQuery):'' ?>';
var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
var rates = <?php echo json_encode(array_keys($rates)) ?>;
var scale = "<?php echo $scale ?>";
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
var scale = <?php echo $scale ?>;
var canEditEvents = <?php echo canEdit( 'Events' )?'true':'false' ?>;
var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
var streamMode = '<?php echo $streamMode ?>';
//
// Strings

View File

@ -1,15 +1,34 @@
function changeScale() {
var scale = $('scale').get('value');
var img = $('frameImg');
if ( img ) {
var baseWidth = $('base_width').value;
var baseHeight = $('base_height').value;
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
let scale = $j('#scale').val();
let img = $j('#frameImg');
let controlsLinks = {
next: $j('#nextLink'),
prev: $j('#prevLink'),
first: $j('#firstLink'),
last: $j('#lastLink')
}
img.style.width = newWidth + "px";
img.style.height = newHeight + "px";
if (img) {
let baseWidth = $j('#base_width').val();
let baseHeight = $j('#base_height').val();
if (scale == "auto") {
let newSize = scaleToFit(baseWidth, baseHeight, img, $j('#controls'));
newWidth = newSize.width;
newHeight = newSize.height;
autoScale = newSize.autoScale;
} else {
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
newWidth = baseWidth * scale / SCALE_BASE;
newHeight = baseHeight * scale / SCALE_BASE;
}
img.css('width', newWidth + "px");
img.css('height', newHeight + "px");
}
Cookie.write( 'zmWatchScale', scale, { duration: 10*365 } );
$j.each(controlsLinks, function(k, anchor) { //Make frames respect scale choices
anchor.prop('href', anchor.prop('href').replace(/scale=.*&/, 'scale=' + scale + '&'));
});
}
if (scale == "auto") $j(document).ready(changeScale);

View File

@ -1,2 +1,3 @@
var scale = '<?php echo $scale ?>';
var SCALE_BASE = <?php echo SCALE_BASE ?>;

View File

@ -286,7 +286,7 @@ sortReverse: true
warningPrefix: "",
errorPrefix: ""
});
new Asset.css( "/css/spinner.css" );
new Asset.css( "css/spinner.css" );
fetchNextLogs();
}

View File

@ -25,18 +25,29 @@ function showPtzControls() {
function changeScale() {
var scale = $('scale').get('value');
var newWidth = ( monitorWidth * scale ) / SCALE_BASE;
var newHeight = ( monitorHeight * scale ) / SCALE_BASE;
var newWidth;
var newHeight;
if (scale == "auto") {
let newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
newWidth = newSize.width;
newHeight = newSize.height;
autoScale = newSize.autoScale;
} else {
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
newWidth = monitorWidth * scale / SCALE_BASE;
newHeight = monitorHeight * scale / SCALE_BASE;
}
Cookie.write( 'zmWatchScale'+monitorId, scale, { duration: 10*365 } );
/*Stream could be an applet so can't use moo tools*/
var streamImg = document.getElementById('liveStream'+monitorId);
var streamImg = $('liveStream'+monitorId);
if ( streamImg ) {
streamImg.style.width = newWidth + "px";
streamImg.style.height = newHeight + "px";
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+scale);
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
} else {
console.error("No element found for liveStream.");
}
@ -622,6 +633,7 @@ function initPage() {
if ( refreshApplet && appletRefreshTime )
appletRefresh.delay( appletRefreshTime*1000 );
if (scale == "auto") changeScale();
}
// Kick everything off

View File

@ -50,7 +50,7 @@ var monitorWidth = <?php echo $monitor->Width() ?>;
var monitorHeight = <?php echo $monitor->Height() ?>;
var monitorUrl = '<?php echo ( $monitor->Server()->Url() ) ?>';
var scale = <?php echo $scale ?>;
var scale = '<?php echo $scale ?>';
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
var eventsRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_EVENTS ?>;

View File

@ -66,8 +66,19 @@ if ( empty($_REQUEST['path']) ) {
$Event = new Event( $_REQUEST['eid'] );
$Frame = Frame::find_one( array( 'EventId' => $_REQUEST['eid'], 'FrameId' => $_REQUEST['fid'] ) );
if ( ! $Frame ) {
$previousBulkFrame = dbFetchOne( "SELECT * FROM Frames WHERE EventId=? AND FrameId < ? ORDER BY FrameID DESC LIMIT 1", NULL, array($_REQUEST['eid'], $_REQUEST['fid'] ) );
$nextBulkFrame = dbFetchOne( "SELECT * FROM Frames WHERE EventId=? AND FrameId > ? ORDER BY FrameID ASC LIMIT 1", NULL, array($_REQUEST['eid'], $_REQUEST['fid'] ) );
if ( $previousBulkFrame and $nextBulkFrame ) {
$Frame = new Frame( $previousBulkFrame );
$Frame->FrameId = ( $_REQUEST['fid'] );
$percentage = ($Frame->FrameId() - $previousBulkFrame['FrameId']) / ($nextBulkFrame['FrameId'] - $previousBulkFrame['FrameId']);
$Frame->Delta = ( $previousBulkFrame['Delta'] + floor( 100* ( $nextBulkFrame['Delta'] - $previousBulkFrame['Delta'] ) * $percentage )/100 );
Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousBulkFrame['Delta'] . " + nextdelta:" . $nextBulkFrame['Delta'] . ' - ' . $previousBulkFrame['Delta'] . ' * ' . $percentage );
} else {
Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")");
}
}
// Frame can be non-existent. We have Bulk frames. So now we should try to load the bulk frame
} else {
# If we are only specifying fid, then the fid must be the primary key into the frames table. But when the event is specified, then it is the frame #
$Frame = new Frame( $_REQUEST['fid'] );

View File

@ -1,25 +1,37 @@
#!/bin/bash
# The purpose of this file is to create the symlinks in the web folder to the content folder. It can use an existing content folder or create a new one.
# This tool is used to verify folders critical to ZoneMinder exist and have the right permissions.
# It will also create symlinks when necessary. It can use an existing content folder or create a new one.
# Set the content dir default to be the one supplied to cmake
ZM_PATH_CONTENT="@ZM_CONTENTDIR@"
# Set the zoneminder log dir default to be the one supplied to cmake
ZM_LOGDIR="@ZM_LOGDIR@"
# Set the zoneminder temp dir default to be the one supplied to cmake
ZM_TMPDIR="@ZM_TMPDIR@"
echo "*** This bash script creates the nessecary symlinks for the zoneminder content"
echo "*** It can use an existing content folder or create a new one"
echo "*** For usage: use -h"
echo "*** The default content directory is: $ZM_PATH_CONTENT"
echo "*** The default log directory is: $ZM_LOGDIR"
echo "*** The default temp directory is: $ZM_TMPDIR"
echo ""
usage()
{
cat <<EOF
Usage: $0 [-q] [-z zm.conf] [-w WEB DIRECTORY] [CONTENT DIRECTORY]
Usage: $0 [-q] [-o] [-z zm.conf] [-w WEB DIRECTORY] [-l LOG DIRECTORY] [-t TMP DIRECTORY] [CONTENT DIRECTORY]
OPTIONS:
-h Show this message and quit
-z ZoneMinder configuration file
-w Override the web directory from zm.conf
-q Quick mode. Do not change ownership recursively.
-l Override the zm log folder location
-t Override the zm temp folder location
-o Enable old legacy symlinks inside ZoneMinder's webroot folder
If the -w option is not used to specify the path to the web directory,
the script will use the path from zoneminder's configuration file.
@ -27,10 +39,14 @@ If the -z option is used, the argument will be used instead of zm.conf
Otherwise, it will attempt to read zm.conf from the local directory.
If that fails, it will try from /etc/zm.conf
Newer versions of ZoneMinder no longer require symlinks to the events and
images folders inside the zm webroot. Indeed this is a security risk, and
$0 will no longer create these unless the -o option is specified.
EOF
}
while getopts "hz:w:q" OPTION
while getopts "hz:w:q:l:t:o" OPTION
do
case $OPTION in
h)
@ -46,6 +62,15 @@ do
q)
QUICK=1
;;
l)
ZM_LOGDIR_FORCE=$OPTARG
;;
t)
ZM_TMPDIR_FORCE=$OPTARG
;;
o)
LEGACY=1
;;
esac
done
shift $(( OPTIND - 1 ))
@ -73,7 +98,7 @@ elif [ -f "/etc/zm.conf" ]; then
echo "Using system zm.conf"
source "/etc/zm.conf"
else
echo "Failed locating zoneminder configuration file (zm.conf)\nUse the -z option to specify the full path to the zoneminder configuration file"
echo -e "Failed locating zoneminder configuration file (zm.conf)\nUse the -z option to specify the full path to the zoneminder configuration file"
exit 45
fi
@ -82,6 +107,16 @@ if [ -n "$ZM_PATH_WEB_FORCE" ]; then
ZM_PATH_WEB="$(readlink -f $ZM_PATH_WEB_FORCE)"
fi
# Override the log directory path
if [ -n "$ZM_LOGDIR_FORCE" ]; then
ZM_LOGDIR="$(readlink -f $ZM_LOGDIR_FORCE)"
fi
# Override the tmp directory path
if [ -n "$ZM_TMPDIR_FORCE" ]; then
ZM_TMPDIR="$(readlink -f $ZM_TMPDIR_FORCE)"
fi
# Override the default content path
if [[ -n "$@" ]]; then
ZM_PATH_CONTENT="$(readlink -f $@)"
@ -90,6 +125,8 @@ fi
# Print some information
echo "Web folder : $ZM_PATH_WEB"
echo "Content folder : $ZM_PATH_CONTENT"
echo "Log folder : $ZM_LOGDIR"
echo "Temp folder : $ZM_TMPDIR"
echo ""
# Verify the web folder is a real directory
@ -98,7 +135,7 @@ if [ -d "$ZM_PATH_WEB" ]; then
echo "OK"
else
echo "Failed"
exit 3
exit 2
fi
# Check if the content folder exists, and if not, create it
@ -109,6 +146,22 @@ else
echo "No"
echo -n "Creating the content folder... "
mkdir "$ZM_PATH_CONTENT"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 3
fi
fi
# Check if the log folder exists, and if not, create the entire folder including its parents
echo -n "Checking if the log folder exists... "
if [ -d "$ZM_LOGDIR" ]; then
echo "Yes"
else
echo "No"
echo -n "Creating the log folder... "
mkdir -p "$ZM_LOGDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
@ -116,6 +169,23 @@ else
exit 4
fi
fi
# Check if the temp folder exists, and if not, create the entire folder including its parents
echo -n "Checking if the temp folder exists... "
if [ -d "$ZM_TMPDIR" ]; then
echo "Yes"
else
echo "No"
echo -n "Creating the temp folder... "
mkdir -p "$ZM_TMPDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 5
fi
fi
# Check if the content/images folder exists, and if not, create it
echo -n "Checking if the images folder exists inside the content folder... "
if [ -d "$ZM_PATH_CONTENT/images" ]; then
@ -131,6 +201,7 @@ else
exit 6
fi
fi
# Check if the content/events folder exists, and if not, create it
echo -n "Checking if the events folder exists inside the content folder... "
if [ -d "$ZM_PATH_CONTENT/events" ]; then
@ -179,24 +250,26 @@ if [ -d "$ZM_PATH_WEB/events" ]; then
fi
fi
# Create the symlink for the images folder
echo -n "Creating the symlink for the images folder... "
ln -s -f "$ZM_PATH_CONTENT/images" "$ZM_PATH_WEB/images"
if [ "$?" = "0" ]; then
if [ -n "$LEGACY" ]; then
# Create the symlink for the images folder
echo -n "Creating the symlink for the images folder... "
ln -s -f "$ZM_PATH_CONTENT/images" "$ZM_PATH_WEB/images"
if [ "$?" = "0" ]; then
echo "OK"
else
else
echo "Failed"
exit 15
fi
fi
# Create the symlink for the events folder
echo -n "Creating the symlink for the events folder... "
ln -s -f "$ZM_PATH_CONTENT/events" "$ZM_PATH_WEB/events"
if [ "$?" = "0" ]; then
# Create the symlink for the events folder
echo -n "Creating the symlink for the events folder... "
ln -s -f "$ZM_PATH_CONTENT/events" "$ZM_PATH_WEB/events"
if [ "$?" = "0" ]; then
echo "OK"
else
else
echo "Failed"
exit 16
fi
fi
# change ownership for the images folder. do it recursively unless -q is used
@ -228,11 +301,32 @@ if [ -n "$QUICK" ]; then
echo "OK"
else
echo "Failed"
exit 25
exit 23
fi
else
echo -n "Changing ownership of the events folder recursively to ${ZM_WEB_USER} ${ZM_WEB_GROUP}... "
chown -R ${ZM_WEB_USER}:${ZM_WEB_GROUP} "$ZM_PATH_CONTENT/events"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 24
fi
fi
# change ownership for the log folder. do it recursively unless -q is used
if [ -n "$QUICK" ]; then
echo -n "Changing ownership of the log folder to ${ZM_WEB_USER} ${ZM_WEB_GROUP}... "
chown ${ZM_WEB_USER}:${ZM_WEB_GROUP} "$ZM_LOGDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 25
fi
else
echo -n "Changing ownership of the log folder recursively to ${ZM_WEB_USER} ${ZM_WEB_GROUP}... "
chown -R ${ZM_WEB_USER}:${ZM_WEB_GROUP} "$ZM_LOGDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
@ -241,6 +335,27 @@ else
fi
fi
# change ownership for the temp folder. do it recursively unless -q is used
if [ -n "$QUICK" ]; then
echo -n "Changing ownership of the temp folder to ${ZM_WEB_USER} ${ZM_WEB_GROUP}... "
chown ${ZM_WEB_USER}:${ZM_WEB_GROUP} "$ZM_TMPDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 27
fi
else
echo -n "Changing ownership of the temp folder recursively to ${ZM_WEB_USER} ${ZM_WEB_GROUP}... "
chown -R ${ZM_WEB_USER}:${ZM_WEB_GROUP} "$ZM_TMPDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 28
fi
fi
# Change directory permissions for the images folder
echo -n "Changing permissions of the images folder to 775... "
chmod 775 "$ZM_PATH_CONTENT/images"
@ -251,7 +366,6 @@ else
exit 30
fi
# Change directory permissions for the events folder
echo -n "Changing permissions of the events folder to 775... "
chmod 775 "$ZM_PATH_CONTENT/events"
@ -262,5 +376,35 @@ else
exit 31
fi
# Change directory permissions for the log folder
echo -n "Changing permissions of the log folder to 775... "
chmod 775 "$ZM_LOGDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 32
fi
# Change directory permissions for the temp folder
echo -n "Changing permissions of the temp folder to 775... "
chmod 775 "$ZM_TMPDIR"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 33
fi
# Link the CakePHP tmp folder to the zoneminder temp folder
echo -n "Linking CakePHP tmp folder to ${ZM_TMPDIR}... "
ln -sfT "$ZM_TMPDIR" "$ZM_PATH_WEB/api/app/tmp"
if [ "$?" = "0" ]; then
echo "OK"
else
echo "Failed"
exit 40
fi
echo ""
echo "All done"