diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 309d79785..5f80fe591 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -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`):** diff --git a/.gitmodules b/.gitmodules index fc8ea09d6..a42bd946b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,4 @@ branch = 3.0 [submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"] path = web/api/app/Plugin/CakePHP-Enum-Behavior - url = https://github.com/asper/CakePHP-Enum-Behavior.git + url = https://github.com/connortechnology/CakePHP-Enum-Behavior.git diff --git a/.travis.yml b/.travis.yml index 916cd5a98..52ba2d486 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,6 @@ env: - 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 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 08bfd406e..000000000 --- a/Dockerfile +++ /dev/null @@ -1,111 +0,0 @@ -# ZoneMinder, you need the GIT repository code and submodules (git submodule update --init --recursive) - -FROM ubuntu:xenial -MAINTAINER Markos Vakondios - -# 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 diff --git a/README.md b/README.md index 1d43ff560..0b9a90f9c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 2ff2de208..f9bb3866d 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -273,10 +273,26 @@ CREATE TABLE `Events_Archived` ( ) ENGINE=@ZM_MYSQL_ENGINE@; -drop trigger if exists event_update_trigger; +drop procedure if exists update_storage_stats; delimiter // +create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT) + +sql security invoker + +deterministic + +begin + + update Storage set DiskSpace = DiskSpace + space where Id = StorageId; + +end; + +// + +drop trigger if exists event_update_trigger// + CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events FOR EACH ROW BEGIN @@ -504,7 +520,7 @@ CREATE TABLE `MonitorPresets` ( `Name` varchar(64) NOT NULL default '', `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','NVSocket') 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, @@ -546,7 +562,7 @@ CREATE TABLE `Monitors` ( `V4LMultiBuffer` tinyint(1) unsigned, `V4LCapturesPerFrame` tinyint(3) unsigned, `Protocol` varchar(16), - `Method` varchar(16) NOT NULL default '', + `Method` varchar(16) default '', `Host` varchar(64), `Port` varchar(8) NOT NULL default '', `SubPath` varchar(64) NOT NULL default '', @@ -628,11 +644,11 @@ CREATE INDEX `Monitors_ServerId_idx` ON `Monitors` (`ServerId`); DROP TABLE IF EXISTS `Monitor_Status`; CREATE TABLE `Monitor_Status` ( - `Id` int(10) unsigned NOT NULL, - `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `MonitorId` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, - PRIMARY KEY (`Id`) + PRIMARY KEY (`MonitorId`) ) ENGINE=MEMORY; -- -- Table structure for table `States` @@ -825,7 +841,7 @@ CREATE TABLE `Storage` ( `Path` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '', `Type` enum('local','s3fs') NOT NULL default 'local', - `DiskSpace` bigint unsigned default NULL, + `DiskSpace` bigint default NULL, `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `ServerId` int(10) unsigned, PRIMARY KEY (`Id`) @@ -885,7 +901,7 @@ 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); @@ -896,67 +912,67 @@ INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1 -- -- Add some monitor preset values -- -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',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,'',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,'',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,'',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,'',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,'',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,'',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,'',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,'',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,'',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,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,':',100,100); -INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp','rtpUni',NULL,NULL,NULL,'',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,'',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,'',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','',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','',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','',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','',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','',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','',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','',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','',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','',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','',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','',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,':',100,100); +INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','',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','',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','',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','',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,'',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,'',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,'',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,'',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,'',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,'',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,'',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,':',100,100); -INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http','simple',NULL,NULL,NULL,'',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,'',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,'',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,'',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,'',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,'',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','',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','',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','',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','',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','',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','',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','',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,':',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','',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','',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','',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','',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','',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','',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','',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:///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://: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:///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@: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','',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=&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://:@:554/11',NULL,704,576,0,NULL,1,'10','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://:@:554/11',NULL,640,480,0,NULL,1,'11','','',100,100); @@ -964,8 +980,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,'','80','/videostream.cgi?user=&pwd=&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','',':@',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,':@','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','',':@',100,100); INSERT INTO MonitorPresets VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video','0',255,'','rtpMulti','','80','rtsp://:554/11','',1920,1080,0,0.00,1,'16','-speed=64',':',100,33); -INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp','rtpRtsp',255,'rtsp','rtpRtsp',NULL,554,'rtsp:///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:///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:///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:///tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100); -- -- Add some zone preset values diff --git a/db/zm_update-1.31.29.sql b/db/zm_update-1.31.29.sql new file mode 100644 index 000000000..7cb7769f4 --- /dev/null +++ b/db/zm_update-1.31.29.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS `Monitor_Status`; +CREATE TABLE `Monitor_Status` ( + `MonitorId` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`MonitorId`) +) ENGINE=MEMORY; diff --git a/db/zm_update-1.31.30.sql b/db/zm_update-1.31.30.sql new file mode 100644 index 000000000..c87b4409a --- /dev/null +++ b/db/zm_update-1.31.30.sql @@ -0,0 +1,20 @@ +DROP TABLE IF EXISTS `Monitor_Status`; +CREATE TABLE `Monitor_Status` ( + `MonitorId` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`MonitorId`) +) ENGINE=MEMORY; + +SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO'; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM Storage WHERE Name = 'Default' AND Id=0 AND Path='/var/cache/zoneminder/events' + ) > 0, + "SELECT 'Default Storage Area already exists.'", + "INSERT INTO Storage (Id,Name,Path,Scheme,ServerId) VALUES (0,'Default','/var/cache/zoneminder/events','Medium',NULL)" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.31.31.sql b/db/zm_update-1.31.31.sql new file mode 100644 index 000000000..66e25f95c --- /dev/null +++ b/db/zm_update-1.31.31.sql @@ -0,0 +1 @@ +ALTER TABLE Storage MODIFY DiskSpace BIGINT default NULL; diff --git a/db/zm_update-1.31.32.sql b/db/zm_update-1.31.32.sql new file mode 100644 index 000000000..781fce1fc --- /dev/null +++ b/db/zm_update-1.31.32.sql @@ -0,0 +1,3 @@ +ALTER TABLE Monitors MODIFY TotalEventDiskSpace BIGINT default NULL; +ALTER TABLE Monitors MODIFY Method VARCHAR(16) default NULL; + diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index fc6a0c7ab..10ce1ff0a 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -110,6 +110,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} diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst index 7d9a9fe8a..06b976037 100644 --- a/distros/ubuntu1204/zoneminder.postinst +++ b/distros/ubuntu1204/zoneminder.postinst @@ -31,9 +31,9 @@ if [ "$1" = "configure" ]; then if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf # This creates the user. - echo "grant lock tables, alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else - echo "grant lock tables, alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi zmupdate.pl --nointeractive diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 35d839cc2..3d8e09a1c 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -33,9 +33,9 @@ if [ "$1" = "configure" ]; then if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf # This creates the user. - echo "grant lock tables,alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else - echo "grant lock tables,alter,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi zmupdate.pl --nointeractive diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index c1f20261d..6b558d11c 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -44,7 +44,7 @@ Clone the ZoneMinder project if you have not done so already. :: - git clone ZoneMinder + git clone https://ZoneMinder/ZoneMinder cd ZoneMinder Alternatively, if you have already cloned the repo and wish to update it, do the following. diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index fe95754d1..e7c821838 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -30,9 +30,14 @@ guide you with a quick search. .. 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 `_ 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: :: diff --git a/misc/apache.conf.in b/misc/apache.conf.in index d1c6d70db..1344bb9b1 100644 --- a/misc/apache.conf.in +++ b/misc/apache.conf.in @@ -8,7 +8,7 @@ ServerAdmin webmaster@localhost DocumentRoot "@WEB_PREFIX@" - Alias /zm/ "@WEB_PREFIX@/" + Alias /zm "@WEB_PREFIX@" Options -Indexes +FollowSymLinks AllowOverride All diff --git a/onvif/CMakeLists.txt b/onvif/CMakeLists.txt index e1ad01556..a6b9bde4e 100644 --- a/onvif/CMakeLists.txt +++ b/onvif/CMakeLists.txt @@ -3,5 +3,3 @@ # Process the perl modules subdirectory add_subdirectory(proxy) add_subdirectory(modules) -add_subdirectory(scripts) - diff --git a/onvif/scripts/CMakeLists.txt b/onvif/scripts/CMakeLists.txt deleted file mode 100644 index 3405fa756..000000000 --- a/onvif/scripts/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# CMakeLists.txt for the ZoneMinder perl scripts. - -# If this is an out-of-source build, copy the files we need to the binary directory -if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") -endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - -# Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/onvif/scripts/zmonvif-probe.pl b/onvif/scripts/zmonvif-probe.pl deleted file mode 100755 index de6c18aa3..000000000 --- a/onvif/scripts/zmonvif-probe.pl +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/perl -w -use strict; -# -# ========================================================================== -# -# ZoneMinder ONVIF Control Protocol Module -# Copyright (C) 2014 Jan M. Hochstein -# -# 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 ONVIF capability prober -# - -use Getopt::Std; -use Data::UUID; - -require ONVIF::Client; - -require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort; -require WSDiscovery10::Elements::Header; -require WSDiscovery10::Elements::Types; -require WSDiscovery10::Elements::Scopes; - -require WSDiscovery::TransportUDP; - -# -# ======================================================================== -# Globals - -my $verbose = 0; -my $soap_version = undef; -my $client; - -# ========================================================================= -# internal functions - -sub deserialize_message -{ - my ($wsdl_client, $response) = @_; - - # copied and adapted from SOAP::WSDL::Client - - # get deserializer - my $deserializer = $wsdl_client->get_deserializer(); - - if(! $deserializer) { - $deserializer = SOAP::WSDL::Factory::Deserializer->get_deserializer({ - soap_version => $wsdl_client->get_soap_version(), - %{ $wsdl_client->get_deserializer_args() }, - }); - } - # set class resolver if serializer supports it - $deserializer->set_class_resolver( $wsdl_client->get_class_resolver() ) - if ( $deserializer->can('set_class_resolver') ); - - # Try deserializing response - there may be some, - # even if transport did not succeed (got a 500 response) - if ( $response ) { - # as our faults are false, returning a success marker is the only - # reliable way of determining whether the deserializer succeeded. - # Custom deserializers may return an empty list, or undef, - # and $@ is not guaranteed to be undefined. - my ($success, $result_body, $result_header) = eval { - (1, $deserializer->deserialize( $response )); - }; - if (defined $success) { - return wantarray - ? ($result_body, $result_header) - : $result_body; - } - elsif (blessed $@) { #}&& $@->isa('SOAP::WSDL::SOAP::Typelib::Fault11')) { - return $@; - } - else { - return $deserializer->generate_fault({ - code => 'soap:Server', - role => 'urn:localhost', - message => "Error deserializing message: $@. \n" - . "Message was: \n$response" - }); - } - }; -} - - -sub interpret_messages -{ - my ($svc_discover, $services, @responses ) = @_; - - foreach my $response ( @responses ) { - - if($verbose) { - print "Received message:\n" . $response . "\n"; - } - - my $result = deserialize_message($svc_discover, $response); - if(not $result) { - if($verbose) { - print "Error deserializing message. No message returned from deserializer.\n"; - } - next; - } - - my $xaddr; - foreach my $l_xaddr (split ' ', $result->get_ProbeMatch()->get_XAddrs()) { - # find IPv4 address - if($verbose) { - print "l_xaddr = $l_xaddr\n"; - } - if($l_xaddr =~ m|//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[:/]|) { - $xaddr = $l_xaddr; - last; - } else { - print STDERR "Unable to find IPv4 address from xaddr $l_xaddr\n"; - } - } - - # No usable address found - next if not $xaddr; - - # ignore multiple responses from one service - next if defined $services->{$xaddr}; - $services->{$xaddr} = 1; - - print "$xaddr, " . $svc_discover->get_soap_version() . ", "; - - print "("; - my $scopes = $result->get_ProbeMatch()->get_Scopes(); - my $count = 0; - foreach my $scope(split ' ', $scopes) { - if($scope =~ m|onvif://www\.onvif\.org/(.+)/(.*)|) { - my ($attr, $value) = ($1,$2); - if( 0 < $count ++) { - print ", "; - } - print $attr . "=\'" . $value . "\'"; - } - } - print ")\n"; - } -} - -# ========================================================================= -# functions - -sub discover -{ - ## collect all responses - my @responses = (); - - no warnings 'redefine'; - - *WSDiscovery::TransportUDP::_notify_response = sub { - my ($transport, $response) = @_; - push @responses, $response; - }; - - ## try both soap versions - my %services; - - my $uuid_gen = Data::UUID->new(); - - if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) { - - if($verbose) { - print "Probing for SOAP 1.1\n" - } - my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ -# no_dispatch => '1', - }); - $svc_discover->set_soap_version('1.1'); - - my $uuid = $uuid_gen->create_str(); - - my $result = $svc_discover->ProbeOp( - { # WSDiscovery::Types::ProbeType - Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType - Scopes => { value => '' }, - }, - WSDiscovery10::Elements::Header->new({ - Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, - MessageID => { value => "urn:uuid:$uuid" }, - To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, - }) - ); - print $result . "\n" if $verbose; - - interpret_messages($svc_discover, \%services, @responses); - @responses = (); - } # end if doing soap 1.1 - - if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) { - if($verbose) { - print "Probing for SOAP 1.2\n" - } - my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ -# no_dispatch => '1', - }); - $svc_discover->set_soap_version('1.2'); - -# copies of the same Probe message must have the same MessageID. -# This is not a copy. So we generate a new uuid. - my $uuid = $uuid_gen->create_str(); - -# Everyone else, like the nodejs onvif code and odm only ask for NetworkVideoTransmitter - my $result = $svc_discover->ProbeOp( - { # WSDiscovery::Types::ProbeType - xmlattr => { 'xmlns:dn' => 'http://www.onvif.org/ver10/network/wsdl', }, - Types => 'dn:NetworkVideoTransmitter', # QNameListType - Scopes => { value => '' }, - }, - WSDiscovery10::Elements::Header->new({ - Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, - MessageID => { value => "urn:uuid:$uuid" }, - To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, - }) - ); - print $result . "\n" if $verbose; - interpret_messages($svc_discover, \%services, @responses); - } # end if doing soap 1.2 - -} - - -sub profiles -{ -# my $result = $services{media}{ep}->GetVideoSources( { } ,, ); -# die $result if not $result; -# print $result . "\n"; - - my $result = $client->get_endpoint('media')->GetProfiles( { } ,, ); - die $result if not $result; - if($verbose) { - print "Received message:\n" . $result . "\n"; - } - - my $profiles = $result->get_Profiles(); - - foreach my $profile ( @{ $profiles } ) { - - my $token = $profile->attr()->get_token() ; - - # Specification gives conflicting values for unicast stream types, try both. - # http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri - foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) { - $result = $client->get_endpoint('media')->GetStreamUri( { - StreamSetup => { # ONVIF::Media::Types::StreamSetup - Stream => $streamtype, # StreamType - Transport => { # ONVIF::Media::Types::Transport - Protocol => 'RTSP', # TransportProtocol - }, - }, - ProfileToken => $token, # ReferenceToken - } ,, ); - last if $result; - } - die $result if not $result; - # print $result . "\n"; - - my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration(); - print join(', ', $token, - $profile->get_Name(), - ( $VideoEncoderConfiguration ? ( - $VideoEncoderConfiguration->get_Encoding(), - $VideoEncoderConfiguration->get_Resolution()->get_Width(), - $VideoEncoderConfiguration->get_Resolution()->get_Height(), - $VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(), - ) : () ), - $result->get_MediaUri()->get_Uri() , - ). "\n"; - } # end foreach profile - -# -# use message parser without schema validation ??? -# - -} - -sub move -{ - my ($dir) = @_; - - - my $result = $client->get_endpoint('ptz')->GetNodes( { } ,, ); - - die $result if not $result; - print $result . "\n"; - -} - -sub metadata -{ - my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; - - $result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; - -# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, ); -# die $result if not $result; -# print $result . "\n"; - -} - -# ======================================================================== -# options processing - -$Getopt::Std::STANDARD_HELP_VERSION = 1; - -our ($opt_v); - -my $OPTIONS = "v"; - -sub HELP_MESSAGE -{ - my ($fh, $pkg, $ver, $opts) = @_; - print $fh "Usage: " . __FILE__ . " [-v] probe \n"; - print $fh " " . __FILE__ . " [-v] \n"; - print $fh <new( { - 'url_svc_device' => $url_svc_device, - 'soap_version' => $soap_version } ); - - $client->set_credentials($username, $password, 1); - - $client->create_services(); - - - if($action eq "profiles") { - - profiles(); - } - elsif($action eq "move") { - my $dir = shift; - move($dir); - } - elsif($action eq "metadata") { - metadata(); - } - else { - print("Error: Unknown command\"$action\""); - exit(1); - } -} diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 81cfdaa56..f1bfa2ed1 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -8,6 +8,7 @@ configure_file(zmaudit.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" @ONLY) configure_file(zmcontrol.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" @ONLY) configure_file(zmdc.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" @ONLY) configure_file(zmfilter.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" @ONLY) +configure_file(zmonvif-probe.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" @ONLY) configure_file(zmpkg.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" @ONLY) configure_file(zmtrack.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" @ONLY) configure_file(zmtrigger.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" @ONLY) @@ -34,7 +35,7 @@ FOREACH(PERLSCRIPT ${perlscripts}) ENDFOREACH(PERLSCRIPT ${perlscripts}) # Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT ZM_NO_X10) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif(NOT ZM_NO_X10) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm index 01cff6c62..f3ac081a5 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm @@ -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 ); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index c362a81bf..692af30d4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -173,8 +173,7 @@ sub runCommand { chomp( $output ); if ( $status || logDebugging() ) { if ( $status ) { - Error( "Unable to run \"$command\", output is \"$output\"\n" ); - exit( -1 ); + Error( "Unable to run \"$command\", output is \"$output\", status is $status\n" ); } else { Debug( "Output: $output\n" ); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in index 23914bdd2..9ce556beb 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in @@ -45,7 +45,6 @@ our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; -use Getopt::Std; use Data::UUID; use vars qw( $verbose $soap_version ); @@ -185,11 +184,11 @@ sub discover { }; ## try both soap versions - my %services; my $uuid_gen = Data::UUID->new(); if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) { + my %services; if($verbose) { print "Probing for SOAP 1.1\n" @@ -219,6 +218,7 @@ sub discover { } # end if doing soap 1.1 if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) { + my %services; if($verbose) { print "Probing for SOAP 1.2\n" } @@ -253,8 +253,17 @@ sub discover { sub profiles { my ( $client ) = @_; - my $result = $client->get_endpoint('media')->GetProfiles( { } ,, ); - die $result if not $result; + my $endpoint = $client->get_endpoint('media'); + if ( ! $endpoint ) { + print "No media enpoint for client.\n"; + return; + } + + my $result = $endpoint->GetProfiles( { } ,, ); + if ( ! $result ) { + print "No result from GetProfiles\n"; + return; + } if($verbose) { print "Received message:\n" . $result . "\n"; } diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 033f265c9..7be542391 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -180,6 +180,9 @@ MAIN: while( $loop ) { Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}"); } elsif ( $Config{ZM_SERVER_ID} ) { @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); + if ( ! @Storage_Areas ) { + Fatal("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID}); + } Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name()); } else { @Storage_Areas = ZoneMinder::Storage->find(); @@ -210,11 +213,10 @@ MAIN: while( $loop ) { $db_events->{$event->{Id}} = $event->{Age}; } Debug( 'Got '.int(keys(%$db_events))." events for monitor $monitor->{Id}" ); - } + } # end while monitors my $fs_monitors; - foreach my $Storage ( @Storage_Areas ) { Debug('Checking events in ' . $Storage->Path() ); if ( ! chdir( $Storage->Path() ) ) { @@ -224,7 +226,14 @@ MAIN: while( $loop ) { # Please note that this glob will take all files beginning with a digit. foreach my $monitor ( glob('[0-9]*') ) { - next if $monitor =~ /\D/; + if ( $monitor =~ /\D/ ) { + Debug("Weird non digit characters in $monitor"); + next; + } + if ( $monitor_id and ( $monitor_id != $monitor ) ) { + Debug("Skipping monitor $monitor because we are only interested in monitor $monitor_id"); + next; + } Debug( "Found filesystem monitor '$monitor'" ); $fs_monitors->{$monitor} = {} if ! $fs_monitors->{$monitor}; @@ -233,8 +242,10 @@ MAIN: while( $loop ) { # De-taint ( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); - #if ( $$Storage{Scheme} eq 'Deep' ) { - foreach my $day_dir ( glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") ) { + { + my @day_dirs = glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]"); + Debug(qq`Checking for Deep Events under using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . " events"); + foreach my $day_dir ( @day_dirs ) { Debug( "Checking day dir $day_dir" ); ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint if ( ! chdir( $day_dir ) ) { @@ -258,7 +269,7 @@ MAIN: while( $loop ) { #Event path is hour/minute/sec my $event_path = readlink( $event_link ); - if ( !-e $event_path ) { + if ( !($event_path and -e $event_path) ) { aud_print( "Event link $day_dir/$event_link does not point to valid target" ); if ( confirm() ) { ( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint @@ -279,9 +290,17 @@ MAIN: while( $loop ) { } # end foreach event_link chdir( $Storage->Path() ); } # end foreach day dir + } - foreach my $event_dir ( glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") ) { - next if ! -d $event_dir; + Debug("Checking for Medium Scheme Events under $monitor_dir"); + { + my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*"); + Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." ); + foreach my $event_dir ( @event_dirs ) { + if ( ! -d $event_dir ) { + Debug( "$event_dir is not a dir. Skipping" ); + next; + } my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/; if ( ! $event_id ) { Debug("Unable to parse date/event_id from $event_dir"); @@ -290,13 +309,16 @@ MAIN: while( $loop ) { my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); $$Event{Id} = $event_id; $$Event{Path} = join('/', $Storage->Path(), $event_dir ); + Debug("Have event $$Event{Id} at $$Event{Path}"); $$Event{Scheme} = 'Medium'; $$Event{RelativePath} = $event_dir; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); } # end foreach event + } if ( ! $$Storage{Scheme} ) { + Debug("Storage Scheme not set on $$Storage{Name}"); if ( ! chdir( $monitor_dir ) ) { Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" ); next; @@ -321,12 +343,21 @@ MAIN: while( $loop ) { #delete_empty_directories( $monitor_dir ); } # end foreach monitor - redo MAIN if ( $cleaned ); + + if ( $cleaned ) { + Debug("First stage cleaning done. Restarting."); + redo MAIN; + } $cleaned = 0; while ( my ( $monitor_id, $fs_events ) = each(%$fs_monitors) ) { if ( my $db_events = $db_monitors->{$monitor_id} ) { - next if ! $fs_events; + if ( ! $fs_events ) { + Debug("No fs_events for database monitor $monitor_id"); + next; + } + my @event_ids = keys %$fs_events; + Debug("Have " .scalar @event_ids . " events for monitor $monitor_id"); foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) { @@ -347,6 +378,7 @@ MAIN: while( $loop ) { } # end foreach fs event } else { aud_print( "Filesystem monitor '$monitor_id' in $$Storage{Path} does not exist in database" ); + if ( confirm() ) { my $command = "rm -rf $monitor_id"; executeShellCommand( $command ); @@ -477,7 +509,7 @@ MAIN: while( $loop ) { $res = $selectEmptyEventsSth->execute() or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() ); while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) { - aud_print( "Found empty event with no frame records '$event->{Id}'" ); + aud_print( "Found empty event with no frame records '$event->{Id}' at $$event{StartTime}" ); if ( confirm() ) { $res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); @@ -689,7 +721,7 @@ sub confirm { if ( $report ) { print( "\n" ); } elsif ( $interactive ) { - print( ", $prompt y/n: " ); + print( ", $prompt Y/n/q: " ); my $char = <>; chomp( $char ); if ( $char eq 'q' ) { @@ -784,6 +816,7 @@ yet. -c, --continuous - Run continuously -f, --force - Run even if pid file exists -i, --interactive - Ask before applying any changes + -m, --monitor_id - Only consider the given monitor -r, --report - Just report don't actually do anything -s, --storage_id - Specify a storage area to audit instead of all -v, --version - Print the installed version of ZoneMinder diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 623769d81..538e2d8f1 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -183,39 +183,39 @@ use Sys::CpuLoad; socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $attempts = 0; - while( !connect( CLIENT, $saddr ) ) { + while( !connect(CLIENT, $saddr) ) { $attempts++; - Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts" ); - Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); + Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts"); + Fatal("Can't connect: $!") if $attempts > MAX_CONNECT_DELAY; usleep(200000); } # end while } elsif ( defined($cpid) ) { ZMServer::run(); } else { - Fatal( "Can't fork: $!" ); + Fatal("Can't fork: $!"); } } # end if ! server is up -if ( $command eq 'check' && ! $daemon ) { - print( "running\n" ); +if ( ($command eq 'check') && !$daemon ) { + print("running\n"); exit(); } elsif ( $command eq 'startup' ) { # Our work here is done - exit() if ( !$server_up ); + exit() if !$server_up; } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = join(';', $command, ( $daemon ? $daemon : () ), @args ); -print( CLIENT $message ); -shutdown( CLIENT, 1 ); +print(CLIENT $message); +shutdown(CLIENT, 1); while( my $line = ) { - chomp( $line ); - print( "$line\n" ); + chomp($line); + print("$line\n"); } # And we're done! -close( CLIENT ); +close(CLIENT); #print( "Finished writing, bye\n" ); exit; @@ -344,9 +344,9 @@ sub run { } else { dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); } - close( CLIENT ); + close(CLIENT); } else { - Fatal( 'Bogus descriptor' ); + Fatal('Bogus descriptor'); } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { @@ -355,9 +355,9 @@ sub run { # See if it needs to start up again restartPending(); } elsif ( $! == EPIPE ) { - Error( "Can't select: $!" ); + Error("Can't select: $!"); } else { - Fatal( "Can't select: $!" ); + Fatal("Can't select: $!"); } } else { #print( "Select timed out\n" ); @@ -566,19 +566,19 @@ sub restart { my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if @args; - dPrint ( ZoneMinder::Logger::WARNING, "Restarting $command\n"); + dPrint ( ZoneMinder::Logger::DEBUG, "Restarting $command\n"); my $process = $cmd_hash{$command}; if ( $process ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process" ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process" ); if ( $process->{pid} ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process pid " .$process->{pid} ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process pid " .$process->{pid} ); my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process pid hash " .$process->{pid} ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process pid hash " .$process->{pid} ); _stop( 0, $process ); return; } else { - dPrint ( ZoneMinder::Logger::WARNING, "Not sending stop" ); + dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" ); } } } diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 1099d5fc2..12dae98cf 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -73,6 +73,7 @@ use autouse 'Data::Dumper'=>qw(Dumper); my $filter_name = ''; my $filter_id; my $version = 0; +my $zm_terminate = 0; GetOptions( 'filter=s' =>\$filter_name, @@ -94,7 +95,20 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) ; logInit(); -logSetSignal(); +sub HupHandler { + Info("Received HUP, reloading"); + &ZoneMinder::Logger::logHupHandler(); +} +sub TermHandler { + Info("Received TERM, exiting"); + $zm_terminate = 1; +} +sub Term { + exit( 0 ); +} +$SIG{HUP} = \&HupHandler; +$SIG{TERM} = \&TermHandler; +$SIG{INT} = \&TermHandler; if ( $Config{ZM_OPT_UPLOAD} ) { # Comment these out if you don't have them and don't want to upload @@ -166,7 +180,7 @@ if ( ! ( $filter_name or $filter_id ) ) { my @filters; my $last_action = 0; -while( 1 ) { +while( ! $zm_terminate ) { my $now = time; if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { Debug( "Reloading filters\n" ); @@ -175,6 +189,7 @@ while( 1 ) { } foreach my $filter ( @filters ) { + last if $zm_terminate; if ( $$filter{Concurrent} and ! ( $filter_id or $filter_name ) ) { my ( $proc ) = $0 =~ /(\S+)/; my ( $id ) = $$filter{Id} =~ /(\d+)/; @@ -186,7 +201,7 @@ while( 1 ) { } } - last if ( $filter_name or $filter_id ); + last if $filter_name or $filter_id or $zm_terminate; Debug( "Sleeping for $delay seconds\n" ); sleep( $delay ); @@ -266,6 +281,7 @@ sub checkFilter { ) ); foreach my $event ( @Events ) { + last if $zm_terminate; Debug( "Checking event $event->{Id}" ); my $delete_ok = !undef; $dbh->ping(); diff --git a/scripts/zmonvif-probe.pl.in b/scripts/zmonvif-probe.pl.in new file mode 100755 index 000000000..001d89f83 --- /dev/null +++ b/scripts/zmonvif-probe.pl.in @@ -0,0 +1,155 @@ +#!/usr/bin/perl -w +use strict; +# +# ========================================================================== +# +# ZoneMinder ONVIF Control Protocol Module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 ONVIF capability prober +# + +use Getopt::Std; + +require ONVIF::Client; + + +# ======================================================================== +# options processing + +$Getopt::Std::STANDARD_HELP_VERSION = 1; + +our ($opt_v); + +my $OPTIONS = "v"; + +sub HELP_MESSAGE +{ + my ($fh, $pkg, $ver, $opts) = @_; + print $fh "Usage: " . __FILE__ . " [-v] probe \n"; + print $fh " " . __FILE__ . " [-v] \n"; + print $fh <new( { + 'url_svc_device' => $url_svc_device, + 'soap_version' => $soap_version } ); + + $client->set_credentials($username, $password, 1); + + $client->create_services(); + + + if ( $action eq "profiles" ) { + ZoneMinder::ONVIF::profiles($client); + } elsif( $action eq "move" ) { + my $dir = shift; + ZoneMinder::ONVIF::move($client, $dir); + } elsif ( $action eq "metadata" ) { + ZoneMinder::ONVIF::metadata($client); + } else { + print("Error: Unknown command\"$action\""); + exit(1); + } +} + +1; +__END__ + +=head1 NAME + +zmonvif-probe.pl - ZoneMinder ONVIF probing tool + +=head1 SYNOPSIS + + zmonfig-probe.pl [-v] probe + [-v] \n"; + + Commands are: + probe - scan for devices on the local network and list them + profiles - print the device's supported stream configurations + metadata - print some of the device's configuration settings + move - move the device (only ptz cameras) + Common parameters: + -v - increase verbosity + Device access parameters (for all commands but 'probe'): + device URL - the ONVIF Device service URL + soap version - SOAP version (1.1 or 1.2) + user - username of a user with access to the device + password - password for the user + +=head1 DESCRIPTION + + +=head1 OPTIONS + + -c, --continuous - Run continuously + -f, --force - Run even if pid file exists + -i, --interactive - Ask before applying any changes + -m, --monitor_id - Only consider the given monitor + -r, --report - Just report don't actually do anything + -s, --storage_id - Specify a storage area to audit instead of all + -v, --version - Print the installed version of ZoneMinder + +=cut + diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 303745277..c37bd04ce 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -43,7 +43,12 @@ int Event::pre_alarm_count = 0; Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } }; -Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent ) : +Event::Event( + Monitor *p_monitor, + struct timeval p_start_time, + const std::string &p_cause, + const StringSetMap &p_noteSetMap, + bool p_videoEvent ) : monitor( p_monitor ), start_time( p_start_time ), cause( p_cause ), @@ -53,12 +58,18 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string { std::string notes; - createNotes( notes ); + createNotes(notes); + + struct timeval now; + gettimeofday(&now, 0); bool untimedEvent = false; if ( !start_time.tv_sec ) { untimedEvent = true; - gettimeofday( &start_time, 0 ); + start_time = now; + } else if ( start_time.tv_sec > now.tv_sec ) { + Error("StartTime in the future"); + start_time = now; } Storage * storage = monitor->getStorage(); @@ -216,7 +227,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string Event::~Event() { static char sql[ZM_SQL_MED_BUFSIZ]; struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2); + Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec ); if ( frames > last_db_frame ) { @@ -462,6 +474,12 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); + // Delta is Decimal(8,2) so 6 integer digits and 2 decimal digits + if ( delta_time.sec > 999999 ) { + Warning("Invalid delta_time from_unixtime(%ld), %s%ld.%02ld", + timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); + delta_time.sec = 0; + } int sql_len = strlen(sql); snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); @@ -474,7 +492,6 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st *(sql+strlen(sql)-2) = '\0'; if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frames: %s, sql was (%s)", mysql_error( &dbconn ), sql ); - exit( mysql_errno( &dbconn ) ); } last_db_frame = frames; } else { diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 5ddd8ee82..77e3c90aa 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -458,5 +458,5 @@ void dumpPacket(AVPacket *pkt, const char *text) { pkt->flags & AV_PKT_FLAG_KEY, pkt->pos, pkt->duration); - Debug(1, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); + Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); } diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 069d68b63..85894e9f2 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -111,7 +111,6 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri mIsOpening = false; mCanCapture = false; mOpenStart = 0; - mReopenThread = 0; videoStore = NULL; video_last_pts = 0; have_video_keyframe = false; @@ -162,21 +161,18 @@ void FfmpegCamera::Terminate() { } int FfmpegCamera::PrimeCapture() { + if ( mCanCapture ) { + CloseFfmpeg(); + } mVideoStreamId = -1; mAudioStreamId = -1; Info( "Priming capture from %s", mPath.c_str() ); -#if THREAD - if ( OpenFfmpeg() != 0 ) { - ReopenFfmpeg(); - } - return 0; -#else return OpenFfmpeg(); -#endif } int FfmpegCamera::PreCapture() { + Debug(1, "PreCapture"); // If Reopen was called, then ffmpeg is closed and we need to reopen it. if ( ! mCanCapture ) return OpenFfmpeg(); @@ -190,18 +186,6 @@ int FfmpegCamera::Capture( Image &image ) { } // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. - if ( mReopenThread != 0 ) { - void *retval = 0; - int ret; - - ret = pthread_join(mReopenThread, &retval); - if ( ret != 0 ) { - Error("Could not join reopen thread."); - } - - Info( "Successfully reopened stream." ); - mReopenThread = 0; - } int frameComplete = false; while ( !frameComplete ) { @@ -217,6 +201,7 @@ int FfmpegCamera::Capture( Image &image ) { ) { Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf ); ReopenFfmpeg(); + continue; } Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); @@ -292,7 +277,7 @@ int FfmpegCamera::Capture( Image &image ) { directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); if ( directbuffer == NULL ) { Error("Failed requesting writeable buffer for the captured image."); - return (-1); + return -1; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) @@ -304,8 +289,10 @@ int FfmpegCamera::Capture( Image &image ) { #endif #if HAVE_LIBSWSCALE - if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 ) - Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); + if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 ) { + Error("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); + return -1; + } #else // HAVE_LIBSWSCALE Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); #endif // HAVE_LIBSWSCALE @@ -327,10 +314,10 @@ int FfmpegCamera::PostCapture() { int FfmpegCamera::OpenFfmpeg() { - Debug ( 2, "OpenFfmpeg called." ); + Debug(2, "OpenFfmpeg called."); uint32_t last_event_id = monitor->GetLastEventId() ; uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); - Debug(2, "last_event(%d), our current (%d)", last_event_id, video_writer_event_id ); + Debug(2, "last_event(%d), our current (%d)", last_event_id, video_writer_event_id); int ret; @@ -369,8 +356,6 @@ int FfmpegCamera::OpenFfmpeg() { Debug ( 1, "Calling avformat_open_input for %s", mPath.c_str() ); mFormatContext = avformat_alloc_context( ); - //mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; - //mFormatContext->interrupt_callback.opaque = this; // Speed up find_stream_info //FIXME can speed up initial analysis but need sensible parameters... //mFormatContext->probesize = 32; @@ -380,7 +365,7 @@ int FfmpegCamera::OpenFfmpeg() { #endif { mIsOpening = false; - Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); + Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(errno)); return -1; } AVDictionaryEntry *e=NULL; @@ -391,19 +376,19 @@ int FfmpegCamera::OpenFfmpeg() { mIsOpening = false; Debug ( 1, "Opened input" ); - Info( "Stream open %s", mPath.c_str() ); + Info( "Stream open %s, parsing streams...", mPath.c_str() ); #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) - Debug ( 1, "Calling av_find_stream_info" ); + Debug(4, "Calling av_find_stream_info"); if ( av_find_stream_info( mFormatContext ) < 0 ) #else - Debug ( 1, "Calling avformat_find_stream_info" ); + Debug(4, "Calling avformat_find_stream_info"); if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) #endif - Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); + Fatal("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno)); startTime = av_gettime();//FIXME here or after find_Stream_info - Debug ( 1, "Got stream info" ); + Debug(4, "Got stream info"); // Find first video stream present // The one we want Might not be the first @@ -448,11 +433,11 @@ int FfmpegCamera::OpenFfmpeg() { if ( mAudioStreamId == -1 ) Debug( 3, "Unable to locate audio stream in %s", mPath.c_str() ); - Debug ( 3, "Found video stream at index %d", mVideoStreamId ); - Debug ( 3, "Found audio stream at index %d", mAudioStreamId ); + Debug(3, "Found video stream at index %d", mVideoStreamId); + Debug(3, "Found audio stream at index %d", mAudioStreamId); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - mVideoCodecContext = avcodec_alloc_context3( NULL ); + mVideoCodecContext = avcodec_alloc_context3(NULL); avcodec_parameters_to_context( mVideoCodecContext, mFormatContext->streams[mVideoStreamId]->codecpar ); #else mVideoCodecContext = mFormatContext->streams[mVideoStreamId]->codec; @@ -505,7 +490,7 @@ int FfmpegCamera::OpenFfmpeg() { Debug( 1, "Input stream appears to be h265. The stored event file may not be viewable in browser." ); } else { #endif - Error( "Input stream is not h264. The stored event file may not be viewable in browser." ); + Warning( "Input stream is not h264. The stored event file may not be viewable in browser." ); #ifdef AV_CODEC_ID_H265 } #endif @@ -514,7 +499,8 @@ int FfmpegCamera::OpenFfmpeg() { if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) { // Try and get the codec from the codec context - Fatal("Can't find codec for video stream from %s", mPath.c_str()); + Error("Can't find codec for video stream from %s", mPath.c_str()); + return -1; } else { Debug(1, "Video Found decoder"); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); @@ -530,7 +516,8 @@ int FfmpegCamera::OpenFfmpeg() { while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { Warning( "Option %s not recognized by ffmpeg", e->key); } - Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); + Error( "Unable to open codec for video stream from %s", mPath.c_str() ); + return -1; } else { AVDictionaryEntry *e = NULL; @@ -557,18 +544,20 @@ int FfmpegCamera::OpenFfmpeg() { } else { Debug(1, "Audio Found decoder"); zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0); - // Open the codec + // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - Debug ( 1, "Calling avcodec_open" ); - if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) + Debug ( 1, "Calling avcodec_open" ); + if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) { #else - Debug ( 1, "Calling avcodec_open2" ); - if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) + Debug ( 1, "Calling avcodec_open2" ); + if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) { #endif - Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); - } - Debug ( 1, "Opened audio codec" ); - } + Error( "Unable to open codec for video stream from %s", mPath.c_str() ); + return -1; + } + Debug(2, "Opened audio codec"); + } // end if find decoder + } // end if have audio_context // Allocate space for the native video frame mRawFrame = zm_av_frame_alloc(); @@ -576,10 +565,12 @@ int FfmpegCamera::OpenFfmpeg() { // Allocate space for the converted video frame mFrame = zm_av_frame_alloc(); - if ( mRawFrame == NULL || mFrame == NULL ) - Fatal( "Unable to allocate frame for %s", mPath.c_str() ); + if ( mRawFrame == NULL || mFrame == NULL ) { + Error("Unable to allocate frame for %s", mPath.c_str()); + return -1; + } - Debug ( 1, "Allocated frames" ); + Debug( 3, "Allocated frames"); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); @@ -588,19 +579,22 @@ int FfmpegCamera::OpenFfmpeg() { #endif if ( (unsigned int)pSize != imagesize ) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + Error("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + return -1; } - Debug ( 1, "Validated imagesize" ); + Debug(4, "Validated imagesize"); #if HAVE_LIBSWSCALE - Debug ( 1, "Calling sws_isSupportedInput" ); + Debug(1, "Calling sws_isSupportedInput"); if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) { - Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff)); + Error("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff)); + return -1; } if ( !sws_isSupportedOutput(imagePixFormat) ) { - Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + return -1; } mConvertContext = sws_getContext(mVideoCodecContext->width, @@ -609,8 +603,10 @@ int FfmpegCamera::OpenFfmpeg() { width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL); - if ( mConvertContext == NULL ) - Fatal( "Unable to create conversion context for %s", mPath.c_str() ); + if ( mConvertContext == NULL ) { + Error( "Unable to create conversion context for %s", mPath.c_str() ); + return -1; + } #else // HAVE_LIBSWSCALE Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE @@ -628,19 +624,8 @@ int FfmpegCamera::ReopenFfmpeg() { Debug(2, "ReopenFfmpeg called."); -#if THREAD - mCanCapture = false; - if ( pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0 ) { - // Log a fatal error and exit the process. - Fatal( "ReopenFfmpeg failed to create worker thread." ); - } -#else CloseFfmpeg(); - OpenFfmpeg(); - -#endif - - return 0; + return OpenFfmpeg(); } int FfmpegCamera::CloseFfmpeg() { @@ -690,45 +675,7 @@ int FfmpegCamera::CloseFfmpeg() { } return 0; -} - -int FfmpegCamera::FfmpegInterruptCallback(void *ctx) { - Debug(3,"FfmpegInteruptCallback"); - FfmpegCamera* camera = reinterpret_cast(ctx); - if ( camera->mIsOpening ) { - int now = time(NULL); - if ( (now - camera->mOpenStart) > config.ffmpeg_open_timeout ) { - Error( "Open video took more than %d seconds.", config.ffmpeg_open_timeout ); - return 1; - } - } - - return 0; -} - -void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){ - Debug(3,"FfmpegReopenThreadtCallback"); - if ( ctx == NULL ) return NULL; - - FfmpegCamera* camera = reinterpret_cast(ctx); - - while (1) { - // Close current stream. - camera->CloseFfmpeg(); - - // Sleep if necessary to not reconnect too fast. - int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); - wait = wait < 0 ? 0 : wait; - if ( wait > 0 ) { - Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); - sleep(wait); - } - - if ( camera->OpenFfmpeg() == 0 ) { - return NULL; - } - } -} +} // end FfmpegCamera::Close //Function to handle capture and store int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) { @@ -738,20 +685,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event int ret; static char errbuf[AV_ERROR_MAX_STRING_SIZE]; - // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened - // the connection to the ffmpeg device, and we can clean up the thread. - if ( mReopenThread != 0 ) { - void *retval = 0; - - ret = pthread_join(mReopenThread, &retval); - if (ret != 0){ - Error("Could not join reopen thread."); - } - - Info( "Successfully reopened stream." ); - mReopenThread = 0; - } - int frameComplete = false; while ( ! frameComplete ) { av_init_packet( &packet ); @@ -995,8 +928,9 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) { - Fatal("Unable to convert raw format %u to target format %u at frame %d", + Error("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); + return -1; } frameCount++; diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index 9f83d8a15..17da647b4 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -76,12 +76,9 @@ class FfmpegCamera : public Camera { int OpenFfmpeg(); int ReopenFfmpeg(); int CloseFfmpeg(); - static int FfmpegInterruptCallback(void *ctx); - static void* ReopenFfmpegThreadCallback(void *ctx); bool mIsOpening; bool mCanCapture; int mOpenStart; - pthread_t mReopenThread; #endif // HAVE_LIBAVFORMAT VideoStore *videoStore; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 980a41645..87cda8c80 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -479,12 +479,12 @@ Debug(2,"last_write_index(%d), last_write_time(%d)", shared_data->last_write_ind while( ( shared_data->last_write_index == (unsigned int)image_buffer_count ) && - ( shared_data->last_write_time == 0) + ( shared_data->last_write_time == 0 ) && ( !zm_terminate ) ) { - Warning( "Waiting for capture daemon" ); - sleep( 1 ); + Debug(1, "Waiting for capture daemon"); + sleep(1); } ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); adaptive_skip = true; @@ -1205,7 +1205,7 @@ bool Monitor::Analyse() { fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps ); static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } @@ -1937,7 +1937,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose } Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); col++; - const char *method = dbrow[col]; col++; + const char *method = dbrow[col] ? dbrow[col] : ""; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; @@ -2438,7 +2438,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose const char *linked_monitors = dbrow[col] ? dbrow[col] : ""; col++; const char *path = dbrow[col]; col++; - const char *method = dbrow[col]; col++; + const char *method = dbrow[col] ? dbrow[col] : ""; col++; const char *options = dbrow[col] ? dbrow[col] : ""; col++; int width = atoi(dbrow[col]); col++; @@ -3022,7 +3022,7 @@ Debug(4, "Return from Capture (%d)", captureResult); Info( "%s: images:%d - Capturing at %.2lf fps", name, image_count, fps ); last_fps_time = now; static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,CaptureFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf", id, fps, fps ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,CaptureFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index ed8009f84..fb3c24f75 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -338,7 +338,7 @@ void MonitorStream::processCommand( const CmdMsg *msg ) { //exit( -1 ); } } -Debug(2, "NUmber of bytes sent: (%d)", nbytes ); +Debug(2, "NUmber of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes ); // quit after sending a status, if this was a quit request if ( (MsgCommand)msg->msg_data[0]==CMD_QUIT ) { @@ -396,7 +396,7 @@ bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp ) int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); if ( frameSendTime > 1000/maxfps ) { maxfps /= 2; - Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); + Info( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); } last_frame_sent = TV_2_FLOAT( now ); @@ -579,7 +579,6 @@ void MonitorStream::runStream() { gettimeofday( &now, NULL ); if ( connkey ) { -Debug(2, "checking command Queue for connkey: %d", connkey ); while(checkCommandQueue()) { Debug(2, "Have checking command Queue for connkey: %d", connkey ); got_command = true; @@ -664,7 +663,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey ); if ( last_read_index != monitor->shared_data->last_write_index ) { int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary last_read_index = monitor->shared_data->last_write_index; - Debug( 1, "index: %d: frame_mod: %d frame count: %d", index, frame_mod, frame_count ); + Debug( 3, "index: %d: frame_mod: %d frame count: %d", index, frame_mod, frame_count ); if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( !paused && !delayed ) { // Send the next frame @@ -713,11 +712,11 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey ); } // end if buffered playback frame_count++; } else { - Debug(2,"Waiting for capture"); + Debug(5,"Waiting for capture"); } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); - Debug(2, "Sleeping for (%d)", sleep_time); + Debug(4, "Sleeping for (%d)", sleep_time); usleep( sleep_time ); if ( ttl ) { if ( (now.tv_sec - stream_start_time) > ttl ) { diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index 448621921..4ce2e0780 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -99,6 +99,7 @@ void VideoStream::SetupFormat( ) { } #endif } else { + Debug(1,"No allocating priv_data"); s->priv_data = NULL; } @@ -120,7 +121,6 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei /* ffmpeg format matching */ switch(colours) { case ZM_COLOUR_RGB24: - { if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { /* BGR subpixel order */ pf = AV_PIX_FMT_BGR24; @@ -129,9 +129,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei pf = AV_PIX_FMT_RGB24; } break; - } case ZM_COLOUR_RGB32: - { if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ pf = AV_PIX_FMT_ARGB; @@ -146,7 +144,6 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei pf = AV_PIX_FMT_RGBA; } break; - } case ZM_COLOUR_GRAY8: pf = AV_PIX_FMT_GRAY8; break; @@ -159,6 +156,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei // RTP must have a packet_size. // Not sure what this value should be really... ofc->packet_size = width*height; + Debug(1,"Setting packet_size to %d", ofc->packet_size); if ( of->video_codec == AV_CODEC_ID_NONE ) { // RTP does not have a default codec in ffmpeg <= 0.8. @@ -171,6 +169,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei AVCodec *a = avcodec_find_encoder_by_name(codec_name); if ( a ) { codec_id = a->id; + Debug( 1, "Using codec \"%s\"", codec_name ); } else { #if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); @@ -209,13 +208,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei Fatal( "Could not alloc stream" ); return; } + Debug( 1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1 ); ost->id = ofc->nb_streams - 1; - Debug( 1, "Allocated stream" ); - #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - codec_context = avcodec_alloc_context3(NULL); - avcodec_parameters_to_context(codec_context, ost->codecpar); + + codec_context = avcodec_alloc_context3(NULL); + //avcodec_parameters_to_context(codec_context, ost->codecpar); #else codec_context = ost->codec; #endif @@ -223,7 +222,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei codec_context->codec_id = codec->id; codec_context->codec_type = codec->type; - codec_context->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; + codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; if ( bitrate <= 100 ) { // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. // This gets rid of artifacts in the beginning of the movie; and well, even quality. @@ -246,8 +245,11 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei identically 1. */ codec_context->time_base.den = frame_rate; codec_context->time_base.num = 1; + ost->time_base.den = frame_rate; + ost->time_base.num = 1; + - Debug( 1, "Will encode in %d fps.", codec_context->time_base.den ); + Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height ); /* emit one intra frame every second */ codec_context->gop_size = frame_rate; @@ -258,6 +260,10 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + avcodec_parameters_from_context(ost->codecpar, codec_context); + zm_dump_codecpar(ost->codecpar); #endif } else { Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); @@ -271,7 +277,7 @@ const char *VideoStream::MimeType( ) const { for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) { if ( strcmp( format, mime_data[i].format ) == 0 ) { Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); - return ( mime_data[i].mime_type); + return mime_data[i].mime_type; } } const char *mime_type = of->mime_type; @@ -282,9 +288,9 @@ const char *VideoStream::MimeType( ) const { Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); } - Debug( 1, "MimeType is \"%s\"", mime_type ); + Debug(1, "MimeType is \"%s\"", mime_type ); - return ( mime_type); + return mime_type; } void VideoStream::OpenStream( ) { @@ -293,11 +299,13 @@ void VideoStream::OpenStream( ) { /* now that all the parameters are set, we can open the video codecs and allocate the necessary encode buffers */ if ( ost ) { + Debug(1,"Opening codec"); + /* open the codec */ #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - if ( (ret = avcodec_open( codec_context, codec )) < 0 ) + if ( (ret = avcodec_open(codec_context, codec)) < 0 ) #else - if ( (ret = avcodec_open2( codec_context, codec, 0 )) < 0 ) + if ( (ret = avcodec_open2(codec_context, codec, 0)) < 0 ) #endif { Fatal( "Could not open codec. Error code %d \"%s\"", ret, av_err2str(ret) ); @@ -310,6 +318,9 @@ void VideoStream::OpenStream( ) { if ( !opicture ) { Panic( "Could not allocate opicture" ); } + opicture->width = codec_context->width; + opicture->height = codec_context->height; + opicture->format = codec_context->pix_fmt; #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) int size = av_image_get_buffer_size( codec_context->pix_fmt, codec_context->width, codec_context->height, 1 ); @@ -362,7 +373,7 @@ void VideoStream::OpenStream( ) { tmp_opicture_buf, pf, codec_context->width, codec_context->height ); #endif } - } + } // end if ost /* open the output file, if needed */ if ( !(of->flags & AVFMT_NOFILE) ) { @@ -384,8 +395,8 @@ void VideoStream::OpenStream( ) { video_outbuf = NULL; #if LIBAVFORMAT_VERSION_CHECK(57, 0, 0, 0, 0) - if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && - codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) { + if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && + codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) { #else if ( !(of->flags & AVFMT_RAWPICTURE) ) { #endif @@ -408,7 +419,7 @@ void VideoStream::OpenStream( ) { #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) ret = av_write_header( ofc ); #else - ret = avformat_write_header( ofc, NULL ); + ret = avformat_write_header(ofc, NULL); #endif if ( ret < 0 ) { @@ -451,6 +462,7 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi } } + codec_context = NULL; SetupFormat( ); SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); SetParameters( ); @@ -466,7 +478,6 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi Fatal("pthread_mutex_init failed"); } - codec_context = NULL; } VideoStream::~VideoStream( ) { @@ -627,7 +638,6 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, opicture_ptr->quality = codec_context->global_quality; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode avcodec_send_frame(codec_context, opicture_ptr); int ret = avcodec_receive_packet(codec_context, pkt); if ( ret < 0 ) { diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index f28f2794a..1a0e74560 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -75,7 +75,6 @@ void StreamBase::updateFrameRate( double fps ) { bool StreamBase::checkCommandQueue() { if ( sd >= 0 ) { - Debug(2, "sd is (%d)", sd ); CmdMsg msg; memset( &msg, 0, sizeof(msg) ); int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index d530c6716..e074b1d9d 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -235,7 +235,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, #else avformat_new_stream(oc, (AVCodec *)audio_in_ctx->codec); #endif - if (!audio_out_stream) { + if ( !audio_out_stream ) { Error("Unable to create audio out stream\n"); audio_out_stream = NULL; } else { diff --git a/src/zmc.cpp b/src/zmc.cpp index 5169b0699..be1ece3a5 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -223,7 +223,7 @@ int main(int argc, char *argv[]) { Info("Starting Capture version %s", ZM_VERSION); static char sql[ZM_SQL_SML_BUFSIZ]; for ( int i = 0; i < n_monitors; i ++ ) { - snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (Id, Status ) VALUES ('%d','Running')", monitors[i]->Id() ); + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } @@ -238,102 +238,119 @@ int main(int argc, char *argv[]) { sigaddset(&block_set, SIGUSR1); sigaddset(&block_set, SIGUSR2); - monitors[0]->setStartupTime((time_t)time(NULL)); - if ( monitors[0]->PrimeCapture() < 0 ) { - Error("Failed to prime capture of initial monitor"); - exit(-1); - } - - long *capture_delays = new long[n_monitors]; - long *alarm_capture_delays = new long[n_monitors]; - long *next_delays = new long[n_monitors]; - struct timeval * last_capture_times = new struct timeval[n_monitors]; - for ( int i = 0; i < n_monitors; i++ ) { - last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; - capture_delays[i] = monitors[i]->GetCaptureDelay(); - alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); - } - int result = 0; - struct timeval now; - struct DeltaTimeval delta_time; - while ( !zm_terminate ) { - sigprocmask(SIG_BLOCK, &block_set, 0); - for ( int i = 0; i < n_monitors; i++ ) { - long min_delay = MAXINT; - gettimeofday(&now, NULL); - for ( int j = 0; j < n_monitors; j++ ) { - if ( last_capture_times[j].tv_sec ) { - DELTA_TIMEVAL(delta_time, now, last_capture_times[j], DT_PREC_3); - if ( monitors[i]->GetState() == Monitor::ALARM ) - next_delays[j] = alarm_capture_delays[j]-delta_time.delta; - else - next_delays[j] = capture_delays[j]-delta_time.delta; - if ( next_delays[j] < 0 ) - next_delays[j] = 0; - } else { - next_delays[j] = 0; - } - if ( next_delays[j] <= min_delay ) { - min_delay = next_delays[j]; - } - } - - if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) { - if ( monitors[i]->PreCapture() < 0 ) { - Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - zm_terminate = true; - result = -1; - break; - } - if ( monitors[i]->Capture() < 0 ) { - Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - zm_terminate = true; - result = -1; - break; - } - if ( monitors[i]->PostCapture() < 0 ) { - Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - zm_terminate = true; - result = -1; - break; - } - - if ( next_delays[i] > 0 ) { - gettimeofday(&now, NULL); - DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); - long sleep_time = next_delays[i]-delta_time.delta; - if ( sleep_time > 0 ) { - usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3)); - } - } - gettimeofday(&(last_capture_times[i]), NULL); - } // end if next_delay <= min_delay || next_delays[i] <= 0 ) - - } // end foreach n_monitors - sigprocmask(SIG_UNBLOCK, &block_set, 0); - if ( zm_reload ) { - for ( int i = 0; i < n_monitors; i++ ) { - monitors[i]->Reload(); - } - logTerm(); - logInit( log_id_string ); - zm_reload = false; + while( ! zm_terminate ) { + for ( int i = 0; i < n_monitors; i ++ ) { + time_t now = (time_t)time(NULL); + monitors[i]->setStartupTime(now); } - } // end while ! zm_terminate + // Outer primary loop, handles connection to camera + if ( monitors[0]->PrimeCapture() < 0 ) { + Error("Failed to prime capture of initial monitor"); + sleep(10); + continue; + } + static char sql[ZM_SQL_SML_BUFSIZ]; + for ( int i = 0; i < n_monitors; i ++ ) { + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id() ); + if ( mysql_query( &dbconn, sql ) ) { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + } + } + + long *capture_delays = new long[n_monitors]; + long *alarm_capture_delays = new long[n_monitors]; + long *next_delays = new long[n_monitors]; + struct timeval * last_capture_times = new struct timeval[n_monitors]; + for ( int i = 0; i < n_monitors; i++ ) { + last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; + capture_delays[i] = monitors[i]->GetCaptureDelay(); + alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); + } + + struct timeval now; + struct DeltaTimeval delta_time; + while ( !zm_terminate ) { + sigprocmask(SIG_BLOCK, &block_set, 0); + for ( int i = 0; i < n_monitors; i++ ) { + long min_delay = MAXINT; + + gettimeofday(&now, NULL); + for ( int j = 0; j < n_monitors; j++ ) { + if ( last_capture_times[j].tv_sec ) { + DELTA_TIMEVAL(delta_time, now, last_capture_times[j], DT_PREC_3); + if ( monitors[i]->GetState() == Monitor::ALARM ) + next_delays[j] = alarm_capture_delays[j]-delta_time.delta; + else + next_delays[j] = capture_delays[j]-delta_time.delta; + if ( next_delays[j] < 0 ) + next_delays[j] = 0; + } else { + next_delays[j] = 0; + } + if ( next_delays[j] <= min_delay ) { + min_delay = next_delays[j]; + } + } // end foreach monitor + + if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) { + if ( monitors[i]->PreCapture() < 0 ) { + Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + if ( monitors[i]->Capture() < 0 ) { + Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + if ( monitors[i]->PostCapture() < 0 ) { + Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + + if ( next_delays[i] > 0 ) { + gettimeofday(&now, NULL); + DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); + long sleep_time = next_delays[i]-delta_time.delta; + if ( sleep_time > 0 ) { + usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3)); + } + } + gettimeofday(&(last_capture_times[i]), NULL); + } // end if next_delay <= min_delay || next_delays[i] <= 0 ) + + } // end foreach n_monitors + sigprocmask(SIG_UNBLOCK, &block_set, 0); + if ( zm_reload ) { + for ( int i = 0; i < n_monitors; i++ ) { + monitors[i]->Reload(); + } + logTerm(); + logInit( log_id_string ); + zm_reload = false; + } + if ( result < 0 ) { + // Failure, try reconnecting + break; + } + } // end while ! zm_terminate + delete [] alarm_capture_delays; + delete [] capture_delays; + delete [] next_delays; + delete [] last_capture_times; + } // end while ! zm_terminate outer connection loop + for ( int i = 0; i < n_monitors; i++ ) { delete monitors[i]; } delete [] monitors; - delete [] alarm_capture_delays; - delete [] capture_delays; - delete [] next_delays; - delete [] last_capture_times; Image::Deinitialise(); logTerm(); zmDbClose(); - return( result ); + return result; } diff --git a/utils/docker/README.md b/utils/docker/README.md index 741039b3d..0dd76b32b 100644 --- a/utils/docker/README.md +++ b/utils/docker/README.md @@ -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 diff --git a/utils/docker/phpdate.ini b/utils/docker/phpdate.ini deleted file mode 100644 index d35b47361..000000000 --- a/utils/docker/phpdate.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Date] -; Defines the default timezone used by the date functions -; http://php.net/date.timezone -date.timezone = GMT - diff --git a/utils/docker/setup.sh b/utils/docker/setup.sh deleted file mode 100755 index d1572d494..000000000 --- a/utils/docker/setup.sh +++ /dev/null @@ -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 diff --git a/utils/docker/start.sh b/utils/docker/start.sh deleted file mode 100755 index 599b6fcb2..000000000 --- a/utils/docker/start.sh +++ /dev/null @@ -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 -} diff --git a/version b/version index 9e60186a4..71736e954 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.28 +1.31.32 diff --git a/web/ajax/add_monitors.php b/web/ajax/add_monitors.php index 65c25d85d..c5664f06b 100644 --- a/web/ajax/add_monitors.php +++ b/web/ajax/add_monitors.php @@ -12,9 +12,33 @@ $defaultMonitor->set(array( ) ); function probe( &$url_bits ) { + error_reporting(0); global $defaultMonitor; $available_streams = array(); if ( ! isset($url_bits['port']) ) { + + $cam_list_html = file_get_contents('http://'.$url_bits['host'].':5000/monitoring/'); + if ( $cam_list_html ) { + Logger::Debug("Have content at port 5000/monitoring"); + $matches_count = preg_match_all( + '/([^<]+)<\/a>/', + $cam_list_html, $cam_list ); + Logger::Debug(print_r($cam_list,true)); + } + if ( $matches_count ) { + for( $index = 0; $index < $matches_count; $index ++ ) { + $new_stream = $url_bits; // make a copy + $new_stream['port'] = $cam_list[2][$index]; + $new_stream['Name'] = trim($cam_list[3][$index]); + if ( ! isset($new_stream['scheme'] ) ) + $new_stream['scheme'] = 'http'; + $available_streams[] = $new_stream; +Logger::Debug("Have new stream " . print_r($new_stream,true) ); + } + } else { + Info('No matches'); + } +if ( 0 ) { // No port given, do a port scan foreach ( range( 2000, 2007 ) as $port ) { $socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); @@ -61,15 +85,17 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); $available_streams[] = $new_stream; } // end if new_Stream } // end foreach port to scan +} # end if 0 } else { // A port was specified, so don't need to port scan. $available_streams[] = $url_bits; } foreach ( $available_streams as &$stream ) { # check for existence in db. - $stream['url'] = unparse_url( $stream, array( 'path'=>'/','query'=>'action=stream' ) ); - $monitors = Monitor::find_all( array( 'Path'=>$stream['url'] ) ); - if ( count($monitors ) ) { + $stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') ); + $monitors = Monitor::find_all( array('Path'=>$stream['url']) ); + if ( count($monitors) ) { + Info("Found monitors matching " . $stream['url'] ); $stream['Monitor'] = $monitors[0]; if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) { $stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n"; @@ -78,11 +104,14 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); $stream['Warning'] .= 'Monitor height ('.$stream['Monitor']->Height().') and stream width ('.$stream['Height'].") do not match!\n"; } } else { - $stream['Monitor'] = $defaultMonitor; + $stream['Monitor'] = clone $defaultMonitor; if ( isset($stream['Width']) ) { $stream['Monitor']->Width( $stream['Width'] ); $stream['Monitor']->Height( $stream['Height'] ); } + if ( isset($stream['Name']) ) { + $stream['Monitor']->Name( $stream['Name'] ); + } } // Monitor found or not } // end foreach Stream diff --git a/web/ajax/status.php b/web/ajax/status.php index 2e9977a3d..3b92f1dd2 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -382,6 +382,11 @@ function getNearEvents() { else $midSql = ''; + # When listing, it may make sense to list them in descending order. But when viewing Prev should timewise earlier and Next should be after. + if ( $sortColumn == 'E.Id' or $sortColumn == 'E.StartTime' ) { + $sortOrder = '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' ) ) { diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 94b621ed4..57e138254 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -1,31 +1,36 @@ 0 ) { - if ( count($rSockets) != 1 ) { - Error( "Bogus return from select, ".count($rSockets).' sockets available' ); - ajaxError( "Bogus return from select, ".count($rSockets).' sockets available' ); + //Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); + usleep(1000); } -} -switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) { + if ( !file_exists($remSockFile) ) { + ajaxError("Socket $remSockFile does not exist. This file is created by zms, and since it does not exist, either zms did not run, or zms exited early. Please check your zms logs and ensure that CGI is enabled in apache and check that the PATH_ZMS is set correctly. Make sure that ZM is actually recording. If you are trying to view a live stream and the capture process (zmc) is not running then zms will exit. Please go to http://zoneminder.readthedocs.io/en/latest/faq.html#why-can-t-i-see-streamed-images-when-i-can-see-stills-in-the-zone-window-etc for more information."); + } else { + if ( !@socket_sendto( $socket, $msg, strlen($msg), 0, $remSockFile ) ) { + ajaxError( "socket_sendto( $remSockFile ) failed: ".socket_strerror(socket_last_error()) ); + } + } + + $rSockets = array( $socket ); + $wSockets = NULL; + $eSockets = NULL; + + $timeout = MSG_TIMEOUT - ( time() - $start_time ); + Logger::Debug("TImeout is: $timeout/1000 seconds. " ); + + $numSockets = socket_select( $rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000 ); + + if ( $numSockets === false ) { + Error('socket_select failed: ' . socket_strerror(socket_last_error()) ); + ajaxError( 'socket_select failed: '.socket_strerror(socket_last_error()) ); + } else if ( $numSockets < 0 ) { + Error( "Socket closed $remSockFile" ); + ajaxError( "Socket closed $remSockFile" ); + } else if ( $numSockets == 0 ) { + Error( "Timed out waiting for msg $remSockFile" ); + socket_Set_nonblock($socket); + #ajaxError( "Timed out waiting for msg $remSockFile" ); + } else if ( $numSockets > 0 ) { + if ( count($rSockets) != 1 ) { + Error( 'Bogus return from select, '.count($rSockets).' sockets available' ); + ajaxError( 'Bogus return from select, '.count($rSockets).' sockets available' ); + } + } + + switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) { case -1 : { ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) ); @@ -109,10 +118,11 @@ switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFil ajaxError( "Got unexpected message size, got $nbytes, expected ".MSG_DATA_SIZE ); break; } -} + } -$data = unpack( 'ltype', $msg ); -switch ( $data['type'] ) { + + $data = unpack( 'ltype', $msg ); + switch ( $data['type'] ) { case MSG_DATA_WATCH : { $data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg ); @@ -156,15 +166,20 @@ switch ( $data['type'] ) { { ajaxError( "Unexpected received message type '$type'" ); } + } + sem_release($semaphore); +} else { + Logger::Debug("Couldn't get semaphore"); + ajaxResponse( array() ); } -ajaxError( 'Unrecognised action or insufficient permissions' ); +ajaxError('Unrecognised action or insufficient permissions in ajax/stream'); function ajaxCleanup() { - global $socket, $locSockFile; + global $socket, $localSocketFile; if ( !empty( $socket ) ) @socket_close( $socket ); - if ( !empty( $locSockFile ) ) - @unlink( $locSockFile ); + if ( !empty( $localSocketFile ) ) + @unlink( $localSocketFile ); } ?> diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index c372463c1..660bee5ce 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -148,13 +148,27 @@ class MonitorsController extends AppController { throw new NotFoundException(__('Invalid monitor')); } if ($this->Session->Read('monitorPermission') != 'Edit') { - throw new UnauthorizedException(__('Insufficient privileges')); + throw new UnauthorizedException(__('Insufficient privileges')); return; } - if ($this->Monitor->save($this->request->data)) { + + $message = ''; + if ( $this->Monitor->save($this->request->data) ) { $message = 'Saved'; + $Monitor = $this->Monitor->find('first', array( + 'fields' => array('Function','ServerId'), + 'conditions' => array('Id' => $id) + ))['Monitor']; + + // - restart or stop this monitor after change + $func = $Monitor['Function']; + // We don't pass the request data as the monitor object because it may be a subset of the full monitor array + $this->daemonControl( $this->Monitor->id, 'stop' ); + if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { + $this->daemonControl( $this->Monitor->id, 'start' ); + } } else { - $message = 'Error'; + $message = 'Error ' . print_r($this->Monitor->invalidFields(), true); } $this->set(array( @@ -162,18 +176,6 @@ class MonitorsController extends AppController { '_serialize' => array('message') )); - $Monitor = $this->Monitor->find('first', array( - 'fields' => array('Function','ServerId'), - 'conditions' => array('Id' => $id) - ))['Monitor']; - - // - restart or stop this monitor after change - $func = $Monitor['Function']; - // We don't pass the request data as the monitor object because it may be a subset of the full monitor array - $this->daemonControl( $this->Monitor->id, 'stop' ); - if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { - $this->daemonControl( $this->Monitor->id, 'start' ); - } } // end function edit /** diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index c9697b694..98ea21841 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -110,13 +110,13 @@ class Monitor extends AppModel { ); public $actsAs = array( 'CakePHP-Enum-Behavior.Enum' => array( - 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'), - 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), - 'Orientation' => array('0','90','180','270','hori','vert'), - 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), - 'OutputContainer' => array('auto','mp4','mkv'), - 'DefaultView' => array('Events','Control'), - 'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'), + 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'), + 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), + 'Orientation' => array('0','90','180','270','hori','vert'), + 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), + 'OutputContainer' => array('auto','mp4','mkv'), + 'DefaultView' => array('Events','Control'), + 'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'), ) ); diff --git a/web/api/app/Plugin/CakePHP-Enum-Behavior b/web/api/app/Plugin/CakePHP-Enum-Behavior index 7108489f2..ca91b87fd 160000 --- a/web/api/app/Plugin/CakePHP-Enum-Behavior +++ b/web/api/app/Plugin/CakePHP-Enum-Behavior @@ -1 +1 @@ -Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237 +Subproject commit ca91b87fda8e006e4fca2ed870f24f9a29c2905d diff --git a/web/includes/Group.php b/web/includes/Group.php index 543665655..ee86a320c 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -249,7 +249,7 @@ $group_options[$Group->Id()] = str_repeat( ' ', $depth ) . $Group->Name(); $MonitorIds = array_merge( $MonitorIds, dbFetchAll( 'SELECT MonitorId FROM Groups_Monitors WHERE GroupId IN (SELECT Id FROM Groups WHERE ParentId = ?)', 'MonitorId', array($group_id) ) ); } - $groupSql = " find_in_set( Id, '".implode( ',', $MonitorIds )."' )"; + $groupSql = " find_in_set( M.Id, '".implode( ',', $MonitorIds )."' )"; } return $groupSql; } # end public static function get_group_sql( $group_id ) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index e98270282..6fb19c1ed 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -5,20 +5,22 @@ require_once( 'Server.php' ); class Monitor { private $defaults = array( -'Id' => null, -'Name' => '', -'StorageId' => 0, -'ServerId' => 0, -'Function' => 'None', -'Enabled' => 1, -'Width' => null, -'Height' => null, -'Orientation' => null, -'AnalysisFPSLimit' => null, -'AnalysisFPS' => null, -'CaptureFPS' => null, -'ZoneCount' => 0, -'Triggers' => null, + 'Id' => null, + 'Name' => '', + 'StorageId' => 0, + 'ServerId' => 0, + 'Function' => 'None', + 'Enabled' => 1, + 'Width' => null, + 'Height' => null, + 'Orientation' => null, + 'AnalysisFPSLimit' => null, + 'ZoneCount' => 0, + 'Triggers' => null, +); +private $status_fields = array( + 'AnalysisFPS' => null, + 'CaptureFPS' => null, ); private $control_fields = array( 'Name' => '', @@ -312,7 +314,7 @@ private $control_fields = array( } // end function save function zmcControl( $mode=false ) { - if ( (!defined('ZM_SERVER_ID')) or ( ZM_SERVER_ID==$this->{'ServerId'} ) ) { + if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { if ( $this->{'Type'} == 'Local' ) { $zmcArgs = '-d '.$this->{'Device'}; } else { @@ -329,7 +331,7 @@ private $control_fields = array( daemonControl( 'start', 'zmc', $zmcArgs ); } } - } else { + } else if ( $this->ServerId() ) { $Server = $this->Server(); $url = $Server->Url() . '/zm/api/monitors/'.$this->{'Id'}.'.json'; @@ -367,7 +369,7 @@ Logger::Debug("sending command to $url"); } // end function zmcControl function zmaControl( $mode=false ) { - if ( (!defined('ZM_SERVER_ID')) or ( ZM_SERVER_ID==$this->{'ServerId'} ) ) { + if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) { if ( ZM_OPT_CONTROL ) { daemonControl( 'stop', 'zmtrack.pl', '-m '.$this->{'Id'} ); diff --git a/web/includes/actions.php b/web/includes/actions.php index 54941ab9f..3c55af3db 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -126,7 +126,7 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're userLogin( $username, $password ); $refreshParent = true; $view = 'console'; - $redirect = true; + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console'; } else if ( $action == 'logout' ) { userLogout(); $refreshParent = true; @@ -483,6 +483,18 @@ if ( canEdit( 'Monitors' ) ) { 'RecordAudio' => 'toggle', ); + if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { + Logger::Debug("Auto selecting server"); + $_REQUEST['newMonitor']['ServerId'] = dbFetchOne( 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem ASC, CpuLoad ASC LIMIT 1', 'Id' ); + Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); + if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { + $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; + Logger::Debug("Auto selecting server to " . ZM_SERVER_ID); + } + } else { + Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']); + } + $columns = getTableColumns( 'Monitors' ); $changes = getFormChanges( $monitor, $_REQUEST['newMonitor'], $types, $columns ); @@ -538,22 +550,26 @@ if ( canEdit( 'Monitors' ) ) { } } $restart = true; - } elseif ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors + } else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors # FIXME This is actually a race condition. Should lock the table. - $maxSeq = dbFetchOne( 'SELECT max(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence' ); + $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence'); $changes[] = 'Sequence = '.($maxSeq+1); - dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ); - $mid = dbInsertId(); - $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; - dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); - //$view = 'none'; - $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); - mkdir( $Storage->Path().'/'.$mid, 0755 ); - $saferName = basename($_REQUEST['newMonitor']['Name']); - symlink( $mid, $Storage->Path().'/'.$saferName ); - if ( isset($_COOKIE['zmGroup']) ) { - dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); + if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) { + $mid = dbInsertId(); + $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; + dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); + //$view = 'none'; + $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); + mkdir( $Storage->Path().'/'.$mid, 0755 ); + $saferName = basename($_REQUEST['newMonitor']['Name']); + symlink( $mid, $Storage->Path().'/'.$saferName ); + if ( isset($_COOKIE['zmGroup']) ) { + dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); + } + } else { + Error("Error saving new Monitor."); + return; } } else { Error("Users with Monitors restrictions cannot create new monitors."); @@ -726,7 +742,7 @@ if ( canEdit( 'System' ) ) { $_SESSION['zmMontageLayout'] = $Layout->Id(); setcookie('zmMontageLayout', $Layout->Id(), 1 ); session_write_close(); - $redirect = true; + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview'; } // end if save } else if ( $_REQUEST['object'] == 'server' ) { @@ -893,6 +909,7 @@ if ( canEdit( 'System' ) ) { case 'lowband' : break; } + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab']; } loadConfig( false ); } elseif ( $action == 'user' ) { diff --git a/web/includes/database.php b/web/includes/database.php index 6bc6c0264..250e5fce8 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -98,7 +98,7 @@ function dbLog( $sql, $update=false ) { } function dbError( $sql ) { - Fatal( "SQL-ERR '".$dbConn->errorInfo()."', statement was '".$sql."'" ); + Error( "SQL-ERR '".$dbConn->errorInfo()."', statement was '".$sql."'" ); } function dbEscape( $string ) { @@ -142,21 +142,26 @@ function dbQuery( $sql, $params=NULL ) { } } catch(PDOException $e) { Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') ); + return NULL; } - return( $result ); + return $result; } function dbFetchOne( $sql, $col=false, $params=NULL ) { $result = dbQuery( $sql, $params ); if ( ! $result ) { - Fatal( "SQL-ERR dbFetchOne no result, statement was '".$sql."'" . ( $params ? 'params: ' . join(',',$params) : '' ) ); + Error( "SQL-ERR dbFetchOne no result, statement was '".$sql."'" . ( $params ? 'params: ' . join(',',$params) : '' ) ); + return false; + } + if ( ! $result->rowCount() ) { + # No rows is not an error return false; } if ( $result && $dbRow = $result->fetch( PDO::FETCH_ASSOC ) ) { if ( $col ) { if ( ! isset( $dbRow[$col] ) ) { - Warning( "$col does not exist in the returned row" ); + Warning( "$col does not exist in the returned row " . print_r($dbRow, true) ); } return $dbRow[$col]; } @@ -168,7 +173,7 @@ function dbFetchOne( $sql, $col=false, $params=NULL ) { function dbFetchAll( $sql, $col=false, $params=NULL ) { $result = dbQuery( $sql, $params ); if ( ! $result ) { - Fatal( "SQL-ERR dbFetchAll no result, statement was '".$sql."'" . ( $params ? 'params: ' .join(',', $params) : '' ) ); + Error( "SQL-ERR dbFetchAll no result, statement was '".$sql."'" . ( $params ? 'params: ' .join(',', $params) : '' ) ); return false; } diff --git a/web/includes/functions.php b/web/includes/functions.php index 2214d1cff..49c3f7a73 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -269,14 +269,14 @@ function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { if ( isWindows() ) { return ' @@ -309,13 +309,13 @@ function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { { return ' 'Ok' ); - if ( is_array( $result ) ) + if ( is_array( $result ) ) { $response = array_merge( $response, $result ); - elseif ( !empty($result) ) + } elseif ( !empty($result) ) { $response['message'] = $result; + } header( 'Content-type: text/plain' ); exit( jsonEncode( $response ) ); } diff --git a/web/index.php b/web/index.php index c3f2269fe..6fbc435f7 100644 --- a/web/index.php +++ b/web/index.php @@ -173,18 +173,17 @@ if ( isset($_REQUEST['request']) ) foreach ( getSkinIncludes( 'skin.php' ) as $includeFile ) require_once $includeFile; -if ( ZM_OPT_USE_AUTH && ZM_AUTH_HASH_LOGINS ) { - Logger::Debug("Useing hash"); - if ( empty($user) && ! empty($_REQUEST['auth']) ) { - if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { - userLogin( $authUser['Username'], $authUser['Password'], true ); - } - } else if ( ! empty($user) ) { - Logger::Debug("generating hash"); +if ( ZM_OPT_USE_AUTH ) { + if ( ZM_AUTH_HASH_LOGINS ) { + if ( empty($user) && ! empty($_REQUEST['auth']) ) { + if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { + userLogin( $authUser['Username'], $authUser['Password'], true ); + } + } + } + if ( ! empty($user) ) { // generate it once here, while session is open. Value will be cached in session and return when called later on generateAuthHash( ZM_AUTH_HASH_IPS ); - } else { - Logger::Debug(" not generating hash"); } } @@ -218,11 +217,11 @@ if ( ZM_OPT_USE_AUTH and ! isset($user) ) { session_write_close(); if ( $redirect ) { - header('Location: '.ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view='.$view); + header('Location: '.$redirect); return; } -if ( isset( $_REQUEST['request'] ) ) { +if ( $request ) { foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) { if ( !file_exists( $includeFile ) ) Fatal( "Request '$request' does not exist" ); diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index f384594da..246554564 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -71,7 +71,9 @@ html ul.tabs li.active, html ul.tabs li.active a:hover { } --> - + diff --git a/web/skins/classic/views/js/add_monitors.js b/web/skins/classic/views/js/add_monitors.js index a94fd2200..d8ad8e1ac 100644 --- a/web/skins/classic/views/js/add_monitors.js +++ b/web/skins/classic/views/js/add_monitors.js @@ -8,21 +8,22 @@ function probe( url_e ) { var ProbeResults; function getProbeResponse( respObj, respText ) { - if ( checkStreamForErrors( "getProbeResponse", respObj ) ) + if ( checkStreamForErrors( "getProbeResponse", respObj ) ) { + console.log(respText); return; -//alert(respText); + } - if ( respObj.Streams ) { + if ( respObj.Streams && respObj.Streams.length ) { parseStreams( respObj.Streams ); - } else { - alert("No Streams"); + //} else { +//console.log("No streams: " + respText); } } // end function getProbeResponse function parseStreams( Streams ) { ProbeResults = Array(); - var results_div = $j('#url_results')[0]; + var results_div = $j('#results')[0]; if ( ! results_div ) { console.log("No results div found."); return; @@ -33,7 +34,7 @@ function parseStreams( Streams ) { for( i in Streams ) { var stream = Streams[i]; if ( stream.url ) { - html += '

'+stream.url; + html += '

'+stream.Monitor.Name + ' at ' + stream.url; if ( stream.Monitor.Id ) { html += ' is already entered into the system by Monitor ' + stream.Monitor.Id + ' ' + stream.Monitor.Name + '
'; html += ''; diff --git a/web/skins/classic/views/js/console.js b/web/skins/classic/views/js/console.js index 8a7342897..2121b6e39 100644 --- a/web/skins/classic/views/js/console.js +++ b/web/skins/classic/views/js/console.js @@ -34,8 +34,11 @@ function addMonitor(element) { } } } - dupParam = (monitorId == -1 ) ? '': '&dupId='+monitorId; - createPopup( '?view=monitor'+dupParam, 'zmMonitor0', 'monitor' ); + if ( monitorId != -1 ) { + createPopup( '?view=monitor&dupId='+monitorId, 'zmMonitor0', 'monitor' ); + } else { + window.location = '?view=add_monitors'; + } } function editMonitor( element ) { diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 73d0b132b..152fdcd9b 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -56,64 +56,66 @@ function Monitor( monitorData ) { var stream = $j('#liveStream'+this.id)[0]; if ( respObj.result == 'Ok' ) { - this.status = respObj.status; - this.alarmState = this.status.state; + if ( respObj.status ) { + this.status = respObj.status; + this.alarmState = this.status.state; - var stateClass = ""; - if ( this.alarmState == STATE_ALARM ) - stateClass = "alarm"; - else if ( this.alarmState == STATE_ALERT ) - stateClass = "alert"; - else - stateClass = "idle"; + var stateClass = ""; + if ( this.alarmState == STATE_ALARM ) + stateClass = "alarm"; + else if ( this.alarmState == STATE_ALERT ) + stateClass = "alert"; + else + stateClass = "idle"; - if ( !COMPACT_MONTAGE ) { - $('fpsValue'+this.id).set( 'text', this.status.fps ); - $('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] ); - this.setStateClass( $('monitorState'+this.id), stateClass ); - } - this.setStateClass( $('monitor'+this.id), stateClass ); + if ( !COMPACT_MONTAGE ) { + $('fpsValue'+this.id).set( 'text', this.status.fps ); + $('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] ); + this.setStateClass( $('monitorState'+this.id), stateClass ); + } + this.setStateClass( $('monitor'+this.id), stateClass ); - /*Stream could be an applet so can't use moo tools*/ - stream.className = stateClass; + /*Stream could be an applet so can't use moo tools*/ + stream.className = stateClass; - var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ); - var wasAlarmed = ( this.lastAlarmState == STATE_ALARM || this.lastAlarmState == STATE_ALERT ); + var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ); + var wasAlarmed = ( this.lastAlarmState == STATE_ALARM || this.lastAlarmState == STATE_ALERT ); - var newAlarm = ( isAlarmed && !wasAlarmed ); - var oldAlarm = ( !isAlarmed && wasAlarmed ); + var newAlarm = ( isAlarmed && !wasAlarmed ); + var oldAlarm = ( !isAlarmed && wasAlarmed ); - if ( newAlarm ) { + if ( newAlarm ) { + if ( false && SOUND_ON_ALARM ) { + // Enable the alarm sound + $('alarmSound').removeClass( 'hidden' ); + } + if ( POPUP_ON_ALARM ) { + windowToFront(); + } + } if ( false && SOUND_ON_ALARM ) { - // Enable the alarm sound - $('alarmSound').removeClass( 'hidden' ); + if ( oldAlarm ) { + // Disable alarm sound + $('alarmSound').addClass( 'hidden' ); + } } - if ( POPUP_ON_ALARM ) { - windowToFront(); - } - } - if ( false && SOUND_ON_ALARM ) { - if ( oldAlarm ) { - // Disable alarm sound - $('alarmSound').addClass( 'hidden' ); - } - } - if ( this.status.auth ) { - if ( this.status.auth != auth_hash ) { - // Try to reload the image stream. - if ( stream ) - stream.src = stream.src.replace( /auth=\w+/i, 'auth='+this.status.auth ); - console.log("Changed auth from " + auth_hash + " to " + this.status.auth ); - auth_hash = this.status.auth; - } - } // end if have a new auth hash + if ( this.status.auth ) { + if ( this.status.auth != auth_hash ) { + // Try to reload the image stream. + if ( stream ) + stream.src = stream.src.replace( /auth=\w+/i, 'auth='+this.status.auth ); + console.log("Changed auth from " + auth_hash + " to " + this.status.auth ); + auth_hash = this.status.auth; + } + } // end if have a new auth hash + } // end if has state } else { console.error( respObj.message ); // Try to reload the image stream. if ( stream ) { if ( stream.src ) { - console.log('Reloading stream: ' + stream.src ); - stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); + console.log('Reloading stream: ' + stream.src ); + stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); } else { } } else { diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index b6470da3f..7f749d47a 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -35,7 +35,7 @@ monitorData[monitorData.length] = { 'connKey': connKey() ?>, 'width': Width() ?>, 'height':Height() ?>, - 'server_url': 'Server()->Url().(ZM_MIN_STREAMING_PORT?':'.(ZM_MIN_STREAMING_PORT+$monitor->Id()):'').$_SERVER['PHP_SELF'] ?>', + 'server_url': 'Server()->Url().$_SERVER['PHP_SELF'] ?>', 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );} }; 95 ) - $('levelValue').className = "alarm"; - else if ( streamStatus.level > 80 ) - $('levelValue').className = "alert"; - else - $('levelValue').className = "ok"; + $('levelValue').set( 'text', streamStatus.level ); + if ( streamStatus.level > 95 ) + $('levelValue').className = "alarm"; + else if ( streamStatus.level > 80 ) + $('levelValue').className = "alert"; + else + $('levelValue').className = "ok"; - var delayString = secsToTime( streamStatus.delay ); + var delayString = secsToTime( streamStatus.delay ); - if ( streamStatus.paused == true ) { - $('modeValue').set( 'text', "Paused" ); - $('rate').addClass( 'hidden' ); - $('delayValue').set( 'text', delayString ); - $('delay').removeClass( 'hidden' ); - $('level').removeClass( 'hidden' ); - streamCmdPause( false ); - } else if ( streamStatus.delayed == true ) { - $('modeValue').set( 'text', "Replay" ); - $('rateValue').set( 'text', streamStatus.rate ); - $('rate').removeClass( 'hidden' ); - $('delayValue').set( 'text', delayString ); - $('delay').removeClass( 'hidden' ); - $('level').removeClass( 'hidden' ); - if ( streamStatus.rate == 1 ) { - streamCmdPlay( false ); - } else if ( streamStatus.rate > 0 ) { - if ( streamStatus.rate < 1 ) - streamCmdSlowFwd( false ); - else - streamCmdFastFwd( false ); - } else { - if ( streamStatus.rate > -1 ) - streamCmdSlowRev( false ); - else - streamCmdFastRev( false ); - } // rate - } else { - $('modeValue').set( 'text', "Live" ); - $('rate').addClass( 'hidden' ); - $('delay').addClass( 'hidden' ); - $('level').addClass( 'hidden' ); - streamCmdPlay( false ); - } // end if paused or delayed - - $('zoomValue').set( 'text', streamStatus.zoom ); - if ( streamStatus.zoom == "1.0" ) - setButtonState( $('zoomOutBtn'), 'unavail' ); - else - setButtonState( $('zoomOutBtn'), 'inactive' ); - - if ( canEditMonitors ) { - if ( streamStatus.enabled ) { - $('enableAlarmsLink').addClass( 'hidden' ); - $('disableAlarmsLink').removeClass( 'hidden' ); - if ( streamStatus.forced ) { - $('forceAlarmLink').addClass( 'hidden' ); - $('cancelAlarmLink').removeClass( 'hidden' ); + if ( streamStatus.paused == true ) { + $('modeValue').set( 'text', "Paused" ); + $('rate').addClass( 'hidden' ); + $('delayValue').set( 'text', delayString ); + $('delay').removeClass( 'hidden' ); + $('level').removeClass( 'hidden' ); + streamCmdPause( false ); + } else if ( streamStatus.delayed == true ) { + $('modeValue').set( 'text', "Replay" ); + $('rateValue').set( 'text', streamStatus.rate ); + $('rate').removeClass( 'hidden' ); + $('delayValue').set( 'text', delayString ); + $('delay').removeClass( 'hidden' ); + $('level').removeClass( 'hidden' ); + if ( streamStatus.rate == 1 ) { + streamCmdPlay( false ); + } else if ( streamStatus.rate > 0 ) { + if ( streamStatus.rate < 1 ) + streamCmdSlowFwd( false ); + else + streamCmdFastFwd( false ); } else { - $('forceAlarmLink').removeClass( 'hidden' ); - $('cancelAlarmLink').addClass( 'hidden' ); - } - $('forceCancelAlarm').removeClass( 'hidden' ); + if ( streamStatus.rate > -1 ) + streamCmdSlowRev( false ); + else + streamCmdFastRev( false ); + } // rate } else { - $('enableAlarmsLink').removeClass( 'hidden' ); - $('disableAlarmsLink').addClass( 'hidden' ); - $('forceCancelAlarm').addClass( 'hidden' ); - } - $('enableDisableAlarms').removeClass( 'hidden' ); - } // end if canEditMonitors + $('modeValue').set( 'text', "Live" ); + $('rate').addClass( 'hidden' ); + $('delay').addClass( 'hidden' ); + $('level').addClass( 'hidden' ); + streamCmdPlay( false ); + } // end if paused or delayed - if ( streamStatus.auth ) { - console.log("Haev a new auth hash" + streamStatus.auth); - // Try to reload the image stream. - var streamImg = $('liveStream'); - if ( streamImg ) - streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); - } // end if haev a new auth hash + $('zoomValue').set( 'text', streamStatus.zoom ); + if ( streamStatus.zoom == "1.0" ) + setButtonState( $('zoomOutBtn'), 'unavail' ); + else + setButtonState( $('zoomOutBtn'), 'inactive' ); + + if ( canEditMonitors ) { + if ( streamStatus.enabled ) { + $('enableAlarmsLink').addClass( 'hidden' ); + $('disableAlarmsLink').removeClass( 'hidden' ); + if ( streamStatus.forced ) { + $('forceAlarmLink').addClass( 'hidden' ); + $('cancelAlarmLink').removeClass( 'hidden' ); + } else { + $('forceAlarmLink').removeClass( 'hidden' ); + $('cancelAlarmLink').addClass( 'hidden' ); + } + $('forceCancelAlarm').removeClass( 'hidden' ); + } else { + $('enableAlarmsLink').removeClass( 'hidden' ); + $('disableAlarmsLink').addClass( 'hidden' ); + $('forceCancelAlarm').addClass( 'hidden' ); + } + $('enableDisableAlarms').removeClass( 'hidden' ); + } // end if canEditMonitors + + if ( streamStatus.auth ) { + console.log("Haev a new auth hash" + streamStatus.auth); + // Try to reload the image stream. + var streamImg = $('liveStream'); + if ( streamImg ) + streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); + } // end if haev a new auth hash + } // end if respObj.status } else { checkStreamForErrors("getStreamCmdResponse",respObj);//log them // Try to reload the image stream. diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index c0c8ab590..79b48de94 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -48,7 +48,7 @@ var maxDisplayEvents = ; var monitorId = Id() ?>; var monitorWidth = Width() ?>; var monitorHeight = Height() ?>; -var monitorUrl = 'Server()->Url() ) ?>'; +var monitorUrl = 'Server()->Url() . ( ZM_MIN_STREAMING_PORT ? ':'. (ZM_MIN_STREAMING_PORT+$monitor->Id()) : '' ) ) ?>'; var scale = ''; diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index f3818ca16..e057732b2 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -72,11 +72,11 @@ if ( ! $monitor ) { $monitor->set( array( 'Id' => 0, 'Name' => translate('Monitor').'-'.$nextId, - 'Function' => 'Monitor', + 'Function' => 'Mocord', 'Enabled' => true, 'LinkedMonitors' => '', - 'Type' => '', - 'Device' => "/dev/video0", + 'Type' => 'Ffmpeg', + 'Device' => '/dev/video0', 'Channel' => '0', 'Format' => 0x000000ff, 'Protocol' => '', @@ -87,26 +87,26 @@ if ( ! $monitor ) { 'Port' => '80', 'User' => '', 'Pass' => '', - 'Colours' => 3, + 'Colours' => 4, 'Palette' => 0, - 'Width' => '320', - 'Height' => '240', + 'Width' => '1280', + 'Height' => '962', 'Orientation' => '0', 'Deinterlacing' => 0, 'RTSPDescribe' => 0, - 'SaveJPEGs' => '3', - 'VideoWriter' => '0', + 'SaveJPEGs' => '4', + 'VideoWriter' => '1', 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", 'RecordAudio' => '0', 'LabelFormat' => '%N - %d/%m/%y %H:%M:%S', 'LabelX' => 0, 'LabelY' => 0, 'LabelSize' => 1, - 'ImageBufferCount' => 50, - 'WarmupCount' => 25, - 'PreEventCount' => 25, - 'PostEventCount' => 25, - 'StreamReplayBuffer' => 1000, + 'ImageBufferCount' => 40, + 'WarmupCount' => 1, + 'PreEventCount' => 1, + 'PostEventCount' => 5, + 'StreamReplayBuffer' => 0, 'AlarmFrameCount' => 1, 'Controllable' => 0, 'ControlId' => '', @@ -124,9 +124,9 @@ if ( ! $monitor ) { 'EventPrefix' => 'Event-', 'AnalysisFPSLimit' => '', 'AnalysisUpdateDelay' => 0, - 'MaxFPS' => '', - 'AlarmMaxFPS' => '', - 'FPSReportInterval' => 1000, + 'MaxFPS' => '30', + 'AlarmMaxFPS' => '30', + 'FPSReportInterval' => 100, 'RefBlendPerc' => 6, 'AlarmRefBlendPerc' => 6, 'DefaultView' => 'Events', @@ -138,8 +138,8 @@ if ( ! $monitor ) { 'Triggers' => '', 'V4LMultiBuffer' => '', 'V4LCapturesPerFrame' => 1, - 'ServerId' => $Server['Id'], - 'StorageId' => '0', + 'ServerId' => 'auto', + 'StorageId' => '1', ) ); } # end if $_REQUEST['dupID'] } # end if $_REQUEST['mid'] @@ -674,7 +674,7 @@ switch ( $tab ) { 'None'); + $servers = array(''=>'None','auto'=>'Auto'); $result = dbQuery( 'SELECT * FROM Servers ORDER BY Name'); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server' ); foreach ( $results as $row => $server_obj ) { diff --git a/web/skins/classic/views/onvifprobe.php b/web/skins/classic/views/onvifprobe.php index f2ca662cb..f9c766846 100644 --- a/web/skins/classic/views/onvifprobe.php +++ b/web/skins/classic/views/onvifprobe.php @@ -158,7 +158,7 @@ if( !isset($_REQUEST['step']) || ($_REQUEST['step'] == "1")) { */ // $sourceDesc = htmlspecialchars(serialize($camera['monitor'])); $sourceDesc = base64_encode(serialize($camera['monitor'])); - $sourceString = $camera['model'].' @ '.$host; + $sourceString = $camera['model'].' @ '.$host . ' using version ' . $camera['monitor']['SOAP'] ; $cameras[$sourceDesc] = $sourceString; } diff --git a/web/skins/classic/views/postlogin.php b/web/skins/classic/views/postlogin.php index 8177dcd01..f57d5335c 100644 --- a/web/skins/classic/views/postlogin.php +++ b/web/skins/classic/views/postlogin.php @@ -23,7 +23,7 @@ xhtmlHeaders(__FILE__, translate('LoggingIn') );

diff --git a/web/skins/classic/views/server.php b/web/skins/classic/views/server.php index 24f849c22..ae78d622f 100644 --- a/web/skins/classic/views/server.php +++ b/web/skins/classic/views/server.php @@ -32,6 +32,9 @@ if ( $_REQUEST['id'] ) { $newServer = array(); $newServer['Name'] = translate('NewServer'); $newServer['Hostname'] = ''; + $newServer['zmstats'] = ''; + $newServer['zmaudit'] = ''; + $newServer['zmtrigger'] = ''; } $focusWindow = true; diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php index 4b041f0f1..65ed57ea5 100644 --- a/web/skins/classic/views/storage.php +++ b/web/skins/classic/views/storage.php @@ -36,6 +36,7 @@ if ( $_REQUEST['id'] ) { $newStorage['Type'] = 'local'; $newStorage['Scheme'] = 'Medium'; $newStorage['StorageId'] = ''; + $newStorage['ServerId'] = ''; } $type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') ); diff --git a/web/views/archive.php b/web/views/archive.php index ce4e1b274..9238a925e 100644 --- a/web/views/archive.php +++ b/web/views/archive.php @@ -18,45 +18,44 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) -{ - $view = "error"; - return; +if ( !canView( 'Events' ) ) { + $view = 'error'; + return; } $archivetype = $_REQUEST['type']; if ( $archivetype ) { - switch ($archivetype) { - case "tar": - $mimetype = "gzip"; - $file_ext = "tar.gz"; - break; - case "zip": - $mimetype = "zip"; - $file_ext = "zip"; - break; - default: - $mimetype = NULL; - $file_ext = NULL; - } + switch ($archivetype) { + case 'tar': + $mimetype = 'gzip'; + $file_ext = 'tar.gz'; + break; + case 'zip': + $mimetype = 'zip'; + $file_ext = 'zip'; + break; + default: + $mimetype = NULL; + $file_ext = NULL; + } - if ( $mimetype ) { - $filename = "zmExport.$file_ext"; - $filename_path = ZM_DIR_TEMP."/".$filename; - if ( is_readable($filename_path) ) { - header( "Content-type: application/$mimetype" ); - header( "Content-Disposition: attachment; filename=$filename"); - set_time_limit(0); - readfile( $filename_path ); - } else { - Error("$filename_path does not exist or is not readable."); - } + if ( $mimetype ) { + $filename = "zmExport.$file_ext"; + $filename_path = ZM_DIR_EXPORTS.'/'.$filename; + if ( is_readable($filename_path) ) { + header( "Content-type: application/$mimetype" ); + header( "Content-Disposition: attachment; filename=$filename"); + set_time_limit(0); + readfile( $filename_path ); } else { - Error("Unsupported archive type specified. Supported archives are tar and zip"); + Error("$filename_path does not exist or is not readable."); } + } else { + Error("Unsupported archive type specified. Supported archives are tar and zip"); + } } else { - Error("No archive type given to archive.php. Please specify a tar or zip archive."); + Error("No archive type given to archive.php. Please specify a tar or zip archive."); } ?> diff --git a/zmlinkcontent.sh.in b/zmlinkcontent.sh.in index d6c791823..409202cb1 100755 --- a/zmlinkcontent.sh.in +++ b/zmlinkcontent.sh.in @@ -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 <