diff --git a/.github/stale.yml b/.github/stale.yml index 9ff190b1f..4dde9b5a7 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,10 +1,10 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 +daysUntilStale: 180 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 +daysUntilClose: 30 # Issues with these labels will never be considered stale exemptLabels: diff --git a/CMakeLists.txt b/CMakeLists.txt index cca9fe82e..6d453f498 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -875,7 +875,6 @@ configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh # Create a target for man pages include(Pod2Man) -ADD_MANPAGE_TARGET() # Process subdirectories diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake index 734be239b..f1c0e400a 100644 --- a/cmake/Modules/Pod2Man.cmake +++ b/cmake/Modules/Pod2Man.cmake @@ -53,8 +53,7 @@ MACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX) SET(MANPAGE_TARGET "man-${MANFILE}") - ADD_CUSTOM_TARGET(${MANPAGE_TARGET} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz) - ADD_DEPENDENCIES(man ${MANPAGE_TARGET}) + ADD_CUSTOM_TARGET(${MANPAGE_TARGET} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz) INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz diff --git a/conf.d/01-system-paths.conf.in b/conf.d/01-system-paths.conf.in index e1aaf0bef..277f63b70 100644 --- a/conf.d/01-system-paths.conf.in +++ b/conf.d/01-system-paths.conf.in @@ -46,3 +46,6 @@ ZM_PATH_SWAP=@ZM_TMPDIR@ # Full path to optional arp binary # ZoneMinder will find the arp binary automatically on most systems ZM_PATH_ARP="@ZM_PATH_ARP@" + +#Full path to shutdown binary +ZM_PATH_SHUTDOWN="@ZM_PATH_SHUTDOWN@" diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 6b561a116..eb687e2e3 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -476,7 +476,7 @@ CREATE TABLE `Monitors` ( `Hue` mediumint(7) NOT NULL default '-1', `Colour` mediumint(7) NOT NULL default '-1', `EventPrefix` varchar(32) NOT NULL default 'Event-', - `LabelFormat` varchar(64) default '%N - %y/%m/%d %H:%M:%S', + `LabelFormat` varchar(64), `LabelX` smallint(5) unsigned NOT NULL default '0', `LabelY` smallint(5) unsigned NOT NULL default '0', `LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1', diff --git a/db/zm_update-1.34.20.sql b/db/zm_update-1.34.20.sql new file mode 100644 index 000000000..3512534be --- /dev/null +++ b/db/zm_update-1.34.20.sql @@ -0,0 +1,2 @@ +/* This was done in 1.31.0 but zm_create.sql.in wasn't updated to match. */ +ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255); diff --git a/distros/beowulf/NEWS b/distros/beowulf/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/beowulf/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/beowulf/README.Debian b/distros/beowulf/README.Debian new file mode 100644 index 000000000..4fe3464d2 --- /dev/null +++ b/distros/beowulf/README.Debian @@ -0,0 +1,130 @@ +Zoneminder for Debian +--------------------- + +Initializing database +--------------------- + + pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf +OR + cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf + + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" +accordingly. + +The following command can help to ensure that zoneminder can read its +configuration file: + + chgrp -c www-data /etc/zm/zm.conf + + +Upgrading database +------------------ + +The database is updated automatically on installation. You should not need to take this step. + +Assuming that database is on "localhost" then the following command can be +used to upgrade "zm" database: + + zmupdate.pl + +Additional permissions may be required to perform upgrade: + + echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +The following command prints the current version of zoneminder database: + + echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm + + +Enabling service +---------------- + +By default Zoneminder service is not automatically started and needs to be +manually enabled once database is configured: + + sudo systemctl enable zoneminder.service + + +Web server set-up +----------------- + +There are few manual steps to get the web interface working: + +## Apache2 + +Apache can be configured as folder "/zm" using sample .conf: + + sudo a2enconf zoneminder + +Alternatively Apache web site configuration template can be used to setup +zoneminder as "http://zoneminder": + + sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ + sudo a2ensite zoneminder.conf + +Common configuration steps for Apache2: + + sudo a2enmod cgi + sudo service apache2 reload + + +## nginx / fcgiwrap + +Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package +for binary "cgi-bin" applications: + + sudo apt-get install php-fpm fcgiwrap + +To enable a URL alias that makes Zoneminder available from + + http://yourserver/zm + +the following line is to be added to "server" section of a web site +configuration: + + include /usr/share/doc/zoneminder/examples/nginx.conf; + +For "default" web site it would be sufficient to include the above +statement to the file + + /etc/nginx/sites-enabled/default + +To avoid problems with feeds from multiple cameras "fcgiwrap" should be +configured to start at least as many processes as there are cameras. +It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". +Systemd users may be affected by the following bug: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 + + +## Note: + +When Zoneminder web site is running it may be necessary to set +Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web +site configuration. + + +Changing the location for images and events +------------------------------------------- + +ZoneMinder is now able to be configured to use an alternative location for storing +events and images at compile time. This package makes use of that, so symlinks in +/usr/share/zoneminder/www are no longer necessary. + +Access to /dev/video* +--------------------- + +For cameras which require access to /dev/video*, zoneminder may need the +www-data user added to the video group in order to see those cameras: + + adduser www-data video + +Note that all web applications running on the zoneminder server will then have +access to all video devices on the system. + + -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/beowulf/TODO.Debian b/distros/beowulf/TODO.Debian new file mode 100644 index 000000000..9dc59613b --- /dev/null +++ b/distros/beowulf/TODO.Debian @@ -0,0 +1,12 @@ + +## Separate substantial /usr/share into its own arch-all package. + +## Decide how to handle database updates. + + * Consider possibility that database may be on another machine (#469239). + * Consider dbconfig-common? Probably not (what if database is not on localhost?). + +### Run `zmupdate.pl` from service control scripts (init.d, service) on start? + + Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). + diff --git a/distros/beowulf/changelog b/distros/beowulf/changelog new file mode 100644 index 000000000..616f75178 --- /dev/null +++ b/distros/beowulf/changelog @@ -0,0 +1,3 @@ +zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low + * + -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 diff --git a/distros/beowulf/clean b/distros/beowulf/clean new file mode 100644 index 000000000..941ef2a3a --- /dev/null +++ b/distros/beowulf/clean @@ -0,0 +1,3 @@ +.gitattributes +web/api/.gitattributes +web/api/.gitignore diff --git a/distros/beowulf/compat b/distros/beowulf/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/distros/beowulf/compat @@ -0,0 +1 @@ +9 diff --git a/distros/beowulf/conf/apache2/zoneminder.conf b/distros/beowulf/conf/apache2/zoneminder.conf new file mode 100644 index 000000000..e3164d36c --- /dev/null +++ b/distros/beowulf/conf/apache2/zoneminder.conf @@ -0,0 +1,57 @@ +# Remember to enable cgi mod (i.e. "a2enmod cgi"). +ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + + +# Order matters. This alias must come first. +Alias /zm/cache /var/cache/zoneminder/cache + + Options -Indexes +FollowSymLinks + AllowOverride None + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + +Alias /zm /usr/share/zoneminder/www + + Options -Indexes +FollowSymLinks + + DirectoryIndex index.php + + + +# For better visibility, the following directives have been migrated from the +# default .htaccess files included with the CakePHP project. +# Parameters not set here are inherited from the parent directive above. + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + RewriteBase /zm/api + diff --git a/distros/beowulf/control b/distros/beowulf/control new file mode 100644 index 000000000..dd410c0ba --- /dev/null +++ b/distros/beowulf/control @@ -0,0 +1,166 @@ +Source: zoneminder +Section: net +Priority: optional +Maintainer: Isaac Connor +Uploaders: Isaac Connor +Build-Depends: debhelper, sphinx-doc, dh-linktree, dh-apache2 + ,cmake + ,libx264-dev, libmp4v2-dev + ,libavdevice-dev + ,libavcodec-dev + ,libavformat-dev + ,libavutil-dev + ,libswresample-dev + ,libswscale-dev + ,ffmpeg + ,net-tools + ,libbz2-dev + ,libgcrypt20-dev + ,libcurl4-gnutls-dev + ,libturbojpeg0-dev + ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat + ,libpcre3-dev + ,libpolkit-gobject-1-dev + ,libv4l-dev [!hurd-any] + ,libvlc-dev + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libsys-mmap-perl [!hurd-any] + ,libwww-perl + ,libdata-uuid-perl + ,libssl-dev + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +# Unbundled (dh_linktree): + ,libjs-jquery + ,libjs-mootools +Standards-Version: 3.9.8 +Homepage: http://www.zoneminder.com/ +Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git +Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git + +Package: zoneminder +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + ,javascript-common + ,libmp4v2-2, libx264-155 + ,libswscale5 + ,libswresample3 + ,ffmpeg + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libmodule-load-conditional-perl + ,libnet-sftp-foreign-perl + ,libarchive-zip-perl + ,libdbd-mysql-perl + ,libdevice-serialport-perl + ,libimage-info-perl + ,libjson-maybexs-perl + ,libsys-mmap-perl [!hurd-any] + ,liburi-encode-perl + ,libwww-perl, liburi-perl + ,libdata-dump-perl + ,libdatetime-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl + ,libnumber-bytes-human-perl + ,libfile-slurp-perl + ,mysql-client | mariadb-client | virtual-mysql-client + ,perl-modules + ,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json + ,policykit-1 + ,rsyslog | system-log-daemon + ,zip + ,libpcre3 + ,libcrypt-eksblowfish-perl + ,libdata-entropy-perl +Recommends: ${misc:Recommends} + ,libapache2-mod-php | php-fpm + ,mysql-server | mariadb-server | virtual-mysql-server + ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg +Suggests: fcgiwrap, logrotate +Description: video camera security and surveillance solution + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + +#Package: libzoneminder-perl +#Section: perl +#Architecture: all +#Multi-Arch: foreign +#Depends: ${misc:Depends}, ${perl:Depends} +# ,libarchive-zip-perl +# ,libdbd-mysql-perl +# ,libdevice-serialport-perl +# ,libimage-info-perl +# ,libjson-maybexs-perl +# ,libsys-mmap-perl [!hurd-any] +# ,liburi-encode-perl +# ,libwww-perl +#Description: ZoneMinder Perl libraries +# ZoneMinder is intended for use in single or multi-camera video security +# applications, including commercial or home CCTV, theft prevention and child +# or family member or home monitoring and other care scenarios. It +# supports capture, analysis, recording, and monitoring of video data coming +# from one or more video or network cameras attached to a Linux system. +# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom +# cameras using a variety of protocols. It is suitable for use as a home +# video security system and for commercial or professional video security +# and surveillance. It can also be integrated into a home automation system +# via X.10 or other protocols. +# . +# This package provides ZoneMinder Perl libraries; it can be used to +# write custom interfaces as well. + +Package: zoneminder-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme +Suggests: www-browser +Description: ZoneMinder documentation + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides ZoneMinder documentation in HTML format. + +Package: zoneminder-dbg +Section: debug +Priority: extra +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Zoneminder -- debugging symbols + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides debugging symbols diff --git a/distros/beowulf/copyright b/distros/beowulf/copyright new file mode 100644 index 000000000..c48025a25 --- /dev/null +++ b/distros/beowulf/copyright @@ -0,0 +1,174 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ZoneMinder +Upstream-Contact: Philip Coombes +Source: https://github.com/ZoneMinder/ZoneMinder +Comment: + This package was originally debianized by matrix + on Mon, 7 Mar 2005 02:07:57 -0500. + It was re-done for submission to the Debian project by Peter Howard + on Fri, 8 Dec 2006 10:19:43 +1100 +Files-Excluded: + web/skins/*/js/jquery-* + web/tools/mootools/*-yc.js + +Files: * +Copyright: 2001-2014 Philip Coombes + 2008 Brian Rudy + 2014 Vincent Giovannone + 2013 Tim Craig + 2003-2008 Corey DeLasaux + 2001-2010 Chris Kistner +License: GPL-2+ + +Files: distros/* +Copyright: 2001-2008 Philip Coombes + 2014 Isaac Connor + 2005 Serg Oskin +License: GPL-2+ + +Files: web/skins/*/js/jquery-* +Copyright: 2010 John Resig + 2010 The Dojo Foundation +License: GPL-2 or Expat +Comment: + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license + . + Includes Sizzle.js http://sizzlejs.com/ + Released under the MIT, BSD, and GPL Licenses. + +Files: web/tools/mootools/*.js +Copyright: 2009 Marcelo Jorge Vieira (metal) + 2006-2010 Valerio Proietti (http://mad4milk.net/) +License: Expat + +Files: web/api/* +Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) +License: Expat + +Files: + cmake/Modules/CheckPrototypeDefinition*.cmake + cmake/Modules/FindGLIB2.cmake + cmake/Modules/FindPolkit.cmake + cmake/Modules/GNUInstallDirs.cmake +Copyright: + 2005-2011 Kitware, Inc. + 2010-2011 Andreas Schneider + 2009 Dario Freddi + 2008 Laurent Montel, + 2011 Nikita Krupen'ko +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + . + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + . + * The names of Kitware, Inc., the Insight Consortium, or the names of + any consortium members, or of any contributors, may not be used to + endorse or promote products derived from this software without + specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Files: cmake/Modules/FindPerlModules.cmake +Copyright: 2012 Iowa State University +License: Boost-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: debian/* +Copyright: 2015 Dmitry Smirnov + 2007-2014 Peter Howard + 2010-2012 Vagrant Cascadian + 2001-2008 Philip Coombes +License: GPL-2+ + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +License: GPL-2+ + This package 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 package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-2 + This package 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; version 2 of the License. + . + This package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/beowulf/examples/nginx.conf b/distros/beowulf/examples/nginx.conf new file mode 100644 index 000000000..5636ca3e1 --- /dev/null +++ b/distros/beowulf/examples/nginx.conf @@ -0,0 +1,32 @@ +location /zm/cgi-bin { + gzip off; + alias /usr/lib/zoneminder/cgi-bin; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/fcgiwrap.socket; +} + +location /zm { +# if ($scheme ~ ^http:){ +# rewrite ^(.*)$ https://$host$1 permanent; +# } + + gzip off; + alias /usr/share/zoneminder/www; + index index.php; + + location ~ \.php$ { + if (!-f $request_filename) { return 404; } + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } +} diff --git a/distros/beowulf/gbp.conf b/distros/beowulf/gbp.conf new file mode 100644 index 000000000..4608913d9 --- /dev/null +++ b/distros/beowulf/gbp.conf @@ -0,0 +1,7 @@ + +[dch] +id-length = 0 + +[import-orig] +pristine-tar = False +merge = False diff --git a/distros/beowulf/libzoneminder-perl.install b/distros/beowulf/libzoneminder-perl.install new file mode 100644 index 000000000..67191d9cf --- /dev/null +++ b/distros/beowulf/libzoneminder-perl.install @@ -0,0 +1,2 @@ +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/beowulf/patches/series b/distros/beowulf/patches/series new file mode 100644 index 000000000..e69de29bb diff --git a/distros/beowulf/rules b/distros/beowulf/rules new file mode 100755 index 000000000..e26f1bb91 --- /dev/null +++ b/distros/beowulf/rules @@ -0,0 +1,87 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed + +ifeq ($(DEB_BUILD_ARCH_OS),hurd) +ARGS:= -DZM_NO_MMAP=ON +endif + +%: + dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ + --with sphinxdoc,apache2,linktree + +override_dh_auto_configure: + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_RUNDIR="/run/zm" \ + -DZM_SOCKDIR="/run/zm" \ + -DZM_TMPDIR="/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ + -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + +override_dh_clean: + dh_clean $(MANPAGES1) + $(RM) -r docs/_build + +build-indep: + #$(MAKE) -C docs text + $(MAKE) -C docs html + +MANPAGES1 = dbuild/scripts/zmupdate.pl.1 +$(MANPAGES1): + # generate man page(s): + pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@ + +## reproducible build: +LAST_CHANGE=$(shell dpkg-parsechangelog -S Date) +BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)") +override_dh_installman: $(MANPAGES1) + $(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\"" + dh_installman --language=C $(MANPAGES1) + +override_dh_auto_install: + dh_auto_install --destdir=$(CURDIR)/debian/tmp + # remove worthless files: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in + # remove empty directories: + find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n' + # remove extra-license-file: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt + +override_dh_fixperms: + dh_fixperms + # + # As requested by the Debian Webapps Policy Manual ยง3.2.1 + chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf + +override_dh_strip: + [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ + && dh_strip --dbg-package=zoneminder-dbg \ + || dh_strip + +#%: +# dh $@ --parallel --buildsystem=autoconf --with autoreconf +# +#override_dh_auto_configure: +# dh_auto_configure -- \ +# --sysconfdir=/etc/zm \ +# --with-mysql=/usr \ +# --with-webdir=/usr/share/zoneminder \ +# --with-ffmpeg=/usr \ +# --with-cgidir=/usr/lib/cgi-bin \ +# --with-webuser=www-data \ +# --with-webgroup=www-data \ +# --enable-mmap=yes diff --git a/distros/beowulf/source/format b/distros/beowulf/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/distros/beowulf/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/distros/beowulf/source/lintian-overrides b/distros/beowulf/source/lintian-overrides new file mode 100644 index 000000000..3669e5de8 --- /dev/null +++ b/distros/beowulf/source/lintian-overrides @@ -0,0 +1,9 @@ +## Actually sources are there: "*-nc.js". +source-is-missing web/tools/mootools/mootools-*-yc.js + +## We're using "libjs-jquery" instead. +source-is-missing web/skins/*/js/jquery-1.4.2.min.js + +## Acknowledged, will repack eventually. +source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js +source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js diff --git a/distros/beowulf/zoneminder-doc.doc-base b/distros/beowulf/zoneminder-doc.doc-base new file mode 100644 index 000000000..c43dc4336 --- /dev/null +++ b/distros/beowulf/zoneminder-doc.doc-base @@ -0,0 +1,8 @@ +Document: zoneminder-doc +Title: Zoneminder documentation +Abstract: This document describes how to use Zoneminder. +Section: System/Administration + +Format: HTML +Index: /usr/share/doc/zoneminder-doc/html/index.html +Files: /usr/share/doc/zoneminder-doc/html/* diff --git a/distros/beowulf/zoneminder-doc.install b/distros/beowulf/zoneminder-doc.install new file mode 100644 index 000000000..c19bc6f3a --- /dev/null +++ b/distros/beowulf/zoneminder-doc.install @@ -0,0 +1 @@ +docs/_build/html usr/share/doc/zoneminder-doc/ diff --git a/distros/beowulf/zoneminder-doc.links b/distros/beowulf/zoneminder-doc.links new file mode 100644 index 000000000..cc09f6462 --- /dev/null +++ b/distros/beowulf/zoneminder-doc.links @@ -0,0 +1,2 @@ +## Convenience symlink: +/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html diff --git a/distros/beowulf/zoneminder.apache2 b/distros/beowulf/zoneminder.apache2 new file mode 100644 index 000000000..466144fa7 --- /dev/null +++ b/distros/beowulf/zoneminder.apache2 @@ -0,0 +1 @@ +conf debian/conf/apache2/zoneminder.conf nginx diff --git a/distros/beowulf/zoneminder.bug-presubj b/distros/beowulf/zoneminder.bug-presubj new file mode 100644 index 000000000..990fc1d94 --- /dev/null +++ b/distros/beowulf/zoneminder.bug-presubj @@ -0,0 +1,5 @@ +Unless bug is specific to Debian please consider reporting it directly to +upstream developer(s): + + https://github.com/ZoneMinder/ZoneMinder/issues + diff --git a/distros/beowulf/zoneminder.dirs b/distros/beowulf/zoneminder.dirs new file mode 100644 index 000000000..6db3d5a95 --- /dev/null +++ b/distros/beowulf/zoneminder.dirs @@ -0,0 +1,9 @@ +var/log/zm +var/lib/zm +var/cache/zoneminder/events +var/cache/zoneminder/images +var/cache/zoneminder/temp +var/cache/zoneminder/cache +usr/share/zoneminder/db +etc/zm/ +etc/zm/conf.d diff --git a/distros/beowulf/zoneminder.docs b/distros/beowulf/zoneminder.docs new file mode 100644 index 000000000..b43bf86b5 --- /dev/null +++ b/distros/beowulf/zoneminder.docs @@ -0,0 +1 @@ +README.md diff --git a/distros/beowulf/zoneminder.examples b/distros/beowulf/zoneminder.examples new file mode 100644 index 000000000..3b8befe7b --- /dev/null +++ b/distros/beowulf/zoneminder.examples @@ -0,0 +1,2 @@ +debian/examples/* +dbuild/misc/apache.conf diff --git a/distros/beowulf/zoneminder.init b/distros/beowulf/zoneminder.init new file mode 100644 index 000000000..6132481f3 --- /dev/null +++ b/distros/beowulf/zoneminder.init @@ -0,0 +1,91 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: zoneminder +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Should-Start: mysql +# Should-Stop: mysql +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Control ZoneMinder as a Service +# Description: ZoneMinder CCTV recording and surveillance system +### END INIT INFO +# chkconfig: 2345 20 20 + +# Source function library. +. /lib/lsb/init-functions + +prog=ZoneMinder +ZM_PATH_BIN="/usr/bin" +RUNDIR="/run/zm" +TMPDIR="/tmp/zm" +command="$ZM_PATH_BIN/zmpkg.pl" + +start() { + echo -n "Starting $prog: " + export TZ=:/etc/localtime + mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" + mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" + $command start + RETVAL=$? + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure + echo + [ $RETVAL = 0 ] && touch /var/lock/zm + return $RETVAL +} +stop() { + echo -n "Stopping $prog: " + # + # Why is this status check being done? + # as $command stop returns 1 if zoneminder + # is stopped, which will result in + # this returning 1, which will stuff + # dpkg when it tries to stop zoneminder before + # uninstalling . . . + # + result=`$command status` + if [ ! "$result" = "running" ]; then + echo "Zoneminder already stopped" + echo + RETVAL=0 + else + $command stop + RETVAL=$? + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure + echo + [ $RETVAL = 0 ] && rm -f /var/lock/zm + fi +} +status() { + result=`$command status` + if [ "$result" = "running" ]; then + echo "ZoneMinder is running" + RETVAL=0 + else + echo "ZoneMinder is stopped" + RETVAL=1 + fi +} + +case "$1" in +'start') + start + ;; +'stop') + stop + ;; +'restart' | 'force-reload') + stop + start + ;; +'status') + status + ;; +*) + echo "Usage: $0 { start | stop | restart | status }" + RETVAL=1 + ;; +esac +exit $RETVAL diff --git a/distros/beowulf/zoneminder.install b/distros/beowulf/zoneminder.install new file mode 100644 index 000000000..67b135de5 --- /dev/null +++ b/distros/beowulf/zoneminder.install @@ -0,0 +1,11 @@ +etc/zm/zm.conf +etc/zm/conf.d/* +usr/bin +usr/lib/zoneminder +usr/share/polkit-1 +usr/share/zoneminder/db +usr/share/zoneminder/www + +# libzoneminder-perl files: +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/beowulf/zoneminder.links b/distros/beowulf/zoneminder.links new file mode 100644 index 000000000..b7258c3c4 --- /dev/null +++ b/distros/beowulf/zoneminder.links @@ -0,0 +1 @@ +/var/tmp /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/beowulf/zoneminder.linktrees b/distros/beowulf/zoneminder.linktrees new file mode 100644 index 000000000..2e843bbf1 --- /dev/null +++ b/distros/beowulf/zoneminder.linktrees @@ -0,0 +1,14 @@ +## cakephp +#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake + +## libjs-mootools +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js + +## libjs-jquery +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js diff --git a/distros/beowulf/zoneminder.lintian-overrides b/distros/beowulf/zoneminder.lintian-overrides new file mode 100644 index 000000000..90be05a9f --- /dev/null +++ b/distros/beowulf/zoneminder.lintian-overrides @@ -0,0 +1,14 @@ +# Depends: policykit-1 +unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + +# Intentionally not others-readable, #637685. +non-standard-file-perm etc/zm/zm.conf 0640 != 0644 + +# Bundled Cake PHP framework, not intended for direct execution: +script-not-executable usr/share/zoneminder/www/api/* + +# Annoying but seems to be too much troubles to fix; should be fixed upstream: +script-with-language-extension usr/bin/*.pl + +# dh-linktree: +package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/* diff --git a/distros/beowulf/zoneminder.logrotate b/distros/beowulf/zoneminder.logrotate new file mode 100644 index 000000000..6162e9c4d --- /dev/null +++ b/distros/beowulf/zoneminder.logrotate @@ -0,0 +1,13 @@ +/var/log/zm/*.log { + missingok + notifempty + sharedscripts + delaycompress + compress + postrotate + /usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || : + endscript + daily + rotate 7 + maxage 7 +} diff --git a/distros/beowulf/zoneminder.maintscript b/distros/beowulf/zoneminder.maintscript new file mode 100644 index 000000000..3aa20b3a0 --- /dev/null +++ b/distros/beowulf/zoneminder.maintscript @@ -0,0 +1 @@ +rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/beowulf/zoneminder.manpages b/distros/beowulf/zoneminder.manpages new file mode 100644 index 000000000..d2053d688 --- /dev/null +++ b/distros/beowulf/zoneminder.manpages @@ -0,0 +1 @@ +docs/_build/man/*.1 diff --git a/distros/beowulf/zoneminder.postinst b/distros/beowulf/zoneminder.postinst new file mode 100644 index 000000000..603786ff6 --- /dev/null +++ b/distros/beowulf/zoneminder.postinst @@ -0,0 +1,66 @@ +#! /bin/sh + +set -e + +if [ "$1" = "configure" ]; then + + . /etc/zm/zm.conf + for i in /etc/zm/conf.d/*.conf; do + . $i + done; + + + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + + # Do this every time the package is installed or upgraded + # Ensure zoneminder is stopped + invoke-rc.d zoneminder stop || true + + if [ "$ZM_DB_HOST" = "localhost" ]; then + if [ -e "/etc/init.d/mysql" ]; then + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + invoke-rc.d mysql start + fi + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + 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 + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi + # This creates the user. + 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,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 + zmupdate.pl --nointeractive -f + + # Add any new PTZ control configurations to the database (will not overwrite) + zmcamtool.pl --import >/dev/null 2>&1 + + else + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'mysql not found, assuming remote server.' + fi + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi + echo "Done Updating, starting ZoneMinder" + invoke-rc.d zoneminder start || true +fi + +#DEBHELPER# diff --git a/distros/beowulf/zoneminder.postrm b/distros/beowulf/zoneminder.postrm new file mode 100644 index 000000000..ba2066c8d --- /dev/null +++ b/distros/beowulf/zoneminder.postrm @@ -0,0 +1,14 @@ +#! /bin/sh + +set -e + +if [ "$1" = "purge" ]; then + echo " +Reminder: to completely remove \"zoneminder\" it may be necessary + * to delete database using the following sample command: + sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + * to delete remaining data files in "/var/cache/zoneminder". +" +fi + +#DEBHELPER# diff --git a/distros/beowulf/zoneminder.preinst b/distros/beowulf/zoneminder.preinst new file mode 100644 index 000000000..6088c3ea9 --- /dev/null +++ b/distros/beowulf/zoneminder.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +#DEBHELPER# diff --git a/distros/beowulf/zoneminder.tmpfile b/distros/beowulf/zoneminder.tmpfile new file mode 100644 index 000000000..cbfdec1de --- /dev/null +++ b/distros/beowulf/zoneminder.tmpfile @@ -0,0 +1,4 @@ +d /run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data +d /var/tmp/zm 0755 www-data www-data +d /var/cache/zoneminder/cache 0755 www-data www-data diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c3458074c..637630324 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.14 +Version: 1.34.20 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/distros/ubuntu1604/changelog b/distros/ubuntu1604/changelog index 616f75178..0fc2fda2e 100644 --- a/distros/ubuntu1604/changelog +++ b/distros/ubuntu1604/changelog @@ -1,3 +1,3 @@ -zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low - * - -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 +zoneminder (1.35.6~20200825.27-xenial) xenial; urgency=low + * + -- Isaac Connor Tue, 25 Aug 2020 09:28:18 -0400 diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index bd1885fbe..891e5583a 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -29,7 +29,7 @@ configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY) #configure_file(zmeventdump.in zmeventdump @ONLY) # Generate man files for the perl scripts destined for the bin folder -file(GLOB perlscripts "*.pl") +file(GLOB perlscripts "${CMAKE_CURRENT_BINARY_DIR}/*.pl") FOREACH(PERLSCRIPT ${perlscripts}) get_filename_component(PERLSCRIPTNAME ${PERLSCRIPT} NAME) POD2MAN(${PERLSCRIPT} zoneminder-${PERLSCRIPTNAME} 8 ${ZM_MANPAGE_DEST_PREFIX}) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm index 4f14e787a..2b2cf3fd2 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm @@ -69,9 +69,9 @@ sub sendCmd { my $result = undef; - printMsg($cmd, 'Tx'); + $self->printMsg($cmd, 'Tx'); - my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); + my $req = HTTP::Request->new(POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi"); $req->content($cmd); my $res = $self->{ua}->request($req); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/HikVision.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/HikVision.pm index a9f8d8f1b..a62c756d8 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/HikVision.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/HikVision.pm @@ -143,10 +143,10 @@ sub PutCmd { } else { Info('Missing username and password in Control Address.'); } - Fatal($res->status_line); + Error($res->status_line); } } else { - Fatal($res->status_line); + Error($res->status_line); } } # end unless res->is_success } # end sub putCmd diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index eb1718655..74886b4d4 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -49,9 +49,9 @@ my %options; GetOptions( 'id=i' =>\$id, 'command=s' =>\$options{command}, - 'xcoord=i' =>\$options{xcoord}, - 'ycoord=i' =>\$options{ycoord}, - 'speed=i' =>\$options{speed}, + 'xcoord=f' =>\$options{xcoord}, + 'ycoord=f' =>\$options{ycoord}, + 'speed=f' =>\$options{speed}, 'step=i' =>\$options{step}, 'panspeed=i' =>\$options{panspeed}, 'tiltspeed=i' =>\$options{tiltspeed}, diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 4ebb9211b..3ab910469 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -160,7 +160,7 @@ if ( !$server_up ) { use ZoneMinder::Server qw(CpuLoad); if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, 'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { - Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr()); + Error('Failed Updating status of Server record to Not Running for Id='.$Config{ZM_SERVER_ID}.': '.$dbh->errstr()); } } # Server is not up. Some commands can still be handled @@ -173,7 +173,7 @@ if ( !$server_up ) { print("stopped\n"); exit(); } elsif ( $command ne 'startup' ) { - print('Unable to connect to server using socket at ' . SOCK_FILE . "\n"); + print('Unable to connect to server using socket at '.SOCK_FILE."\n"); exit(-1); } @@ -189,10 +189,10 @@ if ( !$server_up ) { 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 to zmdc.pl server process at '.SOCK_FILE.': '.$!) if $attempts > MAX_CONNECT_DELAY; usleep(200000); } # end while } elsif ( defined($cpid) ) { @@ -255,13 +255,13 @@ sub run { close($PID); } else { # Log not initialized at this point so use die instead - die "Can't open pid file at ".ZM_PID."\n"; + die 'Can\'t open pid file at '.ZM_PID."\n"; } my $fd = 0; # This also closes dbh and CLIENT and SERVER - while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) { + while ( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) { POSIX::close($fd++); } @@ -301,13 +301,14 @@ sub run { my $timeout = 1; my $secs_count = 0; - while( !$zm_terminate ) { + while ( !$zm_terminate ) { if ( $Config{ZM_SERVER_ID} ) { if ( ! ( $secs_count % 60 ) ) { while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) { Warning("Not connected to db ($dbh)".($dbh?' ping('.$dbh->ping().')':''). ($DBI::errstr?" errstr($DBI::errstr)":'').' Reconnecting'); $dbh = zmDbConnect(); + sleep 10 if !$dbh; } last if $zm_terminate; diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 044f7e1ce..555d9c602 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -48,6 +48,49 @@ while( 1 ) { $dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr()); $dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr()); + # Prune the Logs table if required + if ( $Config{ZM_LOG_DATABASE_LIMIT} ) { + if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ ) { + # Number of rows + my $selectLogRowCountSql = 'SELECT count(*) AS `Rows` FROM `Logs`'; + my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql ) + or Fatal("Can't prepare '$selectLogRowCountSql': ".$dbh->errstr()); + my $res = $selectLogRowCountSth->execute() + or Fatal("Can't execute: ".$selectLogRowCountSth->errstr()); + my $row = $selectLogRowCountSth->fetchrow_hashref(); + my $logRows = $row->{Rows}; + if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} ) { + my $deleteLogByRowsSql = 'DELETE low_priority FROM `Logs` ORDER BY `TimeKey` ASC LIMIT ?'; + my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql ) + or Fatal("Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr()); + $res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} ) + or Fatal("Can't execute: ".$deleteLogByRowsSth->errstr()); + if ( $deleteLogByRowsSth->rows() ) { + Debug('Deleted '.$deleteLogByRowsSth->rows().' log table entries by count'); + } + } + } else { + # Time of record + + # 7 days is invalid. We need to remove the s + if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^(.*)s$/ ) { + $Config{ZM_LOG_DATABASE_LIMIT} = $1; + } + my $deleted_rows; + do { + my $deleteLogByTimeSql = + 'DELETE FROM `Logs` + WHERE `TimeKey` < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.') LIMIT 100'; + my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql ) + or Fatal("Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr()); + my $res = $deleteLogByTimeSth->execute() + or Fatal("Can't execute: ".$deleteLogByTimeSth->errstr()); + $deleted_rows = $deleteLogByTimeSth->rows(); + Debug("Deleted $deleted_rows log table entries by time"); + } while ( $deleted_rows ); + } + } # end if ZM_LOG_DATABASE_LIMIT + sleep($Config{ZM_STATS_UPDATE_INTERVAL}); } # end while (1) diff --git a/scripts/zmtrack.pl.in b/scripts/zmtrack.pl.in index 58798e5de..10db09664 100644 --- a/scripts/zmtrack.pl.in +++ b/scripts/zmtrack.pl.in @@ -119,7 +119,6 @@ if ( !$monitor->{CanMoveMap} ) { } } -Debug("Found monitor for id '$monitor'"); exit(-1) if !zmMemVerify($monitor); sub Suspend { diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 3662b6655..925e58006 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -121,6 +121,8 @@ GetOptions( my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); $Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_PASS} = $dbPass; +# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it. +$dbPass =~ s/'/\\'/g; if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) { if ( $Config{ZM_DYN_DB_VERSION} ) { @@ -384,21 +386,22 @@ if ( $version ) { my $command = 'mysqldump'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { - $command .= " -S".$portOrSocket; + $command .= ' -S'.$portOrSocket; } else { - $command .= " -h".$host." -P".$portOrSocket; + $command .= ' -h'.$host.' -P'.$portOrSocket; } } else { - $command .= " -h".$host; + $command .= ' -h'.$host; } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } - my $backup = "@ZM_TMPDIR@/".$Config{ZM_DB_NAME}."-".$version.".dump"; - $command .= " --add-drop-table --databases ".$Config{ZM_DB_NAME}." > ".$backup; - print( "Creating backup to $backup. This may take several minutes.\n" ); - print( "Executing '$command'\n" ) if ( logDebugging() ); + my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump'; + $command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup; + print("Creating backup to $backup. This may take several minutes.\n"); + ($command) = $command =~ /(.*)/; # detaint + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { @@ -969,7 +972,7 @@ sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); + my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ) if $Config{ZM_DB_HOST}; my $command = 'mysql'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { @@ -977,12 +980,12 @@ sub patchDB { } else { $command .= ' -h'.$host.' -P'.$portOrSocket; } - } else { + } elsif ( $host ) { $command .= ' -h'.$host; } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } $command .= ' '.$Config{ZM_DB_NAME}.' < '; if ( $updateDir ) { diff --git a/src/zm_camera.cpp b/src/zm_camera.cpp index 420d5961a..5cb558645 100644 --- a/src/zm_camera.cpp +++ b/src/zm_camera.cpp @@ -48,11 +48,12 @@ Camera::Camera( record_audio(p_record_audio), bytes(0) { + linesize = width * colours; pixels = width * height; - imagesize = pixels * colours; + imagesize = height * linesize; - Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d", - monitor_id, width, height, colours, subpixelorder, capture); + Debug(2, "New camera id: %d width: %d line size: %d height: %d colours: %d subpixelorder: %d capture: %d", + monitor_id, width, linesize, height, colours, subpixelorder, capture); monitor = NULL; } diff --git a/src/zm_camera.h b/src/zm_camera.h index a6f576af2..32517814b 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -41,6 +41,7 @@ protected: Monitor * monitor; // Null on instantiation, set as soon as possible. SourceType type; unsigned int width; + unsigned int linesize; unsigned int height; unsigned int colours; unsigned int subpixelorder; @@ -69,6 +70,7 @@ public: bool IsLibvlc() const { return type == LIBVLC_SRC; } bool IscURL() const { return type == CURL_SRC; } unsigned int Width() const { return width; } + unsigned int LineSize() const { return linesize; } unsigned int Height() const { return height; } unsigned int Colours() const { return colours; } unsigned int SubpixelOrder() const { return subpixelorder; } diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e564479f3..803724bf5 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -206,7 +206,12 @@ Event::Event( /* X264 MP4 video writer */ if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) { #if ZM_HAVE_VIDEOWRITER_X264MP4 - videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams()); + videowriter = new X264MP4Writer(video_file, + monitor->Width(), + monitor->Height(), + monitor->Colours(), + monitor->SubpixelOrder(), + monitor->GetOptEncoderParamsVec()); #else Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)"); #endif diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index d081f4e76..8eaa9cc8f 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -807,29 +807,38 @@ Debug(1, "Loading image"); if ( send_raw ) { #if HAVE_SENDFILE - fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size); + if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size) ) { + fclose(fdj); /* Close the file handle */ + Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); + return false; + } if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) { /* sendfile() failed, use standard way instead */ - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { fclose(fdj); /* Close the file handle */ - Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); + Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); return false; } } #else - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); - if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { + if ( + (0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) ) + || + ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) + ) { fclose(fdj); /* Close the file handle */ - Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); + Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); return false; } #endif fclose(fdj); /* Close the file handle */ } else { Debug(3, "Content length: %d", img_buffer_size); - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); - if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { + if ( + (0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) ) + || + ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) ) { Error("Unable to send stream frame: %s", strerror(errno)); return false; } diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 73afff3e3..8adad7301 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -40,6 +40,7 @@ extern "C" { #include #include #include "libavutil/audio_fifo.h" +#include "libavutil/imgutils.h" /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 11c25addc..2ca712db8 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -63,31 +63,31 @@ static enum AVPixelFormat get_hw_format( } #if !LIBAVUTIL_VERSION_CHECK(56, 22, 0, 14, 0) static enum AVPixelFormat find_fmt_by_hw_type(const enum AVHWDeviceType type) { - enum AVPixelFormat fmt; - switch (type) { + enum AVPixelFormat fmt; + switch (type) { case AV_HWDEVICE_TYPE_VAAPI: - fmt = AV_PIX_FMT_VAAPI; - break; + fmt = AV_PIX_FMT_VAAPI; + break; case AV_HWDEVICE_TYPE_DXVA2: - fmt = AV_PIX_FMT_DXVA2_VLD; - break; + fmt = AV_PIX_FMT_DXVA2_VLD; + break; case AV_HWDEVICE_TYPE_D3D11VA: - fmt = AV_PIX_FMT_D3D11; - break; + fmt = AV_PIX_FMT_D3D11; + break; case AV_HWDEVICE_TYPE_VDPAU: - fmt = AV_PIX_FMT_VDPAU; - break; + fmt = AV_PIX_FMT_VDPAU; + break; case AV_HWDEVICE_TYPE_CUDA: - fmt = AV_PIX_FMT_CUDA; - break; + fmt = AV_PIX_FMT_CUDA; + break; case AV_HWDEVICE_TYPE_VIDEOTOOLBOX: - fmt = AV_PIX_FMT_VIDEOTOOLBOX; - break; + fmt = AV_PIX_FMT_VIDEOTOOLBOX; + break; default: - fmt = AV_PIX_FMT_NONE; - break; - } - return fmt; + fmt = AV_PIX_FMT_NONE; + break; + } + return fmt; } #endif #endif @@ -174,6 +174,20 @@ FfmpegCamera::FfmpegCamera( } else { Panic("Unexpected colours: %d", colours); } + + // sws_scale needs 32bit aligned width and an extra 16 bytes padding, so recalculate imagesize, which was width*height*bytes_per_pixel +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + alignment = 32; + imagesize = av_image_get_buffer_size(imagePixFormat, width, height, alignment); + // av_image_get_linesize isn't aligned, so we have to do that. + linesize = FFALIGN(av_image_get_linesize(imagePixFormat, width, 0), alignment); +#else + alignment = 1; + linesize = FFALIGN(av_image_get_linesize(imagePixFormat, width, 0), alignment); + imagesize = avpicture_get_size(imagePixFormat, width, height); +#endif + + Debug(1, "ffmpegcamera: width %d height %d linesize %d colours %d imagesize %d", width, height, linesize, colours, imagesize); } // FfmpegCamera::FfmpegCamera FfmpegCamera::~FfmpegCamera() { @@ -194,12 +208,12 @@ void FfmpegCamera::Terminate() { int FfmpegCamera::PrimeCapture() { if ( mCanCapture ) { - Info("Priming capture from %s, Closing", mPath.c_str()); + Debug(1, "Priming capture from %s, Closing", mPath.c_str()); Close(); } mVideoStreamId = -1; mAudioStreamId = -1; - Info("Priming capture from %s", mPath.c_str()); + Debug(1, "Priming capture from %s", mPath.c_str()); return OpenFfmpeg(); } @@ -608,16 +622,6 @@ int FfmpegCamera::OpenFfmpeg() { mFrame->width = width; mFrame->height = height; -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1); -#else - int pSize = avpicture_get_size(imagePixFormat, width, height); -#endif - - if ( (unsigned int)pSize != imagesize ) { - Error("Image size mismatch. Required: %d Available: %d", pSize, imagesize); - return -1; - } #if HAVE_LIBSWSCALE if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) { @@ -976,10 +980,12 @@ int FfmpegCamera::CaptureAndRecord( return -1; } #if HAVE_LIBAVUTIL_HWCONTEXT_H +#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0) if ( (ret == AVERROR_INVALIDDATA ) && (hw_pix_fmt != AV_PIX_FMT_NONE) ) { use_hwaccel = false; return -1; } +#endif #endif } zm_av_packet_unref(&packet); @@ -1087,11 +1093,18 @@ int FfmpegCamera::transfer_to_image( int size = av_image_fill_arrays( output_frame->data, output_frame->linesize, directbuffer, imagePixFormat, width, height, - (AV_PIX_FMT_RGBA == imagePixFormat ? 32 : 1) + alignment ); if ( size < 0 ) { Error("Problem setting up data pointers into image %s", av_make_error_string(size).c_str()); + } else { + Debug(4, "av_image_fill_array %dx%d alignment: %d = %d, buffer size is %d", + width, height, alignment, size, image.Size()); + } + Debug(1, "ffmpegcamera: width %d height %d linesize %d colours %d imagesize %d", width, height, linesize, colours, imagesize); + if ( linesize != (unsigned int)output_frame->linesize[0] ) { + Error("Bad linesize expected %d got %d", linesize, output_frame->linesize[0]); } #else avpicture_fill((AVPicture *)output_frame, directbuffer, diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index badf90834..ce5868690 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -45,6 +45,8 @@ class FfmpegCamera : public Camera { std::string hwaccel_device; int frameCount; + + int alignment; /* ffmpeg wants line sizes to be 32bit aligned. Especially 4.3+ */ #if HAVE_LIBAVFORMAT AVFormatContext *mFormatContext; diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 28ecd4ac7..06857a281 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -19,7 +19,7 @@ FFmpeg_Input::~FFmpeg_Input() { avcodec_close(streams[i].context); streams[i].context = NULL; } - delete streams; + delete[] streams; streams = NULL; } if ( frame ) { diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 31c71037f..1b8b33c8a 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -108,6 +108,7 @@ Image::Image() { if ( !initialised ) Initialise(); width = 0; + linesize = 0; height = 0; pixels = 0; colours = 0; @@ -121,10 +122,11 @@ Image::Image() { blend = fptr_blend; } -Image::Image( const char *filename ) { +Image::Image(const char *filename) { if ( !initialised ) Initialise(); width = 0; + linesize = 0; height = 0; pixels = 0; colours = 0; @@ -139,15 +141,42 @@ Image::Image( const char *filename ) { update_function_pointers(); } -Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer ) { +Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) { if ( !initialised ) Initialise(); width = p_width; height = p_height; pixels = width*height; colours = p_colours; + linesize = p_width * p_colours; + padding = p_padding; subpixelorder = p_subpixelorder; - size = pixels*colours; + size = linesize*height + padding; + buffer = 0; + holdbuffer = 0; + if ( p_buffer ) { + allocation = size; + buffertype = ZM_BUFTYPE_DONTFREE; + buffer = p_buffer; + } else { + AllocImgBuffer(size); + } + text[0] = '\0'; + + update_function_pointers(); +} + +Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer, unsigned int p_padding) { + if ( !initialised ) + Initialise(); + width = p_width; + linesize = p_linesize; + height = p_height; + pixels = width*height; + colours = p_colours; + padding = p_padding; + subpixelorder = p_subpixelorder; + size = linesize*height + padding; buffer = 0; holdbuffer = 0; if ( p_buffer ) { @@ -169,18 +198,25 @@ Image::Image(const AVFrame *frame) { width = frame->width; height = frame->height; pixels = width*height; - colours = ZM_COLOUR_RGB32; subpixelorder = ZM_SUBPIX_ORDER_RGBA; - size = pixels*colours; + #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 32); + // av_image_get_linesize isn't aligned, so we have to do that. + linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32); +#else + linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 1); + size = avpicture_get_size(AV_PIX_FMT_RGBA, width, height); +#endif + buffer = 0; holdbuffer = 0; AllocImgBuffer(size); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) av_image_fill_arrays(dest_frame->data, dest_frame->linesize, - buffer, AV_PIX_FMT_RGBA, width, height, 1); + buffer, AV_PIX_FMT_RGBA, width, height, 32); #else avpicture_fill( (AVPicture *)dest_frame, buffer, AV_PIX_FMT_RGBA, width, height); @@ -212,6 +248,7 @@ Image::Image(const Image &p_image) { if ( !initialised ) Initialise(); width = p_image.width; + linesize = p_image.linesize; height = p_image.height; pixels = p_image.pixels; colours = p_image.colours; @@ -232,28 +269,20 @@ Image::~Image() { /* Should be called as part of program shutdown to free everything */ void Image::Deinitialise() { if ( !initialised ) return; - /* - delete[] y_table; - delete[] uv_table; - delete[] r_v_table; - delete[] g_v_table; - delete[] g_u_table; - delete[] b_u_table; - */ initialised = false; if ( readjpg_dcinfo ) { - jpeg_destroy_decompress( readjpg_dcinfo ); + jpeg_destroy_decompress(readjpg_dcinfo); delete readjpg_dcinfo; - readjpg_dcinfo = 0; + readjpg_dcinfo = NULL; } if ( decodejpg_dcinfo ) { - jpeg_destroy_decompress( decodejpg_dcinfo ); + jpeg_destroy_decompress(decodejpg_dcinfo); delete decodejpg_dcinfo; - decodejpg_dcinfo = 0; + decodejpg_dcinfo = NULL; } for ( unsigned int quality=0; quality <= 100; quality += 1 ) { if ( writejpg_ccinfo[quality] ) { - jpeg_destroy_compress( writejpg_ccinfo[quality] ); + jpeg_destroy_compress(writejpg_ccinfo[quality]); delete writejpg_ccinfo[quality]; writejpg_ccinfo[quality] = NULL; } @@ -268,26 +297,26 @@ void Image::Deinitialise() { void Image::Initialise() { /* Assign the blend pointer to function */ if ( config.fast_image_blends ) { - if ( config.cpu_extensions && sseversion >= 20 ) { + if ( config.cpu_extensions && sse_version >= 20 ) { fptr_blend = &sse2_fastblend; /* SSE2 fast blend */ - Debug(4,"Blend: Using SSE2 fast blend function"); + Debug(4, "Blend: Using SSE2 fast blend function"); } else if ( config.cpu_extensions && neonversion >= 1 ) { #if defined(__aarch64__) fptr_blend = &neon64_armv8_fastblend; /* ARM Neon (AArch64) fast blend */ - Debug(4,"Blend: Using ARM Neon (AArch64) fast blend function"); + Debug(4, "Blend: Using ARM Neon (AArch64) fast blend function"); #elif defined(__arm__) fptr_blend = &neon32_armv7_fastblend; /* ARM Neon (AArch32) fast blend */ - Debug(4,"Blend: Using ARM Neon (AArch32) fast blend function"); + Debug(4, "Blend: Using ARM Neon (AArch32) fast blend function"); #else Panic("Bug: Non ARM platform but neon present"); #endif } else { fptr_blend = &std_fastblend; /* standard fast blend */ - Debug(4,"Blend: Using fast blend function"); + Debug(4, "Blend: Using fast blend function"); } } else { fptr_blend = &std_blend; - Debug(4,"Blend: Using standard blend function"); + Debug(4, "Blend: Using standard blend function"); } __attribute__((aligned(64))) uint8_t blend1[128] = { @@ -314,7 +343,7 @@ void Image::Initialise() { (*fptr_blend)(blend1,blend2,blendres,128,12.0); /* Compare results with expected results */ - for ( int i=0; i < 128; i ++ ) { + for ( int i=0; i < 128; i++ ) { if ( abs(blendexp[i] - blendres[i]) > 3 ) { Panic("Blend function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,blendexp[i],blendres[i]); } @@ -325,22 +354,22 @@ void Image::Initialise() { /* Assign the delta functions */ if ( config.cpu_extensions ) { - if ( sseversion >= 35 ) { + if ( sse_version >= 35 ) { /* SSSE3 available */ fptr_delta8_rgba = &ssse3_delta8_rgba; fptr_delta8_bgra = &ssse3_delta8_bgra; fptr_delta8_argb = &ssse3_delta8_argb; fptr_delta8_abgr = &ssse3_delta8_abgr; fptr_delta8_gray8 = &sse2_delta8_gray8; - Debug(4,"Delta: Using SSSE3 delta functions"); - } else if ( sseversion >= 20 ) { + Debug(4, "Delta: Using SSSE3 delta functions"); + } else if ( sse_version >= 20 ) { /* SSE2 available */ fptr_delta8_rgba = &sse2_delta8_rgba; fptr_delta8_bgra = &sse2_delta8_bgra; fptr_delta8_argb = &sse2_delta8_argb; fptr_delta8_abgr = &sse2_delta8_abgr; fptr_delta8_gray8 = &sse2_delta8_gray8; - Debug(4,"Delta: Using SSE2 delta functions"); + Debug(4, "Delta: Using SSE2 delta functions"); } else if ( neonversion >= 1 ) { /* ARM Neon available */ #if defined(__aarch64__) @@ -349,14 +378,14 @@ void Image::Initialise() { fptr_delta8_argb = &neon64_armv8_delta8_argb; fptr_delta8_abgr = &neon64_armv8_delta8_abgr; fptr_delta8_gray8 = &neon64_armv8_delta8_gray8; - Debug(4,"Delta: Using ARM Neon (AArch64) delta functions"); + Debug(4, "Delta: Using ARM Neon (AArch64) delta functions"); #elif defined(__arm__) fptr_delta8_rgba = &neon32_armv7_delta8_rgba; fptr_delta8_bgra = &neon32_armv7_delta8_bgra; fptr_delta8_argb = &neon32_armv7_delta8_argb; fptr_delta8_abgr = &neon32_armv7_delta8_abgr; fptr_delta8_gray8 = &neon32_armv7_delta8_gray8; - Debug(4,"Delta: Using ARM Neon (AArch32) delta functions"); + Debug(4, "Delta: Using ARM Neon (AArch32) delta functions"); #else Panic("Bug: Non ARM platform but neon present"); #endif @@ -414,7 +443,7 @@ void Image::Initialise() { } /* Run the delta8 RGBA function */ - (*fptr_delta8_rgba)(delta8_1,delta8_2,delta8_rgba_res,32); + (*fptr_delta8_rgba)(delta8_1,delta8_2,delta8_rgba_res, 32); /* Compare results with expected results */ for ( int i=0; i < 32; i++ ) { @@ -433,77 +462,45 @@ void Image::Initialise() { fptr_deinterlace_4field_argb = &std_deinterlace_4field_argb; fptr_deinterlace_4field_abgr = &std_deinterlace_4field_abgr; fptr_deinterlace_4field_gray8 = &std_deinterlace_4field_gray8; - Debug(4,"Deinterlace: Using standard functions"); + Debug(4, "Deinterlace: Using standard functions"); #if defined(__i386__) && !defined(__x86_64__) /* Use SSE2 aligned memory copy? */ - if ( config.cpu_extensions && sseversion >= 20 ) { + if ( config.cpu_extensions && sse_version >= 20 ) { fptr_imgbufcpy = &sse2_aligned_memcpy; - Debug(4,"Image buffer copy: Using SSE2 aligned memcpy"); + Debug(4, "Image buffer copy: Using SSE2 aligned memcpy"); } else { fptr_imgbufcpy = &memcpy; - Debug(4,"Image buffer copy: Using standard memcpy"); + Debug(4, "Image buffer copy: Using standard memcpy"); } #else fptr_imgbufcpy = &memcpy; - Debug(4,"Image buffer copy: Using standard memcpy"); + Debug(4, "Image buffer copy: Using standard memcpy"); #endif - /* Code below relocated from zm_local_camera */ - Debug( 3, "Setting up static colour tables" ); - y_table = y_table_global; uv_table = uv_table_global; r_v_table = r_v_table_global; g_v_table = g_v_table_global; g_u_table = g_u_table_global; b_u_table = b_u_table_global; - /* - y_table = new unsigned char[256]; - for ( int i = 0; i <= 255; i++ ) - { - unsigned char c = i; - if ( c <= 16 ) - y_table[c] = 0; - else if ( c >= 235 ) - y_table[c] = 255; - else - y_table[c] = (255*(c-16))/219; - } - - uv_table = new signed char[256]; - for ( int i = 0; i <= 255; i++ ) - { - unsigned char c = i; - if ( c <= 16 ) - uv_table[c] = -127; - else if ( c >= 240 ) - uv_table[c] = 127; - else - uv_table[c] = (127*(c-128))/112; - } - - r_v_table = new short[255]; - g_v_table = new short[255]; - g_u_table = new short[255]; - b_u_table = new short[255]; - for ( int i = 0; i < 255; i++ ) - { - r_v_table[i] = (1402*(i-128))/1000; - g_u_table[i] = (344*(i-128))/1000; - g_v_table[i] = (714*(i-128))/1000; - b_u_table[i] = (1772*(i-128))/1000; - } - */ initialised = true; } /* Requests a writeable buffer to the image. This is safer than buffer() because this way we can guarantee that a buffer of required size exists */ -uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) { +uint8_t* Image::WriteBuffer( + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder) { - if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) { - Error("WriteBuffer called with unexpected colours: %d",p_colours); + if ( p_colours != ZM_COLOUR_GRAY8 + && + p_colours != ZM_COLOUR_RGB24 + && + p_colours != ZM_COLOUR_RGB32 ) { + Error("WriteBuffer called with unexpected colours: %d", p_colours); return NULL; } @@ -513,6 +510,7 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei } if ( p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder ) { + unsigned int newsize = (p_width * p_height) * p_colours; if ( buffer == NULL ) { @@ -533,23 +531,34 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei width = p_width; height = p_height; colours = p_colours; + linesize = p_width * p_colours; subpixelorder = p_subpixelorder; pixels = height*width; size = newsize; - } // end if need to re-alloc buffer + } // end if need to re-alloc buffer return buffer; } -/* Assign an existing buffer to the image instead of copying from a source buffer. The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. */ -void Image::AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype) { +/* Assign an existing buffer to the image instead of copying from a source buffer. + The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. +*/ +void Image::AssignDirect( + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder, + uint8_t *new_buffer, + const size_t buffer_size, + const int p_buffertype) { + if ( new_buffer == NULL ) { Error("Attempt to directly assign buffer from a NULL pointer"); return; } if ( !p_height || !p_width ) { - Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height); + Error("Attempt to directly assign buffer with invalid width or height: %d %d", p_width, p_height); return; } @@ -561,7 +570,8 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh unsigned int new_buffer_size = ((p_width*p_height)*p_colours); if ( buffer_size < new_buffer_size ) { - Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size ); + Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu", + buffer_size, p_width, p_height, p_colours, new_buffer_size); return; } @@ -573,13 +583,15 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh width = p_width; height = p_height; colours = p_colours; + linesize = width * colours; subpixelorder = p_subpixelorder; pixels = height*width; size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size /* Copy into the held buffer */ - if ( new_buffer != buffer ) + if ( new_buffer != buffer ) { (*fptr_imgbufcpy)(buffer, new_buffer, size); + } /* Free the new buffer */ DumpBuffer(new_buffer, p_buffertype); @@ -591,6 +603,7 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh width = p_width; height = p_height; colours = p_colours; + linesize = width*colours; subpixelorder = p_subpixelorder; pixels = height*width; size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size @@ -599,10 +612,15 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh buffertype = p_buffertype; buffer = new_buffer; } +} // end void Image::AssignDirect -} - -void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) { +void Image::Assign( + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder, + const uint8_t* new_buffer, + const size_t buffer_size) { unsigned int new_size = (p_width * p_height) * p_colours; if ( new_buffer == NULL ) { @@ -611,17 +629,17 @@ void Image::Assign(const unsigned int p_width, const unsigned int p_height, cons } if ( buffer_size < new_size ) { - Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size); + Error("Attempt to assign buffer from an undersized buffer of size: %zu", buffer_size); return; } if ( !p_height || !p_width ) { - Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height); + Error("Attempt to assign buffer with invalid width or height: %d %d", p_width, p_height); return; } if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) { - Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours); + Error("Attempt to assign buffer with unexpected colours per pixel: %d", p_colours); return; } @@ -647,26 +665,32 @@ void Image::Assign(const unsigned int p_width, const unsigned int p_height, cons size = new_size; } - if(new_buffer != buffer) + if ( new_buffer != buffer ) (*fptr_imgbufcpy)(buffer, new_buffer, size); - } -void Image::Assign( const Image &image ) { - unsigned int new_size = (image.width * image.height) * image.colours; +void Image::Assign(const Image &image) { + unsigned int new_size = image.height * image.linesize; if ( image.buffer == NULL ) { Error("Attempt to assign image with an empty buffer"); return; } - if ( image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32 ) { - Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours); + if ( image.colours != ZM_COLOUR_GRAY8 + && + image.colours != ZM_COLOUR_RGB24 + && + image.colours != ZM_COLOUR_RGB32 ) { + Error("Attempt to assign image with unexpected colours per pixel: %d", image.colours); return; } - if ( !buffer || image.width != width || image.height != height - || image.colours != colours || image.subpixelorder != subpixelorder) { + if ( !buffer + || image.width != width || image.height != height + || image.colours != colours || image.subpixelorder != subpixelorder + || image.linesize != linesize + ) { if ( holdbuffer && buffer ) { if ( new_size > allocation ) { @@ -686,110 +710,108 @@ void Image::Assign( const Image &image ) { colours = image.colours; subpixelorder = image.subpixelorder; size = new_size; + linesize = image.linesize; } - if(image.buffer != buffer) + if ( image.buffer != buffer ) (*fptr_imgbufcpy)(buffer, image.buffer, size); } -Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) { +Image *Image::HighlightEdges( + Rgb colour, + unsigned int p_colours, + unsigned int p_subpixelorder, + const Box *limits + ) { if ( colours != ZM_COLOUR_GRAY8 ) { Panic("Attempt to highlight image edges when colours = %d", colours); } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,p_subpixelorder); + colour = rgb_convert(colour, p_subpixelorder); /* Create a new image of the target format */ - Image *high_image = new Image( width, height, p_colours, p_subpixelorder ); + Image *high_image = new Image(width, height, p_colours, p_subpixelorder); uint8_t* high_buff = high_image->WriteBuffer(width, height, p_colours, p_subpixelorder); /* Set image to all black */ high_image->Clear(); - unsigned int lo_x = limits?limits->Lo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; + unsigned int lo_x = limits ? limits->Lo().X() : 0; + unsigned int lo_y = limits ? limits->Lo().Y() : 0; + unsigned int hi_x = limits ? limits->Hi().X() : width-1; + unsigned int hi_y = limits ? limits->Hi().Y() : height-1; - if ( p_colours == ZM_COLOUR_GRAY8 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - uint8_t* phigh = high_buff + (y * width) + lo_x; - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) - { + if ( p_colours == ZM_COLOUR_GRAY8 ) { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) { + const uint8_t* p = buffer + (y * linesize) + lo_x; + uint8_t* phigh = high_buff + (y * linesize) + lo_x; + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) { bool edge = false; - if ( *p ) - { + if ( *p ) { + edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1)) || (y > 0 && !*(p-width)) || (y < (height-1) && !*(p+width)); +#if 0 if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; +#endif } - if ( edge ) - { + if ( edge ) { *phigh = colour; } } } - } - else if ( p_colours == ZM_COLOUR_RGB24 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - uint8_t* phigh = high_buff + (((y * width) + lo_x) * 3); - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) - { + } else if ( p_colours == ZM_COLOUR_RGB24 ) { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) { + const uint8_t* p = buffer + (y * linesize) + lo_x; + uint8_t* phigh = high_buff + (((y * linesize) + lo_x) * 3); + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) { bool edge = false; - if ( *p ) - { + if ( *p ) { + edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1)) || (y > 0 && !*(p-width)) || (y < (height-1) && !*(p+width)); +#if 0 if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; +#endif } - if ( edge ) - { + if ( edge ) { RED_PTR_RGBA(phigh) = RED_VAL_RGBA(colour); GREEN_PTR_RGBA(phigh) = GREEN_VAL_RGBA(colour); BLUE_PTR_RGBA(phigh) = BLUE_VAL_RGBA(colour); } } } - } - else if ( p_colours == ZM_COLOUR_RGB32 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - Rgb* phigh = (Rgb*)(high_buff + (((y * width) + lo_x) * 4)); - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) - { + } else if ( p_colours == ZM_COLOUR_RGB32 ) { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) { + const uint8_t* p = buffer + (y * linesize) + lo_x; + Rgb* phigh = (Rgb*)(high_buff + (((y * linesize) + lo_x) * 4)); + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) { bool edge = false; - if ( *p ) - { + if ( *p ) { + edge = (x > 0 && !*(p-1)) || (x < (width-1) && !*(p+1)) || (y > 0 && !*(p-width)) || (y < (height-1) && !*(p+width)); +#if 0 if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; +#endif } - if ( edge ) - { + if ( edge ) { *phigh = colour; } } } } - return( high_image ); + return high_image; } -bool Image::ReadRaw( const char *filename ) { +bool Image::ReadRaw(const char *filename) { FILE *infile; - if ( (infile = fopen( filename, "rb" )) == NULL ) { + if ( (infile = fopen(filename, "rb")) == NULL ) { Error("Can't open %s: %s", filename, strerror(errno)); return false; } @@ -881,19 +903,16 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int new_height = cinfo->image_height; if ( width != new_width || height != new_height ) { - Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); + Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height); } - switch(p_colours) { + switch ( p_colours ) { case ZM_COLOUR_GRAY8: - { cinfo->out_color_space = JCS_GRAYSCALE; new_colours = ZM_COLOUR_GRAY8; new_subpixelorder = ZM_SUBPIX_ORDER_NONE; break; - } case ZM_COLOUR_RGB32: - { #ifdef JCS_EXTENSIONS new_colours = ZM_COLOUR_RGB32; if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { @@ -914,10 +933,8 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int #else Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); #endif - } case ZM_COLOUR_RGB24: default: - { new_colours = ZM_COLOUR_RGB24; if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGR ) { #ifdef JCS_EXTENSIONS @@ -941,8 +958,7 @@ cinfo->out_color_space = JCS_RGB; new_subpixelorder = ZM_SUBPIX_ORDER_RGB; } break; - } - } + } // end switch p_colours if ( WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL ) { Error("Failed requesting writeable buffer for reading JPEG image."); @@ -953,11 +969,10 @@ cinfo->out_color_space = JCS_RGB; jpeg_start_decompress(cinfo); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = width * colours; /* physical row width in buffer */ + JSAMPROW row_pointer = buffer; while ( cinfo->output_scanline < cinfo->output_height ) { - row_pointer = &buffer[cinfo->output_scanline * row_stride]; jpeg_read_scanlines(cinfo, &row_pointer, 1); + row_pointer += linesize; } jpeg_finish_decompress(cinfo); @@ -971,40 +986,39 @@ cinfo->out_color_space = JCS_RGB; // Note quality=zero means default bool Image::WriteJpeg(const char *filename, int quality_override) const { - return Image::WriteJpeg(filename, quality_override, (timeval){0,0}); + return Image::WriteJpeg(filename, quality_override, (timeval){0,0}, false); } bool Image::WriteJpeg(const char *filename) const { - return Image::WriteJpeg(filename, 0, (timeval){0,0}); + return Image::WriteJpeg(filename, 0, (timeval){0,0}, false); } bool Image::WriteJpeg(const char *filename, bool on_blocking_abort) const { return Image::WriteJpeg(filename, 0, (timeval){0,0}, on_blocking_abort); } bool Image::WriteJpeg(const char *filename, struct timeval timestamp) const { - return Image::WriteJpeg(filename, 0, timestamp); + return Image::WriteJpeg(filename, 0, timestamp, false); } bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const { return Image::WriteJpeg(filename, quality_override, timestamp, false); } + bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp, bool on_blocking_abort) const { if ( config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8) ) { Image temp_image(*this); temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort); } - int quality = quality_override?quality_override:config.jpeg_file_quality; + int quality = quality_override ? quality_override : config.jpeg_file_quality; struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality]; FILE *outfile = NULL; static int raw_fd = 0; - bool need_create_comp = false; raw_fd = 0; if ( !cinfo ) { cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct; cinfo->err = jpeg_std_error(&jpg_err.pub); jpeg_create_compress(cinfo); - need_create_comp = true; } if ( !on_blocking_abort ) { jpg_err.pub.error_exit = zm_jpeg_error_exit; @@ -1022,8 +1036,6 @@ bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval return false; } } - if ( need_create_comp ) - jpeg_create_compress(cinfo); if ( !on_blocking_abort ) { if ( (outfile = fopen(filename, "wb")) == NULL ) { @@ -1046,7 +1058,7 @@ bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval cinfo->image_width = width; /* image width and height, in pixels */ cinfo->image_height = height; - switch (colours) { + switch ( colours ) { case ZM_COLOUR_GRAY8: cinfo->input_components = 1; cinfo->in_color_space = JCS_GRAYSCALE; @@ -1054,13 +1066,16 @@ bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval case ZM_COLOUR_RGB32: #ifdef JCS_EXTENSIONS cinfo->input_components = 4; - if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { + if ( subpixelorder == ZM_SUBPIX_ORDER_RGBA ) { + cinfo->in_color_space = JCS_EXT_RGBX; + } else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { cinfo->in_color_space = JCS_EXT_BGRX; } else if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) { cinfo->in_color_space = JCS_EXT_XRGB; } else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) { cinfo->in_color_space = JCS_EXT_XBGR; } else { + Warning("Unknwon subpixelorder %d", subpixelorder); /* Assume RGBA */ cinfo->in_color_space = JCS_EXT_RGBX; } @@ -1130,15 +1145,12 @@ cinfo->out_color_space = JCS_RGB; jpeg_write_marker(cinfo, EXIF_CODE, (const JOCTET *)exiftimes, sizeof(exiftimes)); } - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ + JSAMPROW row_pointer = buffer; /* pointer to a single row */ while ( cinfo->next_scanline < cinfo->image_height ) { - row_pointer = &buffer[cinfo->next_scanline * row_stride]; jpeg_write_scanlines(cinfo, &row_pointer, 1); + row_pointer += linesize; } - jpeg_finish_compress(cinfo); - fclose(outfile); return true; @@ -1252,11 +1264,10 @@ cinfo->out_color_space = JCS_RGB; jpeg_start_decompress(cinfo); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = width * colours; /* physical row width in buffer */ + JSAMPROW row_pointer = buffer; /* pointer to a single row */ while ( cinfo->output_scanline < cinfo->output_height ) { - row_pointer = &buffer[cinfo->output_scanline * row_stride]; jpeg_read_scanlines(cinfo, &row_pointer, 1); + row_pointer += linesize; } jpeg_finish_decompress(cinfo); @@ -1271,7 +1282,7 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr return temp_image.EncodeJpeg(outbuffer, outbuffer_size, quality_override); } - int quality = quality_override?quality_override:config.jpeg_stream_quality; + int quality = quality_override ? quality_override : config.jpeg_stream_quality; struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality]; @@ -1288,7 +1299,7 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr cinfo->image_width = width; /* image width and height, in pixels */ cinfo->image_height = height; - switch (colours) { + switch ( colours ) { case ZM_COLOUR_GRAY8: cinfo->input_components = 1; cinfo->in_color_space = JCS_GRAYSCALE; @@ -1296,13 +1307,16 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr case ZM_COLOUR_RGB32: #ifdef JCS_EXTENSIONS cinfo->input_components = 4; - if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { + if ( subpixelorder == ZM_SUBPIX_ORDER_RGBA ) { + cinfo->in_color_space = JCS_EXT_RGBX; + } else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { cinfo->in_color_space = JCS_EXT_BGRX; } else if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) { cinfo->in_color_space = JCS_EXT_XRGB; } else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) { cinfo->in_color_space = JCS_EXT_XBGR; } else { + Warning("unknown subpixelorder %d", subpixelorder); /* Assume RGBA */ cinfo->in_color_space = JCS_EXT_RGBX; } @@ -1343,11 +1357,10 @@ cinfo->out_color_space = JCS_RGB; jpeg_start_compress(cinfo, TRUE); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ + JSAMPROW row_pointer = buffer; while ( cinfo->next_scanline < cinfo->image_height ) { - row_pointer = &buffer[cinfo->next_scanline * row_stride]; jpeg_write_scanlines(cinfo, &row_pointer, 1); + row_pointer += linesize; } jpeg_finish_compress(cinfo); @@ -1385,11 +1398,12 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig unsigned int new_height = (hi_y-lo_y)+1; if ( lo_x > hi_x || lo_y > hi_y ) { - Error( "Invalid or reversed crop region %d,%d -> %d,%d", lo_x, lo_y, hi_x, hi_y ); - return( false ); + Error("Invalid or reversed crop region %d,%d -> %d,%d", lo_x, lo_y, hi_x, hi_y); + return false; } if ( hi_x > (width-1) || ( hi_y > (height-1) ) ) { - Error( "Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d", lo_x, lo_y, hi_x, hi_y, width-1, height-1 ); + Error("Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d", + lo_x, lo_y, hi_x, hi_y, width-1, height-1); return false; } @@ -1400,9 +1414,9 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig unsigned int new_size = new_width*new_height*colours; uint8_t *new_buffer = AllocBuffer(new_size); - unsigned int new_stride = new_width*colours; + unsigned int new_stride = new_width * colours; for ( unsigned int y = lo_y, ny = 0; y <= hi_y; y++, ny++ ) { - unsigned char *pbuf = &buffer[((y*width)+lo_x)*colours]; + unsigned char *pbuf = &buffer[((y*linesize)+lo_x)]; unsigned char *pnbuf = &new_buffer[(ny*new_width)*colours]; memcpy( pnbuf, pbuf, new_stride ); } @@ -1412,8 +1426,8 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig return true; } -bool Image::Crop( const Box &limits ) { - return Crop( limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY() ); +bool Image::Crop(const Box &limits) { + return Crop(limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY()); } /* Far from complete */ @@ -1434,7 +1448,7 @@ void Image::Overlay( const Image &image ) { const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) { + while ( pdest < max_ptr ) { if ( *psrc ) { *pdest = *psrc; } @@ -1450,7 +1464,7 @@ void Image::Overlay( const Image &image ) { const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) { + while ( pdest < max_ptr ) { if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) { RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); @@ -1461,7 +1475,7 @@ void Image::Overlay( const Image &image ) { } /* RGB32 ontop of grayscale - convert to same format first - complete */ - } else if( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB32 ) { + } else if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB32 ) { Colourise(image.colours, image.subpixelorder); const Rgb* const max_ptr = (Rgb*)(buffer+size); @@ -1470,7 +1484,7 @@ void Image::Overlay( const Image &image ) { if ( subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA ) { /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ - while (prdest < max_ptr) { + while ( prdest < max_ptr) { if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) { *prdest = *prsrc; } @@ -1479,7 +1493,7 @@ void Image::Overlay( const Image &image ) { } } else { /* ABGR\ARGB subpixel order - Alpha byte is first */ - while (prdest < max_ptr) { + while ( prdest < max_ptr) { if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) { *prdest = *prsrc; } @@ -1494,7 +1508,7 @@ void Image::Overlay( const Image &image ) { const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) { + while ( pdest < max_ptr ) { if ( *psrc ) { RED_PTR_RGBA(pdest) = GREEN_PTR_RGBA(pdest) = BLUE_PTR_RGBA(pdest) = *psrc; } @@ -1508,7 +1522,7 @@ void Image::Overlay( const Image &image ) { const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) { + while ( pdest < max_ptr ) { if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) { RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); @@ -1631,8 +1645,7 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) { } else { Error("Overlay called with unexpected colours: %d", colours); } - -} +} // end void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) void Image::Blend( const Image &image, int transparency ) { #ifdef ZM_IMAGE_PROFILING @@ -1675,7 +1688,7 @@ void Image::Blend( const Image &image, int transparency ) { AssignDirect(width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); } -Image *Image::Merge( unsigned int n_images, Image *images[] ) { +Image *Image::Merge(unsigned int n_images, Image *images[]) { if ( n_images == 1 ) return new Image(*images[0]); unsigned int width = images[0]->width; @@ -1704,7 +1717,7 @@ Image *Image::Merge( unsigned int n_images, Image *images[] ) { return result; } -Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { +Image *Image::Merge(unsigned int n_images, Image *images[], double weight) { if ( n_images == 1 ) return new Image(*images[0]); unsigned int width = images[0]->width; @@ -1717,7 +1730,7 @@ Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { } } - Image *result = new Image( *images[0] ); + Image *result = new Image(*images[0]); unsigned int size = result->size; double factor = 1.0*weight; for ( unsigned int i = 1; i < n_images; i++ ) { @@ -1746,7 +1759,7 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres } } - Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder ); + Image *result = new Image(width, height, images[0]->colours, images[0]->subpixelorder); unsigned int size = result->size; for ( unsigned int c = 0; c < colours; c++ ) { unsigned int ref_colour_rgb = RGB_VAL(ref_colour,c); @@ -1771,7 +1784,7 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres return result; } -/* New function to allow buffer re-using instead of allocationg memory for the delta image every time */ +/* New function to allow buffer re-using instead of allocating memory for the delta image every time */ void Image::Delta( const Image &image, Image* targetimage) const { #ifdef ZM_IMAGE_PROFILING struct timespec start,end,diff; @@ -1794,7 +1807,7 @@ void Image::Delta( const Image &image, Image* targetimage) const { clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); #endif - switch (colours) { + switch ( colours ) { case ZM_COLOUR_RGB24: if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) { /* BGR subpixel order */ @@ -1858,7 +1871,7 @@ const Coord Image::centreCoord( const char *text ) const { } int x = (width - (max_line_len * ZM_CHAR_WIDTH) ) / 2; int y = (height - (line_no * LINE_HEIGHT) ) / 2; - return( Coord( x, y ) ); + return Coord(x, y); } /* RGB32 compatible: complete */ @@ -1905,11 +1918,11 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour /* RGB32 compatible: complete */ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour ) { - strncpy( text, p_text, sizeof(text)-1 ); + strncpy(text, p_text, sizeof(text)-1); unsigned int index = 0; unsigned int line_no = 0; - unsigned int text_len = strlen( text ); + unsigned int text_len = strlen(text); unsigned int line_len = 0; const char *line = text; @@ -1928,10 +1941,10 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int const bool bg_trans = (bg_colour == RGB_TRANSPARENT); int zm_text_bitmask = 0x80; - if (size == 2) + if ( size == 2 ) zm_text_bitmask = 0x8000; - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { + while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) { unsigned int line_width = line_len * ZM_CHAR_WIDTH * size; @@ -1967,10 +1980,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) @@ -1989,10 +2011,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { @@ -2016,10 +2047,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int Rgb* temp_ptr = (Rgb*)ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { @@ -2033,7 +2073,7 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int } } else { - Panic("Annotate called with unexpected colours: %d",colours); + Error("Annotate called with unexpected colours: %d", colours); return; } @@ -2123,7 +2163,7 @@ void Image::DeColourise() { subpixelorder = ZM_SUBPIX_ORDER_NONE; size = width * height; - if ( colours == ZM_COLOUR_RGB32 && config.cpu_extensions && sseversion >= 35 ) { + if ( colours == ZM_COLOUR_RGB32 && config.cpu_extensions && sse_version >= 35 ) { /* Use SSSE3 functions */ switch (subpixelorder) { case ZM_SUBPIX_ORDER_BGRA: @@ -2250,12 +2290,12 @@ void Image::Fill( Rgb colour, int density, const Box *limits ) { if ( density <= 1 ) return Fill(colour,limits); - if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { + if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { Panic("Attempt to fill image with unexpected colours %d", colours); } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,subpixelorder); + colour = rgb_convert(colour, subpixelorder); unsigned int lo_x = limits?limits->Lo().X():0; unsigned int lo_y = limits?limits->Lo().Y():0; @@ -2317,9 +2357,7 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { double grad; - //Debug( 9, "dx: %.2lf, dy: %.2lf", dx, dy ); if ( fabs(dx) <= fabs(dy) ) { - //Debug( 9, "dx <= dy" ); if ( y1 != y2 ) grad = dx/dy; else @@ -2329,9 +2367,7 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) { int y, yinc = (y1= Logger::DEBUG9 ) { for ( int i = 0; i < n_global_edges; i++ ) { - Debug( 9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m ); + Debug(9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", + i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m); } } #endif @@ -2441,11 +2478,12 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) { break; } } - qsort( active_edges, n_active_edges, sizeof(*active_edges), Edge::CompareX ); + qsort(active_edges, n_active_edges, sizeof(*active_edges), Edge::CompareX); #ifndef ZM_DBG_OFF if ( logLevel() >= Logger::DEBUG9 ) { for ( int i = 0; i < n_active_edges; i++ ) { - Debug( 9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); + Debug(9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", + y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); } } #endif @@ -2486,7 +2524,7 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) { for ( int i = n_active_edges-1; i >= 0; i-- ) { if ( y >= active_edges[i].max_y ) { // Or >= as per sheets - Debug( 9, "Deleting active_edge" ); + Debug(9, "Deleting active_edge"); if ( i < (n_active_edges-1) ) { //memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); @@ -2499,27 +2537,21 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) { } while ( n_global_edges || n_active_edges ); } -void Image::Fill( Rgb colour, const Polygon &polygon ) { - Fill( colour, 1, polygon ); +void Image::Fill(Rgb colour, const Polygon &polygon) { + Fill(colour, 1, polygon); } -/* RGB32 compatible: complete */ -void Image::Rotate( int angle ) { - +void Image::Rotate(int angle) { angle %= 360; - if ( !angle ) { + if ( !angle || angle%90 ) { return; } - if ( angle%90 ) { - return; - } - unsigned int new_height = height; unsigned int new_width = width; uint8_t* rotate_buffer = AllocBuffer(size); - switch( angle ) { + switch ( angle ) { case 90 : { new_height = width; @@ -2538,17 +2570,17 @@ void Image::Rotate( int angle ) { } } else if ( colours == ZM_COLOUR_RGB32 ) { Rgb* s_rptr = (Rgb*)s_ptr; - for ( unsigned int i = new_width; i > 0; i-- ) { + for ( unsigned int i = new_width; i; i-- ) { Rgb* d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); - for ( unsigned int j = new_height; j > 0; j-- ) { + for ( unsigned int j = new_height; j; j-- ) { *d_rptr = *s_rptr++; d_rptr += new_width; } } } else /* Assume RGB24 */ { - for ( unsigned int i = new_width; i > 0; i-- ) { + for ( unsigned int i = new_width; i; i-- ) { unsigned char *d_ptr = rotate_buffer+((i-1)*3); - for ( unsigned int j = new_height; j > 0; j-- ) { + for ( unsigned int j = new_height; j; j-- ) { *d_ptr = *s_ptr++; *(d_ptr+1) = *s_ptr++; *(d_ptr+2) = *s_ptr++; @@ -2627,8 +2659,8 @@ void Image::Rotate( int angle ) { } } - AssignDirect( new_width, new_height, colours, subpixelorder, rotate_buffer, size, ZM_BUFTYPE_ZM); -} + AssignDirect(new_width, new_height, colours, subpixelorder, rotate_buffer, size, ZM_BUFTYPE_ZM); +} // void Image::Rotate(int angle) /* RGB32 compatible: complete */ void Image::Flip( bool leftright ) { @@ -2677,20 +2709,19 @@ void Image::Flip( bool leftright ) { unsigned char *s_ptr = buffer+(height*line_bytes); unsigned char *d_ptr = flip_buffer; - while( s_ptr > buffer ) { + while ( s_ptr > buffer ) { s_ptr -= line_bytes; - memcpy( d_ptr, s_ptr, line_bytes ); + memcpy(d_ptr, s_ptr, line_bytes); d_ptr += line_bytes; } } - AssignDirect( width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM); - + AssignDirect(width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM); } -void Image::Scale( unsigned int factor ) { +void Image::Scale(unsigned int factor) { if ( !factor ) { - Error( "Bogus scale factor %d found", factor ); + Error("Bogus scale factor %d found", factor); return; } if ( factor == ZM_SCALE_BASE ) { @@ -2700,6 +2731,7 @@ void Image::Scale( unsigned int factor ) { unsigned int new_width = (width*factor)/ZM_SCALE_BASE; unsigned int new_height = (height*factor)/ZM_SCALE_BASE; + // Why larger than we need? size_t scale_buffer_size = (new_width+1) * (new_height+1) * colours; uint8_t* scale_buffer = AllocBuffer(scale_buffer_size); @@ -2747,7 +2779,7 @@ void Image::Scale( unsigned int factor ) { unsigned int last_h_index = 0; unsigned int last_w_index = 0; unsigned int h_index; - for ( unsigned int y = 0; y < (unsigned int)height; y++ ) { + for ( unsigned int y = 0; y < height; y++ ) { h_count += factor; h_index = h_count/ZM_SCALE_BASE; if ( h_index > last_h_index ) { @@ -2756,7 +2788,7 @@ void Image::Scale( unsigned int factor ) { last_w_index = 0; unsigned char *ps = &buffer[y*wc]; - for ( unsigned int x = 0; x < (unsigned int)width; x++ ) { + for ( unsigned int x = 0; x < width; x++ ) { w_count += factor; w_index = w_count/ZM_SCALE_BASE; @@ -2774,10 +2806,8 @@ void Image::Scale( unsigned int factor ) { } new_width = last_w_index; new_height = last_h_index; - } - - AssignDirect( new_width, new_height, colours, subpixelorder, scale_buffer, scale_buffer_size, ZM_BUFTYPE_ZM); - + } // end foreach line + AssignDirect(new_width, new_height, colours, subpixelorder, scale_buffer, scale_buffer_size, ZM_BUFTYPE_ZM); } void Image::Deinterlace_Discard() { @@ -2818,7 +2848,6 @@ void Image::Deinterlace_Discard() { } else { Error("Deinterlace called with unexpected colours: %d", colours); } - } void Image::Deinterlace_Linear() { @@ -3456,7 +3485,7 @@ __attribute__((noinline)) void fast_delta8_bgr(const uint8_t* col1, const uint8_ int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); @@ -3485,7 +3514,7 @@ __attribute__((noinline)) void std_delta8_bgr(const uint8_t* col1, const uint8_t int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); @@ -3503,7 +3532,7 @@ __attribute__((noinline)) void fast_delta8_rgba(const uint8_t* col1, const uint8 int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { r = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); b = abs(col1[2] - col2[2]); @@ -3532,7 +3561,7 @@ __attribute__((noinline)) void std_delta8_rgba(const uint8_t* col1, const uint8_ int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { r = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); b = abs(col1[2] - col2[2]); @@ -3550,7 +3579,7 @@ __attribute__((noinline)) void fast_delta8_bgra(const uint8_t* col1, const uint8 int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); @@ -3578,7 +3607,7 @@ __attribute__((noinline)) void std_delta8_bgra(const uint8_t* col1, const uint8_ int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); @@ -3596,7 +3625,7 @@ __attribute__((noinline)) void fast_delta8_argb(const uint8_t* col1, const uint8 int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { r = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); b = abs(col1[3] - col2[3]); @@ -3619,12 +3648,13 @@ __attribute__((noinline)) void fast_delta8_argb(const uint8_t* col1, const uint8 result += 4; } } + __attribute__((noinline)) void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { r = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); b = abs(col1[3] - col2[3]); @@ -3642,7 +3672,7 @@ __attribute__((noinline)) void fast_delta8_abgr(const uint8_t* col1, const uint8 int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); r = abs(col1[3] - col2[3]); @@ -3669,7 +3699,7 @@ __attribute__((noinline)) void std_delta8_abgr(const uint8_t* col1, const uint8_ int r,g,b; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while (result < max_ptr) { b = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); r = abs(col1[3] - col2[3]); diff --git a/src/zm_image.h b/src/zm_image.h index 6be574dfb..1b8f614b9 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -152,9 +152,11 @@ protected: static struct zm_error_mgr jpg_err; unsigned int width; + unsigned int linesize; unsigned int height; unsigned int pixels; unsigned int colours; + unsigned int padding; unsigned int size; unsigned int subpixelorder; unsigned long allocation; @@ -165,15 +167,18 @@ protected: public: Image(); - explicit Image( const char *filename ); - Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0); + explicit Image(const char *filename); + Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0, unsigned int padding=0); + Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0, unsigned int padding=0); explicit Image( const Image &p_image ); explicit Image( const AVFrame *frame ); + ~Image(); static void Initialise(); static void Deinitialise(); inline unsigned int Width() const { return width; } + inline unsigned int LineSize() const { return linesize; } inline unsigned int Height() const { return height; } inline unsigned int Pixels() const { return pixels; } inline unsigned int Colours() const { return colours; } @@ -182,7 +187,7 @@ public: /* Internal buffer should not be modified from functions outside of this class */ inline const uint8_t* Buffer() const { return buffer; } - inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[colours*((y*width)+x)]; } + inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[(y*linesize)+x]; } /* Request writeable buffer */ uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder); @@ -193,21 +198,34 @@ public: if ( !holdbuffer ) DumpImgBuffer(); - width = height = colours = size = pixels = subpixelorder = 0; + width = linesize = height = colours = size = pixels = subpixelorder = 0; } - void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size); - void Assign( const Image &image ); - void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype); + void Assign( + unsigned int p_width, + unsigned int p_height, + unsigned int p_colours, + unsigned int p_subpixelorder, + const uint8_t* new_buffer, + const size_t buffer_size); + void Assign(const Image &image); + void AssignDirect( + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder, + uint8_t *new_buffer, + const size_t buffer_size, + const int p_buffertype); - inline void CopyBuffer( const Image &image ) { + inline void CopyBuffer(const Image &image) { Assign(image); } - inline Image &operator=( const Image &image ) { + inline Image &operator=(const Image &image) { Assign(image); return *this; } - inline Image &operator=( const unsigned char *new_buffer ) { + inline Image &operator=(const unsigned char *new_buffer) { (*fptr_imgbufcpy)(buffer, new_buffer, size); return *this; } diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index c6030f08a..8274c1ce5 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -504,7 +504,7 @@ LocalCamera::LocalCamera( subpixelorder = ZM_SUBPIX_ORDER_NONE; } else if ( palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8 ) { /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if ( config.cpu_extensions && sseversion >= 35 ) { + if ( config.cpu_extensions && sse_version >= 35 ) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { @@ -616,7 +616,7 @@ LocalCamera::LocalCamera( } } else if ( (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8 ) { /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if ( config.cpu_extensions && sseversion >= 35 ) { + if ( config.cpu_extensions && sse_version >= 35 ) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d06baf271..31ece8a0e 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -131,12 +131,12 @@ Monitor::MonitorLink::~MonitorLink() { } bool Monitor::MonitorLink::connect() { - if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) { + if ( !last_connect_time || (time( 0 ) - last_connect_time) > ZM_MAX_RESTART_DELAY ) { last_connect_time = time( 0 ); mem_size = sizeof(SharedData) + sizeof(TriggerData); - Debug( 1, "link.mem.size=%d", mem_size ); + Debug(1, "link.mem.size=%d", mem_size); #if ZM_MEM_MAPPED map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); if ( map_fd < 0 ) { @@ -379,6 +379,7 @@ Monitor::Monitor( strncpy(event_prefix, p_event_prefix, sizeof(event_prefix)-1); strncpy(label_format, p_label_format, sizeof(label_format)-1); + Debug(1, "encoder params %s", encoderparams.c_str()); // Change \n to actual line feeds char *token_ptr = label_format; @@ -531,6 +532,7 @@ Monitor::Monitor( shared_data->last_write_index, shared_data->last_write_time ); 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; @@ -605,7 +607,7 @@ bool Monitor::connect() { if ( mem_ptr == MAP_FAILED ) Fatal("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno); if ( mem_ptr == NULL ) { - Error("mmap gave a null address:"); + Error("mmap gave a NULL address:"); } else { Debug(3, "mmapped to %p", mem_ptr); } @@ -1406,9 +1408,6 @@ bool Monitor::Analyse() { score += trigger_data->trigger_score; Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score); if ( !event ) { - // How could it have a length already? - //if ( cause.length() ) - //cause += ", "; cause += trigger_data->trigger_cause; } Event::StringSet noteSet; @@ -1488,6 +1487,7 @@ bool Monitor::Analyse() { score += 50; } } else { + Debug(1, "Linked monitor %d %d is not connected. Connecting.", i, linked_monitors[i]->Id()); linked_monitors[i]->connect(); } } // end foreach linked_monit @@ -1502,7 +1502,7 @@ bool Monitor::Analyse() { if ( section_length && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) - && ( (event_close_mode != CLOSE_TIME) || ! ( timestamp->tv_sec % section_length ) ) + && ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) ) ) { Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d", name, image_count, event->Id(), @@ -1515,7 +1515,6 @@ bool Monitor::Analyse() { } // end if event if ( !event ) { - // Create event event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording); shared_data->last_event = event->Id(); @@ -1529,7 +1528,6 @@ bool Monitor::Analyse() { if ( state == IDLE ) { shared_data->state = state = TAPE; } - } // end if ! event } // end if function == RECORD || function == MOCORD) } // end if !signal_change && signal @@ -1542,7 +1540,7 @@ bool Monitor::Analyse() { && (event_close_mode == CLOSE_ALARM) && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length ) ) { - Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", + Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", name, image_count, event->Id()); closeEvent(); } else if ( event ) { @@ -1553,7 +1551,6 @@ bool Monitor::Analyse() { ); } if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) { - shared_data->state = state = ALARM; // lets construct alarm cause. It will contain cause + names of zones alarmed std::string alarm_cause = ""; for ( int i=0; i < n_zones; i++ ) { @@ -1638,6 +1635,7 @@ bool Monitor::Analyse() { event->SavePreAlarmFrames(); } } + shared_data->state = state = ALARM; } else if ( state != PREALARM ) { Info("%s: %03d - Gone into prealarm state", name, image_count); shared_data->state = state = PREALARM; @@ -1697,17 +1695,10 @@ bool Monitor::Analyse() { } // end if zone is alarmed } // end foreach zone - if ( got_anal_image ) { - if ( state == PREALARM ) - Event::AddPreAlarmFrame(snap_image, *timestamp, score, &alarm_image); - else - event->AddFrame(snap_image, *timestamp, score, &alarm_image); - } else { - if ( state == PREALARM ) - Event::AddPreAlarmFrame(snap_image, *timestamp, score); - else - event->AddFrame(snap_image, *timestamp, score); - } + if ( state == PREALARM ) + Event::AddPreAlarmFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL)); + else + event->AddFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL)); } else { // Not doing alarm frame storage if ( state == PREALARM ) { @@ -1743,7 +1734,6 @@ bool Monitor::Analyse() { //set up video store data snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); video_store_data->recording = event->StartTime(); - } } // end if event @@ -2498,7 +2488,7 @@ int Monitor::Capture() { if ( capture_image->Size() > camera->ImageSize() ) { Error("Captured image %d does not match expected size %d check width, height and colour depth", - capture_image->Size(),camera->ImageSize() ); + capture_image->Size(), camera->ImageSize() ); return -1; } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 9109c9c72..5c6a60c91 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -220,7 +220,7 @@ protected: } inline bool isConnected() const { - return( connected ); + return connected && shared_data->valid; } inline time_t getLastConnectTime() const { return( last_connect_time ); @@ -463,7 +463,8 @@ public: int GetOptSaveJPEGs() const { return savejpegs; } VideoWriter GetOptVideoWriter() const { return videowriter; } - const std::vector* GetOptEncoderParams() const { return &encoderparamsvec; } + const std::vector* GetOptEncoderParamsVec() const { return &encoderparamsvec; } + const std::string GetOptEncoderParams() const { return encoderparams; } uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; } void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; } struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; } diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 49d617f7d..8a3b5511e 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -329,8 +329,11 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { gettimeofday(&frameStartTime, NULL); fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout); - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); - if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { + if ( + (0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size)) + || + (fwrite(img_buffer, img_buffer_size, 1, stdout) != 1) + ) { if ( !zm_terminate ) Warning("Unable to send stream frame: %s", strerror(errno)); return false; @@ -385,7 +388,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { gettimeofday(&frameStartTime, NULL); fputs("--ZoneMinderFrame\r\n", stdout); - switch( type ) { + switch ( type ) { case STREAM_JPEG : send_image->EncodeJpeg(img_buffer, &img_buffer_size); fputs("Content-Type: image/jpeg\r\n", stdout); @@ -410,8 +413,11 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { Error("Unexpected frame type %d", type); return false; } - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); - if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { + if ( + (0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size)) + || + (fwrite(img_buffer, img_buffer_size, 1, stdout) != 1) + ) { if ( !zm_terminate ) { // If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate Warning("Unable to send stream frame: %s", strerror(errno)); diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index f725d17b9..57a677a7b 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -618,8 +618,8 @@ int RemoteCameraHttp::GetResponse() { static char *content_type_header; static char *boundary_header; static char *authenticate_header; - static char subcontent_length_header[32]; - static char subcontent_type_header[64]; + static char subcontent_length_header[33]; + static char subcontent_type_header[65]; static char http_version[16]; static char status_code[16]; @@ -892,25 +892,37 @@ int RemoteCameraHttp::GetResponse() { } } - Debug( 6, "%d: %s", subheader_len, subheader_ptr ); + Debug(6, "%d: %s", subheader_len, subheader_ptr); - if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) { + if ( (crlf = mempbrk(subheader_ptr, "\r\n", subheader_len)) ) { //subheaders[n_subheaders++] = subheader_ptr; n_subheaders++; - if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) { + if ( !boundary_header && (strncasecmp(subheader_ptr, content_boundary, content_boundary_len) == 0) ) { boundary_header = subheader_ptr; - Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); - } else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) { - strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); - *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; - Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); + Debug(4, "Got boundary subheader '%s'", subheader_ptr); + } else if ( + !subcontent_length_header[0] + && + (strncasecmp(subheader_ptr, content_length_match, content_length_match_len) == 0) + ) { + strncpy( + subcontent_length_header, + subheader_ptr+content_length_match_len, + sizeof(subcontent_length_header)-1 + ); + *(subcontent_length_header+strcspn(subcontent_length_header, "\r\n")) = '\0'; + Debug(4, "Got content length subheader '%s'", subcontent_length_header); } else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) { - strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); - *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; - Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); + strncpy( + subcontent_type_header, + subheader_ptr+content_type_match_len, + sizeof(subcontent_type_header)-1 + ); + *(subcontent_type_header+strcspn(subcontent_type_header, "\r\n")) = '\0'; + Debug(4, "Got content type subheader '%s'", subcontent_type_header); } else { - Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); + Debug(6, "Got ignored subheader '%s' found", subheader_ptr); } subheader_ptr = crlf; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index ad1bfd001..323d3c3b4 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -35,7 +35,7 @@ #include #endif -unsigned int sseversion = 0; +unsigned int sse_version = 0; unsigned int neonversion = 0; std::string trimSet(std::string str, std::string trimset) { @@ -44,12 +44,9 @@ std::string trimSet(std::string str, std::string trimset) { size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af // if all spaces or empty return an empty string - if(( std::string::npos == startpos ) || ( std::string::npos == endpos)) - { + if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) ) return std::string(""); - } - else - return str.substr( startpos, endpos-startpos+1 ); + return str.substr(startpos, endpos-startpos+1); } std::string trimSpaces(const std::string &str) { @@ -57,48 +54,46 @@ std::string trimSpaces(const std::string &str) { } std::string replaceAll(std::string str, std::string from, std::string to) { - if(from.empty()) + if ( from.empty() ) return str; size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { + while ( (start_pos = str.find(from, start_pos)) != std::string::npos ) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' } return str; } -const std::string stringtf( const char *format, ... ) -{ +const std::string stringtf( const char *format, ... ) { va_list ap; char tempBuffer[8192]; std::string tempString; - va_start(ap, format ); - vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap ); + va_start(ap, format); + vsnprintf(tempBuffer, sizeof(tempBuffer), format , ap); va_end(ap); tempString = tempBuffer; - return( tempString ); + return tempString; } -const std::string stringtf( const std::string format, ... ) -{ +const std::string stringtf(const std::string format, ...) { va_list ap; char tempBuffer[8192]; std::string tempString; - va_start(ap, format ); - vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap ); + va_start(ap, format); + vsnprintf(tempBuffer, sizeof(tempBuffer), format.c_str(), ap); va_end(ap); tempString = tempBuffer; - return( tempString ); + return tempString; } bool startsWith(const std::string &haystack, const std::string &needle) { - return( haystack.substr(0, needle.length()) == needle ); + return ( haystack.substr(0, needle.length()) == needle ); } StringVector split(const std::string &string, const std::string &chars, int limit) { @@ -145,8 +140,7 @@ const std::string join(const StringVector &v, const char * delim=",") { const std::string base64Encode(const std::string &inString) { static char base64_table[64] = { '\0' }; - if ( !base64_table[0] ) - { + if ( !base64_table[0] ) { int i = 0; for ( char c = 'A'; c <= 'Z'; c++ ) base64_table[i++] = c; @@ -159,59 +153,52 @@ const std::string base64Encode(const std::string &inString) { } std::string outString; - outString.reserve( 2 * inString.size() ); + outString.reserve(2 * inString.size()); const char *inPtr = inString.c_str(); - while( *inPtr ) - { + while ( *inPtr ) { unsigned char selection = *inPtr >> 2; unsigned char remainder = (*inPtr++ & 0x03) << 4; outString += base64_table[selection]; - if ( *inPtr ) - { + if ( *inPtr ) { selection = remainder | (*inPtr >> 4); remainder = (*inPtr++ & 0x0f) << 2; outString += base64_table[selection]; - if ( *inPtr ) - { + if ( *inPtr ) { selection = remainder | (*inPtr >> 6); outString += base64_table[selection]; selection = (*inPtr++ & 0x3f); outString += base64_table[selection]; - } - else - { + } else { outString += base64_table[remainder]; outString += '='; } - } - else - { + } else { outString += base64_table[remainder]; outString += '='; outString += '='; } } - return( outString ); + return outString; } int split(const char* string, const char delim, std::vector& items) { - if(string == NULL) + if ( string == NULL ) return -1; - if(string[0] == 0) + if ( string[0] == 0 ) return -2; std::string str(string); - while(true) { + while ( true ) { size_t pos = str.find(delim); items.push_back(str.substr(0, pos)); str.erase(0, pos+1); - if(pos == std::string::npos) + if ( pos == std::string::npos ) break; } @@ -219,16 +206,16 @@ int split(const char* string, const char delim, std::vector& items) } int pairsplit(const char* string, const char delim, std::string& name, std::string& value) { - if(string == NULL) + if ( string == NULL ) return -1; - if(string[0] == 0) + if ( string[0] == 0 ) return -2; std::string str(string); size_t pos = str.find(delim); - if(pos == std::string::npos || pos == 0 || pos >= str.length()) + if ( pos == std::string::npos || pos == 0 || pos >= str.length() ) return -3; name = str.substr(0, pos); @@ -240,7 +227,7 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri /* Detect special hardware features, such as SIMD instruction sets */ void hwcaps_detect() { neonversion = 0; - sseversion = 0; + sse_version = 0; #if (defined(__i386__) || defined(__x86_64__)) /* x86 or x86-64 processor */ uint32_t r_edx, r_ecx, r_ebx; @@ -277,33 +264,33 @@ void hwcaps_detect() { ); #endif - if (r_ebx & 0x00000020) { - sseversion = 52; /* AVX2 */ - Debug(1,"Detected a x86\\x86-64 processor with AVX2"); - } else if (r_ecx & 0x10000000) { - sseversion = 51; /* AVX */ - Debug(1,"Detected a x86\\x86-64 processor with AVX"); - } else if (r_ecx & 0x00100000) { - sseversion = 42; /* SSE4.2 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE4.2"); - } else if (r_ecx & 0x00080000) { - sseversion = 41; /* SSE4.1 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE4.1"); - } else if (r_ecx & 0x00000200) { - sseversion = 35; /* SSSE3 */ + if ( r_ebx & 0x00000020 ) { + sse_version = 52; /* AVX2 */ + Debug(1, "Detected a x86\\x86-64 processor with AVX2"); + } else if ( r_ecx & 0x10000000 ) { + sse_version = 51; /* AVX */ + Debug(1, "Detected a x86\\x86-64 processor with AVX"); + } else if ( r_ecx & 0x00100000 ) { + sse_version = 42; /* SSE4.2 */ + Debug(1, "Detected a x86\\x86-64 processor with SSE4.2"); + } else if ( r_ecx & 0x00080000 ) { + sse_version = 41; /* SSE4.1 */ + Debug(1, "Detected a x86\\x86-64 processor with SSE4.1"); + } else if ( r_ecx & 0x00000200 ) { + sse_version = 35; /* SSSE3 */ Debug(1,"Detected a x86\\x86-64 processor with SSSE3"); - } else if (r_ecx & 0x00000001) { - sseversion = 30; /* SSE3 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE3"); - } else if (r_edx & 0x04000000) { - sseversion = 20; /* SSE2 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE2"); - } else if (r_edx & 0x02000000) { - sseversion = 10; /* SSE */ - Debug(1,"Detected a x86\\x86-64 processor with SSE"); + } else if ( r_ecx & 0x00000001 ) { + sse_version = 30; /* SSE3 */ + Debug(1, "Detected a x86\\x86-64 processor with SSE3"); + } else if ( r_edx & 0x04000000 ) { + sse_version = 20; /* SSE2 */ + Debug(1, "Detected a x86\\x86-64 processor with SSE2"); + } else if ( r_edx & 0x02000000 ) { + sse_version = 10; /* SSE */ + Debug(1, "Detected a x86\\x86-64 processor with SSE"); } else { - sseversion = 0; - Debug(1,"Detected a x86\\x86-64 processor"); + sse_version = 0; + Debug(1, "Detected a x86\\x86-64 processor"); } #elif defined(__arm__) // ARM processor in 32bit mode diff --git a/src/zm_utils.h b/src/zm_utils.h index 9a8dd1948..9c8cc01a0 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -59,7 +59,7 @@ void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes); void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff); void hwcaps_detect(); -extern unsigned int sseversion; +extern unsigned int sse_version; extern unsigned int neonversion; char *timeval_to_string( struct timeval tv ); diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 6acee0cfd..19f1251e2 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -37,11 +37,12 @@ VideoStore::VideoStore( const char *format_in, AVStream *p_video_in_stream, AVStream *p_audio_in_stream, - Monitor *monitor + Monitor *p_monitor ) { video_in_stream = p_video_in_stream; audio_in_stream = p_audio_in_stream; + monitor = p_monitor; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) //video_in_ctx = avcodec_alloc_context3(NULL); @@ -213,11 +214,18 @@ VideoStore::VideoStore( } #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) +#if 0 +# This is commented out because we are only doing passthrough right now /* I'm not entirely sure that this is a good idea. We may have to do it someday but really only when transcoding * * think what I was trying to achieve here was to have zm_dump_codecpar output nice info * */ -#if 0 + AVDictionary *opts = 0; + ret = av_dict_parse_string(&opts, monitor->GetOptEncoderParams().c_str(), "=", ",", 0); + if ( ret < 0 ) { + Warning("Could not parse ffmpeg encoder options '%s'", monitor->GetOptEncoderParams().c_str()); + } + if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { Warning("Can't open video codec (%s) %s", video_out_codec->name, @@ -404,21 +412,39 @@ bool VideoStore::open() { } zm_dump_stream_format(oc, 0, 0, 1); - if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1); + if ( audio_out_stream ) zm_dump_stream_format(oc, 1, 0, 1); AVDictionary *opts = NULL; - // av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); - // Shiboleth reports that this may break seeking in mp4 before it downloads - av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); - // av_dict_set(&opts, "movflags", - // "frag_keyframe+empty_moov+default_base_moof", 0); + + std::string option_string = monitor->GetOptEncoderParams(); + ret = av_dict_parse_string(&opts, option_string.c_str(), "=", ",\n", 0); + if ( ret < 0 ) { + Warning("Could not parse ffmpeg output options '%s'", option_string.c_str()); + } + + const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", NULL, AV_DICT_MATCH_CASE); + if ( !movflags_entry ) { + Debug(1, "setting movflags to frag_keyframe+empty_moov"); + // av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); + // Shiboleth reports that this may break seeking in mp4 before it downloads + av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); + // av_dict_set(&opts, "movflags", + // "frag_keyframe+empty_moov+default_base_moof", 0); + } else { + Debug(1, "using movflags %s", movflags_entry->value); + } if ( (ret = avformat_write_header(oc, &opts)) < 0 ) { - // if ((ret = avformat_write_header(oc, &opts)) < 0) { - Warning("Unable to set movflags to frag_custom+dash+delay_moov"); - /* Write the stream header, if any. */ + Warning("Unable to set movflags trying with defaults."); ret = avformat_write_header(oc, NULL); - } else if (av_dict_count(opts) != 0) { - Warning("some options not set"); + } else if ( av_dict_count(opts) != 0 ) { + Info("some options not used, turn on debugging for a list."); + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Debug(1, "Encoder Option %s=>%s", e->key, e->value); + if ( !e->value ) { + av_dict_set(&opts, e->key, NULL, 0); + } + } } if ( opts ) av_dict_free(&opts); if ( ret < 0 ) { diff --git a/src/zm_videostore.h b/src/zm_videostore.h index 7c465ea4b..64c2296ba 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -19,7 +19,6 @@ extern "C" { class VideoStore { private: - AVOutputFormat *out_format; AVFormatContext *oc; @@ -30,6 +29,7 @@ private: AVStream *video_in_stream; AVStream *audio_in_stream; + Monitor *monitor; // Move this into the object so that we aren't constantly allocating/deallocating it on the stack AVPacket opkt; diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 1e0a16cda..ea6c170ac 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -227,7 +227,7 @@ bool Zone::CheckAlarms(const Image *delta_image) { Debug(4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y); - /* if(config.cpu_extensions && sseversion >= 20) { + /* if(config.cpu_extensions && sse_version >= 20) { sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); } else { std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); diff --git a/src/zmc.cpp b/src/zmc.cpp index 511e019ab..22132e668 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -259,7 +259,8 @@ int main(int argc, char *argv[]) { Debug(1, "Failed to prime capture of initial monitor"); } prime_capture_log_count ++; - sleep(10); + if ( !zm_terminate ) + sleep(10); continue; } diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index f6a5c6bec..c907fa64d 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -80,7 +80,7 @@ fi; if [ "$DISTROS" == "" ]; then if [ "$RELEASE" != "" ]; then - DISTROS="xenial,bionic,disco,eoan,focal,trusty" + DISTROS="xenial,bionic,focal,trusty" else DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`; fi; @@ -220,14 +220,19 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do fi; # Generate Changlog - if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then + if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ] + then cp -Rpd distros/ubuntu1204 debian - else - if [ "$DISTRO" == "wheezy" ]; then - cp -Rpd distros/debian debian - else - cp -Rpd distros/ubuntu1604 debian - fi; + + elif [ "$DISTRO" == "wheezy" ] + then + cp -Rpd distros/debian debian + + elif [ "$DISTRO" == "beowulf" ] + then + cp -Rpd distros/beowulf debian + else + cp -Rpd distros/ubuntu1604 debian fi; if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then diff --git a/utils/packpack/rsync_xfer.sh b/utils/packpack/rsync_xfer.sh index c9a737a03..5078194a4 100755 --- a/utils/packpack/rsync_xfer.sh +++ b/utils/packpack/rsync_xfer.sh @@ -37,7 +37,7 @@ echo "Target subfolder set to $targetfolder" echo echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)" -rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 +rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -v' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1 if [ "$?" -eq 0 ]; then echo echo "Files copied successfully." diff --git a/version b/version index 46a99f18d..f883b434e 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.14 +1.34.20 diff --git a/web/ajax/status.php b/web/ajax/status.php index fea5943ca..e34f6fe6f 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -19,7 +19,7 @@ $statusData = array( 'limit' => 1, 'elements' => array( 'MonitorCount' => array( 'sql' => 'count(*)' ), - 'ActiveMonitorCount' => array( 'sql' => 'count(if(Function != \'None\',1,NULL))' ), + 'ActiveMonitorCount' => array( 'sql' => 'count(if(`Function` != \'None\',1,NULL))' ), 'State' => array( 'func' => 'daemonCheck()?'.translate('Running').':'.translate('Stopped') ), 'Load' => array( 'func' => 'getLoad()' ), 'Disk' => array( 'func' => 'getDiskPercent()' ), @@ -211,17 +211,17 @@ function collectData() { $values = array(); $elements = &$entitySpec['elements']; - $lc_elements = array_change_key_case( $elements ); + $lc_elements = array_change_key_case($elements); $id = false; if ( isset($_REQUEST['id']) ) if ( !is_array($_REQUEST['id']) ) $id = array( validJsStr($_REQUEST['id']) ); else - $id = array_values( $_REQUEST['id'] ); + $id = array_values($_REQUEST['id']); if ( !isset($_REQUEST['element']) ) - $_REQUEST['element'] = array_keys( $elements ); + $_REQUEST['element'] = array_keys($elements); else if ( !is_array($_REQUEST['element']) ) $_REQUEST['element'] = array( validJsStr($_REQUEST['element']) ); @@ -235,18 +235,18 @@ function collectData() { foreach ( $_REQUEST['element'] as $element ) { if ( !($elementData = $lc_elements[strtolower($element)]) ) - ajaxError( 'Bad '.validJsStr($_REQUEST['entity']).' element '.$element ); + ajaxError('Bad '.validJsStr($_REQUEST['entity']).' element '.$element); if ( isset($elementData['func']) ) - $data[$element] = eval( 'return( '.$elementData['func'].' );' ); + $data[$element] = eval('return( '.$elementData['func'].' );'); else if ( isset($elementData['postFunc']) ) $postFuncs[$element] = $elementData['postFunc']; else if ( isset($elementData['zmu']) ) - $data[$element] = exec( escapeshellcmd( getZmuCommand( ' '.$elementData['zmu'] ) ) ); + $data[$element] = exec(escapeshellcmd(getZmuCommand(' '.$elementData['zmu']))); else { if ( isset($elementData['sql']) ) $fieldSql[] = $elementData['sql'].' as '.$element; else - $fieldSql[] = $element; + $fieldSql[] = '`'.$element.'`'; if ( isset($elementData['table']) && isset($elementData['join']) ) { $joinSql[] = 'left join '.$elementData['table'].' on '.$elementData['join']; } @@ -285,7 +285,7 @@ function collectData() { preg_match('/^(\w+)\s*(ASC|DESC)?( NULLS FIRST)?$/i', $sort_field, $matches); if ( count($matches) ) { - if ( in_array($matches[1], $fieldSql) ) { + if ( in_array($matches[1], $fieldSql) or in_array('`'.$matches[1].'`', $fieldSql) ) { $sql .= $matches[1]; } else { ZM\Error('Sort field ' . $matches[1] . ' not in SQL Fields'); @@ -296,7 +296,7 @@ function collectData() { $sql .= ' '.strtoupper($matches[3]); } } else { - ZM\Error("Sort field didn't match regexp $sort_field"); + ZM\Error('Sort field didn\'t match regexp '.$sort_field); } } # end foreach sort field } # end if has sort @@ -310,7 +310,7 @@ function collectData() { if ( !empty( $limit ) ) $sql .= ' limit '.$limit_offset.$limit; if ( isset($limit) && $limit == 1 ) { - if ( $sqlData = dbFetchOne( $sql, NULL, $values ) ) { + if ( $sqlData = dbFetchOne($sql, NULL, $values) ) { foreach ( $postFuncs as $element=>$func ) $sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' ); $data = array_merge( $data, $sqlData ); diff --git a/web/api/app/Config/bootstrap.php.in b/web/api/app/Config/bootstrap.php.in index 881c08eb5..894b3ec00 100644 --- a/web/api/app/Config/bootstrap.php.in +++ b/web/api/app/Config/bootstrap.php.in @@ -102,21 +102,21 @@ CakeLog::config('debug', array( 'engine' => 'File', 'types' => array('notice', 'info', 'debug'), 'file' => 'cake_debug', - 'path' => '@ZM_LOGDIR@/' + 'path' => '@ZM_LOGDIR@/' )); CakeLog::config('error', array( 'engine' => 'File', 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), 'file' => 'cake_error', - 'path' => '@ZM_LOGDIR@/' + 'path' => '@ZM_LOGDIR@/' )); CakeLog::config('custom_path', array( - 'engine' => 'File', - 'path' => '@ZM_LOGDIR@/' + 'engine' => 'File', + 'path' => '@ZM_LOGDIR@/' )); -Configure::write('ZM_CONFIG', '@ZM_CONFIG@'); -Configure::write('ZM_CONFIG_SUBDIR', '@ZM_CONFIG_SUBDIR@'); +Configure::write('ZM_CONFIG', '@ZM_CONFIG@'); +Configure::write('ZM_CONFIG_SUBDIR', '@ZM_CONFIG_SUBDIR@'); Configure::write('ZM_VERSION', '@VERSION@'); Configure::write('ZM_API_VERSION', '@API_VERSION@'); @@ -128,27 +128,3 @@ global $configvals; foreach( $configvals as $key => $value) { Configure::write($key, $value); } -if ( 0 ) { - // No longer needed, but I want to keep the code for reference - // -// For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID -if ( ! defined('ZM_SERVER_ID') ) { - App::uses('ClassRegistry', 'Utility'); - $ServerModel = ClassRegistry::init('Server'); - if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) { - $Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_NAME) ) ); - if ( ! $Server ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.'); - } else { - define( 'ZM_SERVER_ID', $Server['Server']['Id'] ); - } - } else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) { - $Server = $ServerModel->find( 'first', array( 'conditions'=>array('Name'=>ZM_SERVER_HOST) ) ); - if ( ! $Server ) { - Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.'); - } else { - define( 'ZM_SERVER_ID', $Server['Server']['Id'] ); - } - } -} -} diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 5188c18b4..3b132e932 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -434,14 +434,11 @@ class EventsController extends AppController { // Find the max Frame for this Event. Error out otherwise. $this->loadModel('Frame'); - if (! $frame = $this->Frame->find('first', array( + $frame = $this->Frame->find('first', array( 'conditions' => array( - 'EventId' => $event['Event']['Id'], - 'Score' => $event['Event']['MaxScore'] - ) - ))) { - throw new NotFoundException(__('Can not find Frame for Event ' . $event['Event']['Id'])); - } - return $frame['Frame']['Id']; + 'EventId' => $event['Event']['Id'], + 'Score' => $event['Event']['MaxScore'] + ))); + return empty($frame)?null:$frame['Frame']['Id']; } } // end class EventsController diff --git a/web/api/app/Controller/UsersController.php b/web/api/app/Controller/UsersController.php index 6b691cee1..502a929ff 100644 --- a/web/api/app/Controller/UsersController.php +++ b/web/api/app/Controller/UsersController.php @@ -14,6 +14,18 @@ class UsersController extends AppController { */ public $components = array('RequestHandler', 'Paginator'); + public function beforeFilter() { + parent::beforeFilter(); + + global $user; + # We already tested for auth in appController, so we just need to test for specific permission + $canView = (!$user) || ($user['System'] != 'None'); + if ( !$canView ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + } + /** * index method * @@ -23,6 +35,12 @@ class UsersController extends AppController { public function index() { $this->User->recursive = 0; + global $user; + # We should actually be able to list our own user, but I'm not bothering at this time. + if ( $user['System'] == 'None' ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } $users = $this->Paginator->paginate('User'); $this->set(compact('users')); @@ -36,16 +54,20 @@ class UsersController extends AppController { * @return void */ public function view($id = null) { - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) - )); - $this->User->recursive = 1; - if (!$this->User->exists($id)) { + + global $user; + # We can view ourselves + $canView = ($user['System'] != 'None') or ($user['Id'] == $id); + if ( !$canView ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + + if ( !$this->User->exists($id) ) { throw new NotFoundException(__('Invalid user')); } + $options = array('conditions' => array('User.' . $this->User->primaryKey => $id)); $user = $this->User->find('first', $options); @@ -61,9 +83,16 @@ class UsersController extends AppController { * @return void */ public function add() { - if ($this->request->is('post')) { + if ( $this->request->is('post') ) { + + global $user; + if ( $user['System'] != 'Edit' ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + $this->User->create(); - if ($this->User->save($this->request->data)) { + if ( $this->User->save($this->request->data) ) { return $this->flash(__('The user has been saved.'), array('action' => 'index')); } $this->Session->setFlash( @@ -82,20 +111,29 @@ class UsersController extends AppController { public function edit($id = null) { $this->User->id = $id; - if (!$this->User->exists($id)) { + global $user; + $canEdit = ($user['System'] == 'Edit') or (($user['Id'] == $id) and ZM_USER_SELF_EDIT); + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + + if ( !$this->User->exists($id) ) { throw new NotFoundException(__('Invalid user')); } - if ($this->request->is('post') || $this->request->is('put')) { - if ($this->User->save($this->request->data)) { + if ( $this->request->is('post') || $this->request->is('put') ) { + if ( $this->User->save($this->request->data) ) { $message = 'Saved'; } else { $message = 'Error'; } } else { - $this->request->data = $this->User->read(null, $id); - unset($this->request->data['User']['password']); - } + # What is this doing? Resetting the request data? I understand clearing the password field + # but generally I feel like the request data should be read only + $this->request->data = $this->User->read(null, $id); + unset($this->request->data['User']['Password']); + } $this->set(array( 'message' => $message, @@ -112,11 +150,18 @@ class UsersController extends AppController { */ public function delete($id = null) { $this->User->id = $id; - if (!$this->User->exists()) { + + global $user; + # Can't delete ourselves + if ( ($user['System'] != 'Edit') or ($user['Id'] == $id) ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + if ( !$this->User->exists() ) { throw new NotFoundException(__('Invalid user')); } $this->request->allowMethod('post', 'delete'); - if ($this->User->delete()) { + if ( $this->User->delete() ) { $message = 'The user has been deleted.'; } else { $message = 'The user could not be deleted. Please, try again.'; @@ -126,47 +171,4 @@ class UsersController extends AppController { '_serialize' => array('message') )); } - - public function beforeFilter() { - parent::beforeFilter(); - - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_OPT_USE_AUTH')) - )); - if ( $configs['ZM_OPT_USE_AUTH'] ) { - $this->Auth->allow('add','logout'); - } else { - $this->Auth->allow(); - } - } - - public function login() { - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_OPT_USE_AUTH')) - )); - - if ( ! $configs['ZM_OPT_USE_AUTH'] ) { - $this->set(array( - 'message' => 'Login is not required.', - '_serialize' => array('message') - )); - return; - } - - if ($this->request->is('post')) { - if ($this->Auth->login()) { - return $this->redirect($this->Auth->redirectUrl()); - } - $this->Session->setFlash(__('Invalid username or password, try again')); - } - } - - public function logout() { - return $this->redirect($this->Auth->logout()); - } - -} +} # end class UsersController diff --git a/web/api/app/Controller/ZonesController.php b/web/api/app/Controller/ZonesController.php index 5b8c3f1a3..f01931f65 100644 --- a/web/api/app/Controller/ZonesController.php +++ b/web/api/app/Controller/ZonesController.php @@ -40,6 +40,7 @@ class ZonesController extends AppController { '_serialize' => array('zones') )); } + public function index() { $this->Zone->recursive = -1; @@ -63,23 +64,43 @@ class ZonesController extends AppController { * @return void */ public function add() { - if ( $this->request->is('post') ) { - global $user; - $canEdit = (!$user) || $user['Monitors'] == 'Edit'; - if ( !$canEdit ) { - throw new UnauthorizedException(__('Insufficient Privileges')); - return; - } + if ( !$this->request->is('post') ) { + throw new BadRequestException(__('Invalid method. Should be post')); + return; + } - $this->Zone->create(); - if ( $this->Zone->save($this->request->data) ) { - return $this->flash(__('The zone has been saved.'), array('action' => 'index')); + global $user; + $canEdit = (!$user) || $user['Monitors'] == 'Edit'; + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + + $zone = null; + + $this->Zone->create(); + $zone = $this->Zone->save($this->request->data); + if ( $zone ) { + require_once __DIR__ .'/../../../includes/Monitor.php'; + $monitor = new ZM\Monitor($zone['Zone']['MonitorId']); + $monitor->zmaControl('restart'); + $message = 'Saved'; + //$zone = $this->Zone->find('first', array('conditions' => array( array('Zone.' . $this->Zone->primaryKey => $this->Zone), + } else { + $message = 'Error: '; + // if there is a validation message, use it + if ( !$this->Zone->validates() ) { + $message = $this->Zone->validationErrors; } } - $monitors = $this->Zone->Monitor->find('list'); - $this->set(compact('monitors')); - } + + $this->set(array( + 'message' => $message, + 'zone' => $zone, + '_serialize' => array('message','zone') + )); + } // end function add() /** * edit method diff --git a/web/api/app/Model/User.php b/web/api/app/Model/User.php index 9137ebfc5..1548aaee3 100644 --- a/web/api/app/Model/User.php +++ b/web/api/app/Model/User.php @@ -3,8 +3,6 @@ App::uses('AppModel', 'Model'); /** * User Model * - * @property Monitor $Monitor - * @property Frame $Frame */ class User extends AppModel { @@ -23,6 +21,20 @@ class User extends AppModel { ) ); + function beforeFind($query) { + if ( empty($query['fields']) ) { + $schema = $this->schema(); + unset($schema['Password']); + + foreach (array_keys($schema) as $field) { + $query['fields'][] = $this->alias . '.' . $field; + } + return $query; + } + return parent::beforeFind($query); + } + + /** * Use table * @@ -53,14 +65,6 @@ class User extends AppModel { * @var array */ public $belongsTo = array( - /*'Monitor' => array( - 'className' => 'Monitor', - 'foreignKey' => 'MonitorId', - 'conditions' => '', - 'fields' => '', - 'order' => '' - ) -*/ ); /** @@ -69,21 +73,6 @@ class User extends AppModel { * @var array */ public $hasMany = array( -/* - 'Frame' => array( - 'className' => 'Frame', - 'foreignKey' => 'UserId', - 'dependent' => true, - 'conditions' => '', - 'fields' => '', - 'order' => '', - 'limit' => '', - 'offset' => '', - 'exclusive' => '', - 'finderQuery' => '', - 'counterQuery' => '' - ) -*/ ); } diff --git a/web/api/app/Model/Zone.php b/web/api/app/Model/Zone.php index 8cdc8131b..0f5e4d653 100644 --- a/web/api/app/Model/Zone.php +++ b/web/api/app/Model/Zone.php @@ -30,6 +30,32 @@ class Zone extends AppModel { public $recursive = -1; + /** + * Validation rules + * + * @var array + */ + public $validate = array( + 'MonitorId' => array( + 'rule' => 'checkMonitorId', + //array('naturalNumber'), + 'message' => 'Zones must have a valid MonitorId', + 'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + 'Name' => array( + 'required' => array( + //'on' => 'create', + 'rule' => 'notBlank', + 'message' => 'Zone Name must be specified for creation', + 'required' => true, + ), + ) + + ); + //The Associations below have been created with all possible keys, those that are not needed can be removed /** @@ -41,9 +67,17 @@ class Zone extends AppModel { 'Monitor' => array( 'className' => 'Monitor', 'foreignKey' => 'MonitorId', - 'conditions' => '', - 'fields' => '', - 'order' => '' + //'conditions' => '', + //'fields' => '', + //'order' => '' ) ); + + public function checkMonitorId($data) { + if ( !$this->Monitor->find('first', array('conditions'=>array('Id'=>$data['MonitorId']))) ) { + //$this->invalidate('MonitorId', 'Invalid Monitor Id'); + return false; + } + return true; + } } diff --git a/web/api/app/View/Zones/json/add.ctp b/web/api/app/View/Zones/json/add.ctp new file mode 100644 index 000000000..1675fa9be --- /dev/null +++ b/web/api/app/View/Zones/json/add.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($zone); diff --git a/web/api/app/View/Zones/json/edit.ctp b/web/api/app/View/Zones/json/edit.ctp new file mode 100644 index 000000000..1675fa9be --- /dev/null +++ b/web/api/app/View/Zones/json/edit.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($zone); diff --git a/web/api/app/View/Zones/xml/add.ctp b/web/api/app/View/Zones/xml/add.ctp new file mode 100644 index 000000000..a8fcec73b --- /dev/null +++ b/web/api/app/View/Zones/xml/add.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response'=>$message, 'zone'=>$zone)); +echo $xml->asXML(); diff --git a/web/api/app/View/Zones/xml/edit.ctp b/web/api/app/View/Zones/xml/edit.ctp new file mode 100644 index 000000000..cb6f92f66 --- /dev/null +++ b/web/api/app/View/Zones/xml/edit.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $message, 'zone'=>$zone)); +echo $xml->asXML(); diff --git a/web/includes/Event.php b/web/includes/Event.php index f1ba99626..2f9fa4d20 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -262,13 +262,18 @@ class Event extends ZM_Object { return $streamSrc; } // end function getStreamSrc + # The new='' is to so that if we pass null, we reset the value of DiskSpace. + # '' is not a valid DiskSpace so that tells us that nothing was passed whereas null (unknown) is. function DiskSpace( $new='' ) { if ( is_null($new) or ( $new != '' ) ) { $this->{'DiskSpace'} = $new; } if ( (!property_exists($this, 'DiskSpace')) or (null === $this->{'DiskSpace'}) ) { $this->{'DiskSpace'} = folder_size($this->Path()); - dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'})); + if ( $this->{'EndTime'} ) { + # Finished events shouldn't grow in size much so we can commit it to the db. + dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'})); + } } return $this->{'DiskSpace'}; } @@ -380,54 +385,54 @@ class Event extends ZM_Object { $Event = $this; $eventPath = $Event->Path(); - if ( $frame and ! is_array($frame) ) { + if ( $frame and !is_array($frame) ) { # Must be an Id Logger::Debug("Assuming that $frame is an Id"); - $frame = array( 'FrameId'=>$frame, 'Type'=>'', 'Delta'=>0 ); + $frame = array('FrameId'=>$frame, 'Type'=>'', 'Delta'=>0); } - if ( ( ! $frame ) and file_exists($eventPath.'/snapshot.jpg') ) { + if ( ( !$frame ) and file_exists($eventPath.'/snapshot.jpg') ) { # No frame specified, so look for a snapshot to use $captImage = 'snapshot.jpg'; - Logger::Debug("Frame not specified, using snapshot"); - $frame = array('FrameId'=>'snapshot', 'Type'=>'','Delta'=>0); + Logger::Debug('Frame not specified, using snapshot'); + $frame = array('FrameId'=>'snapshot', 'Type'=>'', 'Delta'=>0); } else { - $captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId'] ); + $captImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId']); if ( ! file_exists( $eventPath.'/'.$captImage ) ) { - $captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId'] ); - if ( ! file_exists( $eventPath.'/'.$captImage ) ) { + $captImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId']); + if ( !file_exists($eventPath.'/'.$captImage) ) { # Generate the frame JPG if ( $Event->DefaultVideo() ) { $videoPath = $eventPath.'/'.$Event->DefaultVideo(); - if ( ! file_exists( $videoPath ) ) { - Error("Event claims to have a video file, but it does not seem to exist at $videoPath" ); + if ( !file_exists($videoPath) ) { + Error('Event claims to have a video file, but it does not seem to exist at '.$videoPath); return ''; } #$command ='ffmpeg -v 0 -i '.$videoPath.' -vf "select=gte(n\\,'.$frame['FrameId'].'),setpts=PTS-STARTPTS" '.$eventPath.'/'.$captImage; $command ='ffmpeg -ss '. $frame['Delta'] .' -i '.$videoPath.' -frames:v 1 '.$eventPath.'/'.$captImage; - Logger::Debug( "Running $command" ); + Logger::Debug('Running '.$command); $output = array(); $retval = 0; - exec( $command, $output, $retval ); + exec($command, $output, $retval); Logger::Debug("Retval: $retval, output: " . implode("\n", $output)); } else { - Error("Can't create frame images from video because there is no video file for event ".$Event->Id().' at ' .$Event->Path() ); + Error('Can\'t create frame images from video because there is no video file for event '.$Event->Id().' at ' .$Event->Path()); } } // end if capture file exists } // end if analyze file exists } // end if frame or snapshot $captPath = $eventPath.'/'.$captImage; - if ( ! file_exists($captPath) ) { - Error("Capture file does not exist at $captPath"); + if ( !file_exists($captPath) ) { + Error('Capture file does not exist at '.$captPath); } $analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']); $analPath = $eventPath.'/'.$analImage; - $alarmFrame = $frame['Type']=='Alarm'; + $alarmFrame = $frame['Type'] == 'Alarm'; $hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath); $isAnalImage = $hasAnalImage && !$captureOnly; @@ -443,8 +448,8 @@ class Event extends ZM_Object { $fraction = sprintf('%.3f', $scale/SCALE_BASE); $scale = (int)round($scale); - $thumbCaptPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $captPath ); - $thumbAnalPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $analPath ); + $thumbCaptPath = preg_replace('/\.jpg$/', "-$scale.jpg", $captPath); + $thumbAnalPath = preg_replace('/\.jpg$/', "-$scale.jpg", $analPath); if ( $isAnalImage ) { $imagePath = $analPath; @@ -457,7 +462,7 @@ class Event extends ZM_Object { $thumbFile = $thumbPath; if ( $overwrite || ! file_exists($thumbFile) || ! filesize($thumbFile) ) { // Get new dimensions - list( $imageWidth, $imageHeight ) = getimagesize($imagePath); + list($imageWidth, $imageHeight) = getimagesize($imagePath); $thumbWidth = $imageWidth * $fraction; $thumbHeight = $imageHeight * $fraction; @@ -484,7 +489,7 @@ class Event extends ZM_Object { ); return $imageData; - } + } # getImageSrc public function link_to($text=null) { if ( !$text ) diff --git a/web/includes/Group.php b/web/includes/Group.php index 3a34e0af4..f5a78f875 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -19,9 +19,9 @@ class Group extends ZM_Object { public function delete() { if ( property_exists($this, 'Id') ) { - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'})); - dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'})); - dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'})); + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($this->{'Id'})); + dbQuery('UPDATE `Groups` SET `ParentId`=NULL WHERE `ParentId`=?', array($this->{'Id'})); + dbQuery('DELETE FROM `Groups` WHERE Id=?', array($this->{'Id'})); if ( isset($_COOKIE['zmGroup']) ) { if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) { unset($_COOKIE['zmGroup']); @@ -47,7 +47,7 @@ class Group extends ZM_Object { public function MonitorIds( ) { if ( ! property_exists($this, 'MonitorIds') ) { - $this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'})); + $this->{'MonitorIds'} = dbFetchAll('SELECT `MonitorId` FROM `Groups_Monitors` WHERE `GroupId`=?', 'MonitorId', array($this->{'Id'})); } return $this->{'MonitorIds'}; } @@ -65,7 +65,7 @@ class Group extends ZM_Object { session_write_close(); } - return htmlSelect( 'GroupId[]', Group::get_dropdown_options(), isset($_SESSION['GroupId'])?$_SESSION['GroupId']:null, array( + return htmlSelect('GroupId[]', Group::get_dropdown_options(), isset($_SESSION['GroupId'])?$_SESSION['GroupId']:null, array( 'data-on-change' => 'submitThisForm', 'class'=>'chosen', 'multiple'=>'multiple', diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 066625c20..12da03c8a 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -201,6 +201,11 @@ class Monitor extends ZM_Object { if ( ZM_RAND_STREAM ) { $args['rand'] = time(); } + foreach ( array('scale') as $int_arg ) { + if ( isset($args[$int_arg]) and (!is_numeric($args[$int_arg]) or !$args[$int_arg] ) ) { + unset($args[$int_arg]); + } + } $streamSrc .= '?'.http_build_query($args, '', $querySep); @@ -293,12 +298,12 @@ class Monitor extends ZM_Object { return; } } - Logger::Debug("sending command to $url"); + Logger::Debug('sending command to '.$url); $context = stream_context_create(); try { $result = file_get_contents($url, false, $context); - if ($result === FALSE) { /* Handle error */ + if ( $result === FALSE ) { /* Handle error */ Error("Error restarting zmc using $url"); } } catch ( Exception $e ) { @@ -310,14 +315,15 @@ class Monitor extends ZM_Object { } // end function zmcControl function zmaControl($mode=false) { - if ( ! $this->{'Id'} ) { + if ( !$this->{'Id'} ) { Warning('Attempt to control a monitor with no Id'); return; } if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) { if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) { - if ( ZM_OPT_CONTROL ) { + if ( ZM_OPT_CONTROL && $this->Controllable() && $this->TrackMotion() && + ( $this->{'Function'} == 'Modect' || $this->{'Function'} == 'Mocord' ) ) { daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'}); } daemonControl('stop', 'zma', '-m '.$this->{'Id'}); @@ -340,7 +346,7 @@ class Monitor extends ZM_Object { } else if ( $this->ServerId() ) { $Server = $this->Server(); - $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zma.json'; + $url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zma.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS); @@ -354,10 +360,10 @@ class Monitor extends ZM_Object { } Logger::Debug("sending command to $url"); - $context = stream_context_create(); + $context = stream_context_create(); try { $result = file_get_contents($url, false, $context); - if ($result === FALSE) { /* Handle error */ + if ( $result === FALSE ) { /* Handle error */ Error("Error restarting zma using $url"); } } catch ( Exception $e ) { @@ -490,12 +496,11 @@ class Monitor extends ZM_Object { foreach ( explode(' ', $command) as $option ) { if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) { $options[$matches[1]] = $matches[2]?$matches[2]:1; - } else if ( $option != '' and $option != 'quit' ) { + } else if ( $option != '' and $option != 'quit' and $option != 'start' and $option != 'stop' ) { Warning("Ignored command for zmcontrol $option in $command"); } } if ( !count($options) ) { - if ( $command == 'quit' or $command == 'start' or $command == 'stop' ) { # These are special as we now run zmcontrol as a daemon through zmdc. $status = daemonStatus('zmcontrol.pl', array('--id', $this->{'Id'})); @@ -539,7 +544,7 @@ class Monitor extends ZM_Object { } else if ( $this->ServerId() ) { $Server = $this->Server(); - $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json'; + $url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS); diff --git a/web/includes/Object.php b/web/includes/Object.php index 09be86a9e..1fd704ec9 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -306,7 +306,7 @@ class ZM_Object { $fields = array_keys($fields); if ( $this->Id() ) { - $sql = 'UPDATE '.$table.' SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; + $sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; $values = array_map(function($field){ return $this->{$field};}, $fields); $values[] = $this->{'Id'}; if ( dbQuery($sql, $values) ) @@ -314,8 +314,8 @@ class ZM_Object { } else { unset($fields['Id']); - $sql = 'INSERT INTO '.$table. - ' ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)). + $sql = 'INSERT INTO `'.$table. + '` ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)). ') VALUES ('. implode(', ', array_map(function($field){return '?';}, $fields)).')'; @@ -331,7 +331,7 @@ class ZM_Object { public function delete() { $class = get_class($this); $table = $class::$table; - dbQuery("DELETE FROM $table WHERE Id=?", array($this->{'Id'})); + dbQuery("DELETE FROM `$table` WHERE Id=?", array($this->{'Id'})); if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) ) unset($object_cache[$class][$this->{'Id'}]); } diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php index ac2435302..a2e272025 100644 --- a/web/includes/actions/filter.php +++ b/web/includes/actions/filter.php @@ -85,8 +85,6 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { // We update the request id so that the newly saved filter is auto-selected $_REQUEST['Id'] = $filter->Id(); } - if ( $filter->Background() ) - $filter->control('start'); if ( $action == 'execute' ) { $filter->execute(); @@ -94,6 +92,8 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { $filter->delete(); $view = 'events'; + } else if ( $filter->Background() ) { + $filter->control('start'); } } else if ( $action == 'control' ) { diff --git a/web/includes/actions/user.php b/web/includes/actions/user.php index fde1c0d0f..acbcdab7e 100644 --- a/web/includes/actions/user.php +++ b/web/includes/actions/user.php @@ -18,27 +18,36 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( $action == 'user' ) { +if ( $action == 'Save' ) { if ( canEdit('System') ) { - if ( !empty($_REQUEST['uid']) ) + if ( !empty($_REQUEST['uid']) ) { $dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid'])); - else + } else { $dbUser = array(); + } $types = array(); + if ( isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']) ) + $_REQUEST['newUser']['MonitorIds'] = implode(',', $_REQUEST['newUser']['MonitorIds']); + if ( !$_REQUEST['newUser']['Password'] ) + unset($_REQUEST['newUser']['Password']); + $changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types); - if ( function_exists('password_hash') ) { - $pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"'; - } else { - $pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') '; - ZM\Info('Cannot use bcrypt as you are using PHP < 5.3'); - } - - if ( $_REQUEST['newUser']['Password'] ) { - $changes['Password'] = 'Password = '.$pass_hash; - } else { - unset($changes['Password']); + + if ( isset($_REQUEST['newUser']['Password']) ) { + if ( function_exists('password_hash') ) { + $pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"'; + } else { + $pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') '; + ZM\Info('Cannot use bcrypt as you are using PHP < 5.3'); + } + + if ( $_REQUEST['newUser']['Password'] ) { + $changes['Password'] = 'Password = '.$pass_hash; + } else { + unset($changes['Password']); + } } if ( count($changes) ) { diff --git a/web/includes/auth.php b/web/includes/auth.php index c3a34405d..e49f5876b 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -260,6 +260,22 @@ function userFromSession() { return $user; } +function get_auth_relay() { + if ( ZM_OPT_USE_AUTH ) { + if ( ZM_AUTH_RELAY == 'hashed' ) { + return 'auth='.generateAuthHash(ZM_AUTH_HASH_IPS); + } else if ( ZM_AUTH_RELAY == 'plain' ) { + // password probably needs to be escaped + return 'username='.$_SESSION['username'].'&password='.urlencode($_SESSION['password']); + } else if ( ZM_AUTH_RELAY == 'none' ) { + return 'username='.$_SESSION['username']; + } else { + ZM\Error('Unknown value for ZM_AUTH_RELAY ' . ZM_AUTH_RELAY); + } + } + return ''; +} // end function get_auth_relay + if ( ZM_OPT_USE_AUTH ) { if ( !empty($_REQUEST['token']) ) { // we only need to get the username here diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php index d96165eb9..edb04002a 100644 --- a/web/includes/csrf/csrf-magic.php +++ b/web/includes/csrf/csrf-magic.php @@ -389,7 +389,7 @@ function csrf_conf($key, $val) { */ function csrf_start() { if ($GLOBALS['csrf']['auto-session'] && !session_id()) { - session_start(); + zm_session_start(); } } diff --git a/web/includes/database.php b/web/includes/database.php index d0124656d..2fb2db108 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -25,20 +25,26 @@ define('DB_LOG_DEBUG', 2); $GLOBALS['dbLogLevel'] = DB_LOG_OFF; $GLOBALS['dbConn'] = false; +require_once('logger.php'); function dbConnect() { global $dbConn; - if ( strpos(ZM_DB_HOST, ':') ) { - // Host variable may carry a port or socket. - list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); - if ( ctype_digit($portOrSocket) ) { - $socket = ':host='.$host . ';port='.$portOrSocket; + $socket = ''; + if ( ZM_DB_HOST ) { + if ( strpos(ZM_DB_HOST, ':') ) { + // Host variable may carry a port or socket. + list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); + if ( ctype_digit($portOrSocket) ) { + $socket = ':host='.$host . ';port='.$portOrSocket.';'; + } else { + $socket = ':unix_socket='.$portOrSocket.';'; + } } else { - $socket = ':unix_socket='.$portOrSocket; + $socket = ':host='.ZM_DB_HOST.';'; } } else { - $socket = ':host='.ZM_DB_HOST; + $socket = ':host=localhost;'; } try { @@ -49,9 +55,9 @@ function dbConnect() { PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, ); - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); } else { - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); } $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); @@ -61,9 +67,12 @@ function dbConnect() { error_log('Unable to connect to ZM DB ' . $ex->getMessage()); $dbConn = null; } -} + return $dbConn; +} // end function dbConnect -dbConnect(); +if ( !dbConnect() ) { + ZM\Fatal("Failed db connection to $socket"); +} function dbDisconnect() { global $dbConn; diff --git a/web/includes/functions.php b/web/includes/functions.php index c31347a6f..8081314e2 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -40,7 +40,7 @@ function CSPHeaders($view, $nonce) { if ( ! $Servers ) $Servers = ZM\Server::find(); - $additionalScriptSrc = implode(' ', array_map(function($S){return $S->Url();}, $Servers)); + $additionalScriptSrc = implode(' ', array_map(function($S){return $S->Hostname();}, $Servers)); switch ($view) { case 'login': { if (defined('ZM_OPT_USE_GOOG_RECAPTCHA') @@ -840,9 +840,15 @@ function daemonStatus($daemon, $args=false) { initDaemonStatus(); $string = $daemon; - if ( $args ) - $string .= ' ' . $args; - return( strpos($daemon_status, "'$string' running") !== false ); + if ( $args ) { + if ( is_array($args) ) { + $string .= join(' ', $args); +ZM\Warning("daemonStatus args: $string"); + } else { + $string .= ' ' . $args; + } + } + return ( strpos($daemon_status, "'$string' running") !== false ); } function zmcStatus($monitor) { @@ -1484,15 +1490,15 @@ function getLoad() { function getDiskPercent($path = ZM_DIR_EVENTS) { $total = disk_total_space($path); if ( $total === false ) { - Error('disk_total_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_total_space returned false. Verify the web account user has access to ' . $path); return 0; } elseif ( $total == 0 ) { - Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); + ZM\Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); return 100; } $free = disk_free_space($path); if ( $free === false ) { - Error('disk_free_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_free_space returned false. Verify the web account user has access to ' . $path); } $space = round((($total - $free) / $total) * 100); return $space; @@ -2048,7 +2054,7 @@ function logState() { if ( $count['Level'] <= ZM\Logger::PANIC ) $count['Level'] = ZM\Logger::FATAL; if ( !($levelCount = $levelCounts[$count['Level']]) ) { - Error('Unexpected Log level '.$count['Level']); + ZM\Error('Unexpected Log level '.$count['Level']); next; } if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { @@ -2075,18 +2081,23 @@ function isVector(&$array) { function checkJsonError($value) { if ( function_exists('json_last_error') ) { - $value = var_export($value,true); - switch( json_last_error() ) { + $value = var_export($value, true); + switch ( json_last_error() ) { case JSON_ERROR_DEPTH : - ZM\Fatal("Unable to decode JSON string '$value', maximum stack depth exceeded"); + ZM\Error("Unable to decode JSON string '$value', maximum stack depth exceeded"); + break; case JSON_ERROR_CTRL_CHAR : - ZM\Fatal("Unable to decode JSON string '$value', unexpected control character found"); + ZM\Error("Unable to decode JSON string '$value', unexpected control character found"); + break; case JSON_ERROR_STATE_MISMATCH : - ZM\Fatal("Unable to decode JSON string '$value', invalid or malformed JSON"); + ZM\Error("Unable to decode JSON string '$value', invalid or malformed JSON"); + break; case JSON_ERROR_SYNTAX : - ZM\Fatal("Unable to decode JSON string '$value', syntax error"); + ZM\Error("Unable to decode JSON string '$value', syntax error"); + break; default : - ZM\Fatal("Unable to decode JSON string '$value', unexpected error ".json_last_error()); + ZM\Error("Unable to decode JSON string '$value', unexpected error ".json_last_error()); + break; case JSON_ERROR_NONE: break; } @@ -2290,7 +2301,8 @@ function validHtmlStr($input) { function getStreamHTML($monitor, $options = array()) { if ( isset($options['scale']) ) { - if ( $options['scale'] != 'auto' && $options['scale'] != '0' ) { + if ( $options['scale'] != 'auto' && $options['scale'] != '0' and $options['scale'] != '' ) { + ZM\Logger::Debug("Setting dimensions from scale:".$options['scale']); $options['width'] = reScale($monitor->ViewWidth(), $options['scale']).'px'; $options['height'] = reScale($monitor->ViewHeight(), $options['scale']).'px'; } else { diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 6b60c0099..c423a3507 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -87,6 +87,7 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Add', + 'AddMonitorDisabled' => 'Your user is not allowed to add a new monitor', 'AddNewServer' => 'Add New Server', 'AddNewStorage' => 'Add New Storage', 'AddNewUser' => 'Add New User', @@ -983,6 +984,18 @@ $OLANG = array( "loglevel=debug" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug) ' ), + 'OPTIONS_ENCODER_PARAMETERS' => array( + 'Help' => ' + Parameters passed to the encoding codec. name=value separated by either , or newline.~~ + For example to changing quality, use the crf option. 1 is best, 51 is worst 23 is default.~~ +~~ + crf=23~~ + ~~ + You might want to alter the movflags value to support different behaviours. Some people have troubles viewing videos due to the frag_keyframe option, but that option is supposed to allow viewing of incomplete events. See + [https://ffmpeg.org/ffmpeg-formats.html](https://ffmpeg.org/ffmpeg-formats.html) + for more information. ZoneMinder\'s default is frag_keyframe,empty_moov~~ + ', + ), 'OPTIONS_DECODERHWACCELNAME' => array( 'Help' => ' This is equivalent to the ffmpeg -hwaccel command line option. With intel graphics support, use "vaapi". For NVIDIA cuda support use "cuda". To check for support, run ffmpeg -hwaccels on the command line.' diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index cc58073e1..a8b28848c 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -509,7 +509,7 @@ th.table-th-sort-rev span.table-th-sort-span { } #content { - width: 96%; + width: 100%; margin: 0 auto 8px auto; line-height: 130%; text-align: center; diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index c0f4a36d9..2cdc0c400 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -11,10 +11,30 @@ textarea, input[name="newMonitor[Name]"], +input[name="newMonitor[LabelFormat]"], input[name="newMonitor[ControlDevice]"], input[name="newMonitor[ControlAddress]"] { width: 100%; } +input[name="newMonitor[AnalysisFPSLimit]"], +input[name="newMonitor[MaxFPS]"], +input[name="newMonitor[AlarmMaxFPS]"], +input[name="newMonitor[V4LCapturesPerFrame]"]{ + width: 60px; +} +input[name="newMonitor[Port]"], +input[name="newMonitor[Refresh]"], +input[name="newMonitor[LabelX]"], +input[name="newMonitor[LabelY]"], +input[name="newMonitor[ImageBufferCount]"], +input[name="newMonitor[WarmupCount]"], +input[name="newMonitor[PreEventCount]"], +input[name="newMonitor[PostEventCount]"], +input[name="newMonitor[StreamReplayBuffer]"], +input[name="newMonitor[AlarmFrameCount]"], +input[name="newMonitor[AutoStopTimeout]"], +input[name="newMonitor[TrackDelay]"], +input[name="newMonitor[ReturnDelay]"], input[name="newMonitor[Width]"], input[name="newMonitor[Height]"] { width: 80px; diff --git a/web/skins/classic/css/base/views/montage.css b/web/skins/classic/css/base/views/montage.css index e9ec28bd0..aef963e79 100644 --- a/web/skins/classic/css/base/views/montage.css +++ b/web/skins/classic/css/base/views/montage.css @@ -3,7 +3,9 @@ } #content { + /* width: 99%; + */ } #monitors:after { diff --git a/web/skins/classic/css/base/views/zone.css b/web/skins/classic/css/base/views/zone.css index 44cd1a115..4e9d8d479 100644 --- a/web/skins/classic/css/base/views/zone.css +++ b/web/skins/classic/css/base/views/zone.css @@ -4,11 +4,13 @@ } #settingsPanel, #zonePoints { - width: 600px; + max-width: 600px; + width: 50%; } #zoneSettings { border-collapse: collapse; + width: 100%; } #zoneSettings th, @@ -132,5 +134,22 @@ width: 100%; input[name="newAlarmRgbR"], input[name="newAlarmRgbG"], input[name="newAlarmRgbB"] { -width: 30%; +width: 25%; +} +input[name="newZone[MinPixelThreshold]"], +input[name="newZone[MaxPixelThreshold]"], +input[name="newZone[FilterX]"], +input[name="newZone[FilterY]"], +input[name="newZone[MinFilterPixels]"], +input[name="newZone[MaxFilterPixels]"], +input[name="newZone[MinBlobPixels]"], +input[name="newZone[MaxBlobPixels]"], +input[name="newZone[MinBlobs]"], +input[name="newZone[MaxBlobs]"], +input[name="newZone[MinAlarmPixels]"], +input[name="newZone[MaxAlarmPixels]"] { + width: 90px; +} +input.ZonePoint { + width: 75px; } diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 48747b98d..86f4e2533 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -59,7 +59,7 @@ var popupSizes = { 'options': {'width': 1000, 'height': 660}, 'preset': {'width': 300, 'height': 220}, 'server': {'width': 600, 'height': 405}, - 'settings': {'width': 220, 'height': 235}, + 'settings': {'width': 250, 'height': 335}, 'shutdown': {'width': 400, 'height': 400}, 'state': {'width': 400, 'height': 170}, 'stats': {'width': 840, 'height': 200}, diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 2012837e8..067c39391 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -236,7 +236,7 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { console.error("Nothing found to bind to " + fnName); return; } - el.onchange = window[fnName].bind(el, el); + el.oninput = window[fnName].bind(el, el); }); }); diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 5250e654f..2855b404d 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -70,23 +70,5 @@ var focusWindow = ; var imagePrefix = ""; -var auth_hash; -var auth_relay; - +var auth_hash = ''; +var auth_relay = ''; diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index 62a91d7f3..f144c17fa 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -23,7 +23,7 @@ $ServersById = array(); foreach ( $servers as $S ) { $ServersById[$S->Id()] = $S; } -session_start(); +zm_session_start(); foreach ( array('GroupId','Function','ServerId','StorageId','Status','MonitorId','MonitorName','Source') as $var ) { if ( isset($_REQUEST[$var]) ) { if ( $_REQUEST[$var] != '' ) { diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 71c60b6f0..73f79185d 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -178,7 +178,7 @@ getBodyTopHTML();

diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index f8bceb837..464c68b14 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -58,7 +58,7 @@ if ( isset($_REQUEST['scale']) ) { $codec = 'auto'; if ( isset($_REQUEST['codec']) ) { $codec = $_REQUEST['codec']; - session_start(); + zm_session_start(); $_SESSION['zmEventCodec'.$Event->MonitorId()] = $codec; session_write_close(); } else if ( isset($_SESSION['zmEventCodec'.$Event->MonitorId()]) ) { diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 034e908ef..db9f11d4a 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -106,7 +106,7 @@ xhtmlHeaders(__FILE__, translate('Events') ); -

+ @@ -138,6 +138,7 @@ if ( $pages > 1 ) { +
+
diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index 1f696dd70..56a355263 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -145,7 +145,7 @@ foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Servers` ORDER BY lower(`Name`) $monitors = array(); foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Monitors` ORDER BY lower(`Name`) ASC') as $monitor ) { if ( visibleMonitor($monitor['Id']) ) { - $monitors[$monitor['Name']] = validHtmlStr($monitor['Name']); + $monitors[$monitor['Name']] = $monitor; } } diff --git a/web/skins/classic/views/frame.php b/web/skins/classic/views/frame.php index 64f1035b3..1ac65212c 100644 --- a/web/skins/classic/views/frame.php +++ b/web/skins/classic/views/frame.php @@ -26,16 +26,17 @@ if ( !canView('Events') ) { require_once('includes/Frame.php'); $eid = validInt($_REQUEST['eid']); -if ( !empty($_REQUEST['fid']) ) - $fid = validInt($_REQUEST['fid']); +$fid = empty($_REQUEST['fid']) ? 0 : validInt($_REQUEST['fid']); $Event = new ZM\Event($eid); $Monitor = $Event->Monitor(); +# This is kinda weird.. so if we pass fid=0 or some other non-integer, then it loads max score +# perhaps we should consider being explicit, like fid = maxscore if ( !empty($fid) ) { $sql = 'SELECT * FROM Frames WHERE EventId = ? AND FrameId = ?'; - if ( !($frame = dbFetchOne( $sql, NULL, array($eid, $fid) )) ) - $frame = array( 'EventId'=>$eid, 'FrameId'=>$fid, 'Type'=>'Normal', 'Score'=>0 ); + if ( !($frame = dbFetchOne($sql, NULL, array($eid, $fid))) ) + $frame = array('EventId'=>$eid, 'FrameId'=>$fid, 'Type'=>'Normal', 'Score'=>0); } else { $frame = dbFetchOne('SELECT * FROM Frames WHERE EventId = ? AND Score = ?', NULL, array($eid, $Event->MaxScore())); } @@ -61,7 +62,7 @@ if ( isset( $_REQUEST['scale'] ) ) { } $scale = $scale ?: 'auto'; -$imageData = $Event->getImageSrc( $frame, $scale, 0 ); +$imageData = $Event->getImageSrc($frame, $scale, 0); if ( ! $imageData ) { ZM\Error("No data found for Event $eid frame $fid"); $imageData = array(); diff --git a/web/skins/classic/views/js/cycle.js b/web/skins/classic/views/js/cycle.js index f9b1d19fc..6d2c3bdd4 100644 --- a/web/skins/classic/views/js/cycle.js +++ b/web/skins/classic/views/js/cycle.js @@ -9,24 +9,27 @@ function cyclePause() { $('pauseBtn').disabled = true; $('playBtn').disabled = false; } + function cycleStart() { periodical_id = nextCycleView.periodical(cycleRefreshTimeout); $('pauseBtn').disabled = false; $('playBtn').disabled = true; } + function cycleNext() { monIdx ++; if ( monIdx >= monitorData.length ) { monIdx = 0; } if ( !monitorData[monIdx] ) { - console.log("No monitorData for " + monIdx); + console.log('No monitorData for ' + monIdx); } window.location.replace('?view=cycle&mid='+monitorData[monIdx].id+'&mode='+mode, cycleRefreshTimeout); } + function cyclePrev() { - if (monIdx) { + if ( monIdx ) { monIdx -= 1; } else { monIdx = monitorData.length - 1; @@ -71,7 +74,7 @@ function changeSize() { streamImg.style.width = width ? width : null; streamImg.style.height = height ? height : null; } else { - console.log("Did not find liveStream"+monitorData[monIdx].id); + console.log('Did not find liveStream'+monitorData[monIdx].id); } $('scale').set('value', ''); Cookie.write('zmCycleScale', '', {duration: 10*365}); @@ -96,7 +99,7 @@ function changeScale() { return; } - if ( scale != '0' ) { + if ( scale != '0' && scale != '' && scale != 'auto' ) { if ( newWidth ) { monitor_frame.css('width', newWidth+'px'); } @@ -107,33 +110,36 @@ function changeScale() { monitor_frame.css('width', '100%'); monitor_frame.css('height', 'auto'); } + /*Stream could be an applet so can't use moo tools*/ var streamImg = $j('#liveStream'+monitorData[monIdx].id)[0]; - if ( streamImg ) { - if ( streamImg.nodeName == 'IMG' ) { - var src = streamImg.src; - streamImg.src = ''; - - //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); - src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); - if ( scale != '0' ) { - src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); - src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); - } else { - src = src.replace(/width=[\.\d]+/i, 'width='+monitorData[monIdx].width); - src = src.replace(/height=[\.\d]+/i, 'height='+monitorData[monIdx].height); - } - streamImg.src = src; - } - if ( scale != '0' ) { - streamImg.style.width = newWidth+'px'; - streamImg.style.height = newHeight+'px'; - } else { - streamImg.style.width = '100%'; - streamImg.style.height = 'auto'; - } - } else { + if ( !streamImg ) { console.log("Did not find liveStream"+monitorData[monIdx].id); + return; + } + + if ( streamImg.nodeName == 'IMG' ) { + var src = streamImg.src; + streamImg.src = ''; + + //src = src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); + src = src.replace(/scale=[\.\d]+/i, 'scale='+scale); + if ( scale != '0' && scale != '' && scale != 'auto' ) { + src = src.replace(/width=[\.\d]+/i, 'width='+newWidth); + src = src.replace(/height=[\.\d]+/i, 'height='+newHeight); + } else { + src = src.replace(/width=[\.\d]+/i, 'width='+monitorData[monIdx].width); + src = src.replace(/height=[\.\d]+/i, 'height='+monitorData[monIdx].height); + } + streamImg.src = src; + } + + if ( scale != '0' && scale != '' && scale != 'auto' ) { + streamImg.style.width = newWidth+'px'; + streamImg.style.height = newHeight+'px'; + } else { + streamImg.style.width = '100%'; + streamImg.style.height = 'auto'; } } // end function changeScale() diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 69b34d055..683f144f6 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -308,8 +308,7 @@ function pauseClicked() { streamPause(); } -function streamPause( ) { - +function streamPause() { $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); @@ -1078,6 +1077,12 @@ function initPage() { vid.on('click', function(event) { handleClick(event); }); + vid.on('volumechange', function() { + Cookie.write('volume', vid.volume(), {duration: 10*365}); + }); + if ( Cookie.read('volume') != null ) { + vid.volume(Cookie.read('volume')); + } vid.on('timeupdate', function() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 5b01d150f..a77b757ec 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -7,7 +7,7 @@ function validateForm( form ) { var obrCount = 0; var cbrCount = 0; for ( var i = 0; i < rows.length; i++ ) { - if (rows.length > 2) { + if ( rows.length > 2 ) { obrCount += parseInt(form.elements['filter[Query][terms][' + i + '][obr]'].value); cbrCount += parseInt(form.elements['filter[Query][terms][' + i + '][cbr]'].value); } @@ -22,9 +22,43 @@ function validateForm( form ) { } var numbers_reg = /\D/; if ( numbers_reg.test(form.elements['filter[Query][limit]'].value) ) { - alert("There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty."); + alert('There appear to be non-numeric characters in your limit. Limit must be a positive integer value or empty.'); return false; } + if ( form.elements['filter[AutoDelete]'].checked ) { + // if Delete action is Enabled should also have an unarchived term + var have_archivestatus_term = false; + for ( var i = 0; i < rows.length; i++ ) { + if ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'Archived' ) { + have_archivestatus_term = true; + } + } + if ( ! have_archivestatus_term ) { + return confirm('You have enabled deleting events but do not have a term referencing the archived status of the event. This filter may delete events that you want to save! Are you sure?'); + } + } else if ( form.elements['filter[Background]'].checked ) { + if ( ! ( + form.elements['filter[AutoArchive]'].checked + || + form.elements['filter[UpdateDiskSpace]'].checked + || + form.elements['filter[AutoVideo]'].checked + || + form.elements['filter[AutoEmail]'].checked + || + form.elements['filter[AutoMessage]'].checked + || + form.elements['filter[AutoExecute]'].checked + || + form.elements['filter[AutoDelete]'].checked + || + form.elements['filter[AutoCopy]'].checked + || + form.elements['filter[AutoMove]'].checked + ) ) { + alert('You have chosen to run this filter in the background but not selected any actions.'); + } + } return true; } @@ -143,8 +177,8 @@ function deleteFilter( element ) { } var escape = document.createElement('textarea'); function escapeHTML(html) { - escape.textContent = html; - return escape.innerHTML; + escape.textContent = html; + return escape.innerHTML; } function parseRows(rows) { diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index d0e0c348c..0ebc328bd 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -212,7 +212,8 @@ function Monitor(monitorData) { * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - layout = $j(element).val(); + var ddm = $j('#zmMontageLayout'); + layout = ddm.val(); if ( layout_id = parseInt(layout) ) { layout = layouts[layout]; @@ -231,6 +232,7 @@ function selectLayout(element) { if ( layout.Positions['default'] ) { styles = layout.Positions['default']; for ( style in styles ) { + console.log("Applying " + style + ' ' + styles[style]); monitor_frame.css(style, styles[style]); } } else { @@ -241,7 +243,6 @@ function selectLayout(element) { styles = layout.Positions['mId'+monitor.id]; for ( style in styles ) { monitor_frame.css(style, styles[style]); - console.log("Applying " + style + ' : ' + styles[style]); } } else { console.log("No Monitor styles to apply"); @@ -284,8 +285,8 @@ function selectLayout(element) { * called when the widthControl|heightControl select elements are changed */ function changeSize() { - var width = $('width').get('value'); - var height = $('height').get('value'); + var width = parseInt($('width').get('value')); + var height = parseInt($('height').get('value')); for ( var i = 0, length = monitors.length; i < length; i++ ) { var monitor = monitors[i]; @@ -296,12 +297,8 @@ function changeSize() { console.log("Error finding frame for " + monitor.id); continue; } - if ( width ) { - monitor_frame.css('width', width); - } - if ( height ) { - monitor_frame.css('height', height); - } + monitor_frame.css('width', ( width ? width+'px' : 'auto')); + monitor_frame.css('height', ( height ? height+'px' : 'auto')); /*Stream could be an applet so can't use moo tools*/ var streamImg = $('liveStream'+monitor.id); @@ -314,8 +311,8 @@ function changeSize() { src = src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); streamImg.src = src; } - streamImg.style.width = width ? width : null; - streamImg.style.height = height ? height : null; + streamImg.style.width = width ? width+'px' : null; + streamImg.style.height = height ? height+'px' : null; //streamImg.style.height = ''; } } @@ -382,8 +379,8 @@ function changeScale() { streamImg.src = src; } if ( scale != '0' ) { - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; + streamImg.style.width = newWidth + 'px'; + streamImg.style.height = newHeight + 'px'; } else { streamImg.style.width = '100%'; streamImg.style.height = 'auto'; diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 0b4d1d5fc..8742ac67b 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -208,7 +208,9 @@ window.addEventListener('DOMContentLoaded', function() { el.onmouseover = window[el.getAttribute('data-on-mouseover-this')].bind(el, el); }); document.querySelectorAll('div.zoom').forEach(function(el) { - el.onclick = function(ev) { window[el.getAttribute('data-on-click')](ev); }; + el.onclick = function(ev) { + window[el.getAttribute('data-on-click')](ev); + }; }); }); diff --git a/web/skins/classic/views/js/user.js b/web/skins/classic/views/js/user.js index 65c05c546..d491514e3 100644 --- a/web/skins/classic/views/js/user.js +++ b/web/skins/classic/views/js/user.js @@ -12,16 +12,22 @@ function validateForm( form, newUser ) { } else if ( newUser ) { errors[errors.length] = "You must supply a password"; } - var monitorIds = new Array(); - for ( var i = 0; i < form.elements['monitorIds'].options.length; i++ ) { - if ( form.elements['monitorIds'].options[i].selected ) { - monitorIds[monitorIds.length] = form.elements['monitorIds'].options[i].value; - } - } - form.elements['newUser[MonitorIds]'].value = monitorIds.join( ',' ); if ( errors.length ) { - alert( errors.join( "\n" ) ); - return ( false ); + alert(errors.join("\n")); + return false; } - return ( true ); + return true; } + +function initPage() { + $j('#contentForm').submit(function(event) { + if ( validateForm(this) ) { + $j('#contentButtons').hide(); + return true; + } else { + return false; + }; + }); +} // end function initPage + +window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 0056f0a08..19f74333b 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -635,7 +635,7 @@ function getEventCmdResponse( respObj, respText ) { row.removeClass('recent'); } row.addClass('updated'); - } // end foreach event + } // end foreach event var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { @@ -651,7 +651,7 @@ function getEventCmdResponse( respObj, respText ) { } } else { checkStreamForErrors('getEventCmdResponse', respObj); - } // end if objresult == ok + } // end if objresult == ok var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { diff --git a/web/skins/classic/views/js/zone.js b/web/skins/classic/views/js/zone.js index 2ea02cfd9..9071cfb24 100644 --- a/web/skins/classic/views/js/zone.js +++ b/web/skins/classic/views/js/zone.js @@ -178,13 +178,23 @@ function applyPreset() { function toPixels(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((field.value*maxValue)/100); + if ( field.value > maxValue ) + field.value = maxValue; } + field.setAttribute('step', 1); + field.setAttribute('max', maxValue); } +// maxValue is the max Pixels value which is normally the max area function toPercent(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((100*100*field.value)/maxValue)/100; + if ( field.value > 100 ) { + field.value = 100; + } } + field.setAttribute('step', 0.01); + field.setAttribute('max', 100); } function applyZoneUnits() { @@ -307,8 +317,8 @@ function updateActivePoint(index) { zone['Points'][index].x = x; zone['Points'][index].y = y; var Point = $('zonePoly').points.getItem(index); - Point.x =x; - Point.y =y; + Point.x = x; + Point.y = y; updateArea(); } @@ -317,17 +327,15 @@ function addPoint(index) { if ( index >= (zone['Points'].length-1) ) { nextIndex = 0; } + var newX = parseInt(Math.round((zone['Points'][index]['x']+zone['Points'][nextIndex]['x'])/2)); var newY = parseInt(Math.round((zone['Points'][index]['y']+zone['Points'][nextIndex]['y'])/2)); if ( nextIndex == 0 ) { zone['Points'][zone['Points'].length] = {'x': newX, 'y': newY}; } else { - zone['Points'].splice( nextIndex, 0, {'x': newX, 'y': newY} ); + zone['Points'].splice(nextIndex, 0, {'x': newX, 'y': newY}); } drawZonePoints(); - // drawZonePoints calls updateZoneImage - //updateZoneImage(); - //setActivePoint( nextIndex ); } function delPoint(index) { @@ -349,15 +357,17 @@ function updateArea( ) { } else if ( form.elements['newZone[Units]'].value == 'Pixels' ) { form.elements['newZone[TempArea]'].value = area; } else { - alert("Unknown units: " + form.elements['newZone[Units]'].value); + alert('Unknown units: ' + form.elements['newZone[Units]'].value); } } -function updateX(index) { - limitPointValue($('newZone[Points]['+index+'][x]'), 0, maxX); +function updateX(input) { + index = input.getAttribute('data-point-index'); + + limitPointValue(input, 0, maxX); var point = $('point'+index); - var x = $('newZone[Points]['+index+'][x]').get('value'); + var x = input.value; point.setStyle('left', x+'px'); zone['Points'][index].x = x; @@ -366,11 +376,12 @@ function updateX(index) { updateArea(); } -function updateY(index) { - limitPointValue($('newZone[Points]['+index+'][y]'), 0, maxY); +function updateY(input) { + index = input.getAttribute('data-point-index'); + limitPointValue(input, 0, maxY); var point = $('point'+index); - var y = $('newZone[Points]['+index+'][y]').get('value'); + var y = input.value; point.setStyle('top', y+'px'); zone['Points'][index].y = y; @@ -384,7 +395,7 @@ function saveChanges(element) { if ( validateForm(form) ) { submitForm(form); if ( form.elements['newZone[Type]'].value == 'Privacy' ) { - alert( 'Capture process for this monitor will be restarted for the Privacy zone changes to take effect.' ); + alert('Capture process for this monitor will be restarted for the Privacy zone changes to take effect.'); } return true; } @@ -399,6 +410,7 @@ function drawZonePoints() { for ( var i = 0; i < zone['Points'].length; i++ ) { var div = new Element('div', { 'id': 'point'+i, + 'data-index': i, 'class': 'zonePoint', 'title': 'Point '+(i+1), 'styles': { @@ -408,6 +420,7 @@ function drawZonePoints() { }); div.addEvent('mouseover', highlightOn.pass(i)); div.addEvent('mouseout', highlightOff.pass(i)); + div.inject($('imageFrame')); div.makeDraggable( { 'container': $('imageFrame'), @@ -435,9 +448,13 @@ function drawZonePoints() { 'id': 'newZone[Points]['+i+'][x]', 'name': 'newZone[Points]['+i+'][x]', 'value': zone['Points'][i].x, - 'size': 5 + 'type': 'number', + 'class': 'ZonePoint', + 'min': '0', + 'max': maxX, + 'data-point-index': i }); - input.addEvent('input', updateX.pass(i)); + input.oninput = window['updateX'].bind(input, input); input.inject(cell); cell.inject(row); @@ -446,9 +463,13 @@ function drawZonePoints() { 'id': 'newZone[Points]['+i+'][y]', 'name': 'newZone[Points]['+i+'][y]', 'value': zone['Points'][i].y, - 'size': 5 + 'type': 'number', + 'class': 'ZonePoint', + 'min': '0', + 'max': maxY, + 'data-point-index': i } ); - input.addEvent('input', updateY.pass(i)); + input.oninput = window['updateY'].bind(input, input); input.inject(cell); cell.inject(row); @@ -468,7 +489,7 @@ function drawZonePoints() { cell.inject(row); row.inject(tables[i%tables.length].getElement('tbody')); - } + } // end foreach point // Sets up the SVG polygon updateZoneImage(); } @@ -495,34 +516,6 @@ function setAlarmState( currentAlarmState ) { } else { $('stateValue').removeProperty('class'); } - - var isAlarmed = ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ); - var wasAlarmed = ( lastAlarmState == STATE_ALARM || lastAlarmState == STATE_ALERT ); - - var newAlarm = ( isAlarmed && !wasAlarmed ); - var oldAlarm = ( !isAlarmed && wasAlarmed ); - - if ( newAlarm ) { - if ( SOUND_ON_ALARM ) { - // Enable the alarm sound - if ( !canPlayPauseAudio ) { - $('alarmSound').removeClass('hidden'); - } else { - $('MediaPlayer').Play(); - } - } - } - if ( SOUND_ON_ALARM ) { - if ( oldAlarm ) { - // Disable alarm sound - if ( !canPlayPauseAudio ) { - $('alarmSound').addClass('hidden'); - } else { - $('MediaPlayer').Stop(); - } - } - } - lastAlarmState = alarmState; } var streamCmdParms = "view=request&request=stream&connkey="+connKey; @@ -684,7 +677,6 @@ var watchdogFunctions = { //Make sure the various refreshes are still taking effect function watchdogCheck(type) { if ( watchdogInactive[type] ) { - console.log("Detected streamWatch of type: " + type + " stopped, restarting"); watchdogFunctions[type](); watchdogInactive[type] = false; } else { diff --git a/web/skins/classic/views/js/zone.js.php b/web/skins/classic/views/js/zone.js.php index 41f384250..c03264d7a 100644 --- a/web/skins/classic/views/js/zone.js.php +++ b/web/skins/classic/views/js/zone.js.php @@ -1,3 +1,14 @@ + + var presets = new Object(); ] = { 'ExtendAlarmFrames': '' }; var zone = { diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 5ac52494e..faa06bebf 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -21,7 +21,7 @@ require_once('includes/Server.php'); require_once('includes/Storage.php'); -if ( !canView('Monitors') ) { +if ( !canEdit('Monitors', empty($_REQUEST['mid'])?0:$_REQUEST['mid']) ) { $view = 'error'; return; } @@ -170,14 +170,14 @@ unset($httpMethods['jpegTags']); if ( ZM_HAS_V4L1 ) { $v4l1DeviceFormats = array( - 'PAL' => 0, - 'NTSC' => 1, - 'SECAM' => 2, - 'AUTO' => 3, - 'FMT4' => 4, - 'FMT5' => 5, - 'FMT6' => 6, - 'FMT7' => 7 + 0 => 'PAL', + 1 => 'NTSC', + 2 => 'SECAM', + 3 => 'AUTO', + 4 => 'FMT4', + 5 => 'FMT5', + 6 => 'FMT6', + 7 => 'FMT7' ); $v4l1MaxChannels = 15; @@ -186,48 +186,48 @@ if ( ZM_HAS_V4L1 ) { $v4l1DeviceChannels[$i] = $i; $v4l1LocalPalettes = array( - translate('Grey') => 1, - 'BGR32' => 5, - 'BGR24' => 4, - '*YUYV' => 8, - '*RGB565' => 3, - '*RGB555' => 6, - '*YUV422' => 7, - '*YUV422P' => 13, - '*YUV420P' => 15 + 1 => translate('Grey'), + 5 => 'BGR32', + 4 => 'BGR24', + 8 => '*YUYV', + 3 => '*RGB565', + 6 => '*RGB555', + 7 => '*YUV422', + 13 => '*YUV422P', + 15 => '*YUV420P', ); } if ( ZM_HAS_V4L2 ) { $v4l2DeviceFormats = array( - 'PAL' => 0x000000ff, - 'NTSC' => 0x0000b000, - 'PAL B' => 0x00000001, - 'PAL B1' => 0x00000002, - 'PAL G' => 0x00000004, - 'PAL H' => 0x00000008, - 'PAL I' => 0x00000010, - 'PAL D' => 0x00000020, - 'PAL D1' => 0x00000040, - 'PAL K' => 0x00000080, - 'PAL M' => 0x00000100, - 'PAL N' => 0x00000200, - 'PAL Nc' => 0x00000400, - 'PAL 60' => 0x00000800, - 'NTSC M' => 0x00001000, - 'NTSC M JP' => 0x00002000, - 'NTSC 443' => 0x00004000, - 'NTSC M KR' => 0x00008000, - 'SECAM B' => 0x00010000, - 'SECAM D' => 0x00020000, - 'SECAM G' => 0x00040000, - 'SECAM H' => 0x00080000, - 'SECAM K' => 0x00100000, - 'SECAM K1' => 0x00200000, - 'SECAM L' => 0x00400000, - 'SECAM LC' => 0x00800000, - 'ATSC 8 VSB' => 0x01000000, - 'ATSC 16 VSB' => 0x02000000, + 0x000000ff => 'PAL', + 0x0000b000 => 'NTSC', + 0x00000001 => 'PAL B', + 0x00000002 => 'PAL B1', + 0x00000004 => 'PAL G', + 0x00000008 => 'PAL H', + 0x00000010 => 'PAL I', + 0x00000020 => 'PAL D', + 0x00000040 => 'PAL D1', + 0x00000080 => 'PAL K', + 0x00000100 => 'PAL M', + 0x00000200 => 'PAL N', + 0x00000400 => 'PAL Nc', + 0x00000800 => 'PAL 60', + 0x00001000 => 'NTSC M', + 0x00002000 => 'NTSC M JP', + 0x00004000 => 'NTSC 443', + 0x00008000 => 'NTSC M KR', + 0x00010000 => 'SECAM B', + 0x00020000 => 'SECAM D', + 0x00040000 => 'SECAM G', + 0x00080000 => 'SECAM H', + 0x00100000 => 'SECAM K', + 0x00200000 => 'SECAM K1', + 0x00400000 => 'SECAM L', + 0x00800000 => 'SECAM LC', + 0x01000000 => 'ATSC 8 VSB', + 0x02000000 => 'ATSC 16 VSB', ); $v4l2MaxChannels = 31; @@ -236,57 +236,58 @@ if ( ZM_HAS_V4L2 ) { $v4l2DeviceChannels[$i] = $i; $v4l2LocalPalettes = array( - 'Auto' => 0, /* Automatic palette selection */ + 0 => 'Auto', /* Automatic palette selection */ - /* Pixel format FOURCC depth Description */ - translate('Grey') => fourcc('G','R','E','Y'), /* 8 Greyscale */ - 'BGR32' => fourcc('B','G','R','4'), /* 32 BGR-8-8-8-8 */ - 'RGB32' => fourcc('R','G','B','4'), /* 32 RGB-8-8-8-8 */ - 'BGR24' => fourcc('B','G','R','3'), /* 24 BGR-8-8-8 */ - 'RGB24' => fourcc('R','G','B','3'), /* 24 RGB-8-8-8 */ - '*YUYV' => fourcc('Y','U','Y','V'), /* 16 YUV 4:2:2 */ + /* FOURCC => Pixel format depth Description */ + fourcc('G','R','E','Y') => translate('Grey'), /* 8 Greyscale */ + fourcc('B','G','R','4') => 'BGR32', /* 32 BGR-8-8-8-8 */ + fourcc('R','G','B','4') => 'RGB32', /* 32 RGB-8-8-8-8 */ + fourcc('B','G','R','3') => 'BGR24', /* 24 BGR-8-8-8 */ + fourcc('R','G','B','3') => 'RGB24', /* 24 RGB-8-8-8 */ + fourcc('Y','U','Y','V') => '*YUYV', /* 16 YUV 4:2:2 */ /* compressed formats */ - '*JPEG' => fourcc('J','P','E','G'), /* JFIF JPEG */ - '*MJPEG' => fourcc('M','J','P','G'), /* Motion-JPEG */ - //'DV' => fourcc('d','v','s','d'), /* 1394 */ - //'MPEG' => fourcc('M','P','E','G'), /* MPEG-1/2/4 */ + fourcc('J','P','E','G') => '*JPEG', /* JFIF JPEG */ + fourcc('M','J','P','G') => '*MJPEG', /* Motion-JPEG */ + // fourcc('d','v','s','d') => 'DV', /* 1394 */ + // fourcc('M','P','E','G') => 'MPEG', /* MPEG-1/2/4 */ - //'RGB332' => fourcc('R','G','B','1'), /* 8 RGB-3-3-2 */ - '*RGB444' => fourcc('R','4','4','4'), /* 16 xxxxrrrr ggggbbbb */ - '*RGB555' => fourcc('R','G','B','O'), /* 16 RGB-5-5-5 */ - '*RGB565' => fourcc('R','G','B','P'), /* 16 RGB-5-6-5 */ - //'RGB555X' => fourcc('R','G','B','Q'), /* 16 RGB-5-5-5 BE */ - //'RGB565X' => fourcc('R','G','B','R'), /* 16 RGB-5-6-5 BE */ - //'Y16' => fourcc('Y','1','6',''), /* 16 Greyscale */ - //'PAL8' => fourcc('P','A','L','8'), /* 8 8-bit palette */ - //'YVU410' => fourcc('Y','V','U','9'), /* 9 YVU 4:1:0 */ - //'YVU420' => fourcc('Y','V','1','2'), /* 12 YVU 4:2:0 */ + // + fourcc('R','G','B','1') => 'RGB332', /* 8 RGB-3-3-2 */ + fourcc('R','4','4','4') => '*RGB444', /* 16 xxxxrrrr ggggbbbb */ + fourcc('R','G','B','O') => '*RGB555', /* 16 RGB-5-5-5 */ + fourcc('R','G','B','P') => '*RGB565', /* 16 RGB-5-6-5 */ + // fourcc('R','G','B','Q') => 'RGB555X', /* 16 RGB-5-5-5 BE */ + // fourcc('R','G','B','R') => 'RGB565X', /* 16 RGB-5-6-5 BE */ + // fourcc('Y','1','6','') => 'Y16', /* 16 Greyscale */ + // fourcc('P','A','L','8') => 'PAL8', /* 8 8-bit palette */ + // fourcc('Y','V','U','9') => 'YVU410', /* 9 YVU 4:1:0 */ + // fourcc('Y','V','1','2') => 'YVU420', /* 12 YVU 4:2:0 */ - '*UYVY' => fourcc('U','Y','V','Y'), /* 16 YUV 4:2:2 */ - '*YUV422P' => fourcc('4','2','2','P'), /* 16 YVU422 planar */ - '*YUV411P' => fourcc('4','1','1','P'), /* 16 YVU411 planar */ - //'Y41P' => fourcc('Y','4','1','P'), /* 12 YUV 4:1:1 */ - '*YUV444' => fourcc('Y','4','4','4'), /* 16 xxxxyyyy uuuuvvvv */ - //'YUV555' => fourcc('Y','U','V','O'), /* 16 YUV-5-5-5 */ - //'YUV565' => fourcc('Y','U','V','P'), /* 16 YUV-5-6-5 */ - //'YUV32' => fourcc('Y','U','V','4'), /* 32 YUV-8-8-8-8 */ + fourcc('U','Y','V','Y') => '*UYVY', /* 16 YUV 4:2:2 */ + fourcc('4','2','2','P') => '*YUV422P', /* 16 YVU422 planar */ + fourcc('4','1','1','P') => '*YUV411P', /* 16 YVU411 planar */ + // fourcc('Y','4','1','P') => 'Y41P', /* 12 YUV 4:1:1 */ + fourcc('Y','4','4','4') => '*YUV444', /* 16 xxxxyyyy uuuuvvvv */ + // fourcc('Y','U','V','O') => 'YUV555', /* 16 YUV-5-5-5 */ + // fourcc('Y','U','V','P') => 'YUV565', /* 16 YUV-5-6-5 */ + // fourcc('Y','U','V','4') => 'YUV32', /* 32 YUV-8-8-8-8 */ /* two planes -- one Y, one Cr + Cb interleaved */ - //'NV12' => fourcc('N','V','1','2'), /* 12 Y/CbCr 4:2:0 */ - //'NV21' => fourcc('N','V','2','1'), /* 12 Y/CrCb 4:2:0 */ + fourcc('N','V','1','2') => 'NV12', /* 12 Y/CbCr 4:2:0 */ + // fourcc('N','V','2','1') => 'NV21', /* 12 Y/CrCb 4:2:0 */ /* The following formats are not defined in the V4L2 specification */ - '*YUV410' => fourcc('Y','U','V','9'), /* 9 YUV 4:1:0 */ - '*YUV420' => fourcc('Y','U','1','2'), /* 12 YUV 4:2:0 */ - //'YYUV' => fourcc('Y','Y','U','V'), /* 16 YUV 4:2:2 */ - //'HI240' => fourcc('H','I','2','4'), /* 8 8-bit color */ - //'HM12' => fourcc('H','M','1','2'), /* 8 YUV 4:2:0 16x16 macroblocks */ + fourcc('Y','U','V','9') => '*YUV410', /* 9 YUV 4:1:0 */ + fourcc('Y','U','1','2') => '*YUV420', /* 12 YUV 4:2:0 */ + // fourcc('Y','Y','U','V') => 'YYUV', /* 16 YUV 4:2:2 */ + // fourcc('H','I','2','4') => 'HI240', /* 8 8-bit color */ + // fourcc('H','M','1','2') => 'HM12', /* 8 YUV 4:2:0 16x16 macroblocks */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ - //'SBGGR8' => fourcc('B','A','8','1'), /* 8 BGBG.. GRGR.. */ - //'SGBRG8' => fourcc('G','B','R','G'), /* 8 GBGB.. RGRG.. */ - //'SBGGR16' => fourcc('B','Y','R','2'), /* 16 BGBG.. GRGR.. */ + // fourcc('B','A','8','1') => 'SBGGR8', /* 8 BGBG.. GRGR.. */ + // fourcc('G','B','R','G') => 'SGBRG8', /* 8 GBGB.. RGRG.. */ + // fourcc('B','Y','R','2') => 'SBGGR16', /* 16 BGBG.. GRGR.. */ /* Vendor-specific formats */ //'WNVA' => fourcc('W','N','V','A'), /* Winnov hw compress */ @@ -320,50 +321,50 @@ $orientations = array( ); $deinterlaceopts = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', +); $deinterlaceopts_v4l2 = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205, - 'V4L2: Capture top field only' => 0x02000000, - 'V4L2: Capture bottom field only' => 0x03000000, - 'V4L2: Alternate fields (Bob)' => 0x07000000, - 'V4L2: Progressive' => 0x01000000, - 'V4L2: Interlaced' => 0x04000000 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', + 0x02000000 => 'V4L2: Capture top field only', + 0x03000000 => 'V4L2: Capture bottom field only', + 0x07000000 => 'V4L2: Alternate fields (Bob)', + 0x01000000 => 'V4L2: Progressive', + 0x04000000 => 'V4L2: Interlaced', +); $fastblendopts = array( - 'No blending' => 0, - '1.5625%' => 1, - '3.125%' => 3, - '6.25% (Indoor)' => 6, - '12.5% (Outdoor)' => 12, - '25%' => 25, - '50%' => 50 + 0 => 'No blending', + 1 => '1.5625%', + 3 => '3.125%', + 6 => '6.25% (Indoor)', + 12 => '12.5% (Outdoor)', + 25 => '25%', + 50 => '50%', ); $fastblendopts_alarm = array( - 'No blending (Alarm lasts forever)' => 0, - '1.5625%' => 1, - '3.125%' => 3, - '6.25%' => 6, - '12.5%' => 12, - '25%' => 25, - '50% (Alarm lasts a moment)' => 50 + 0 => 'No blending (Alarm lasts forever)', + 1 => '1.5625%', + 3 => '3.125%', + 6 => '6.25%', + 12 => '12.5%', + 25 => '25%', + 50 => '50% (Alarm lasts a moment)', ); $label_size = array( @@ -671,45 +672,50 @@ switch ( $tab ) { echo htmlOptions(ZM\Group::get_dropdown_options(), $monitor->GroupIds()); ?> - + + + + Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) { ?>  () - + CAUTION: See the help text  () - + CAUTION: See the help text - - + + + + + + + + - + RefBlendPerc()); ?> - - + AlarmRefBlendPerc()); ?> + @@ -729,8 +735,8 @@ switch ( $tab ) { continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
'; - echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; $optCount ++; } # end foreach trigger option if ( !$optCount ) { @@ -746,23 +752,41 @@ switch ( $tab ) { { if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) { ?> - - Method(), "submitTab( '$tab' );" ); ?> + + + + Method(), array('onchange'=>'submitTab', 'data-tab-name'=>$tab) ); ?> + Method() == 'v4l1' ) { ?> - + Channel()); ?> + + + + Format()); ?> + + + + Palette()); ?> - - - - - + + + Channel()); ?> + + + + Format()); ?> + + + + Palette()); ?> + @@ -774,35 +798,39 @@ switch ( $tab ) { V4LMultiBuffer() == '' ? 'checked="checked"' : '' ) ?>/> - + + + + Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); } else if ( $monitor->Type() == 'Remote' ) { ?> - Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> - + + + Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> + -Protocol() || $monitor->Protocol() == 'http' ) { - echo htmlSelect('newMonitor[Method]', $httpMethods, $monitor->Method() ); + echo htmlSelect('newMonitor[Method]', $httpMethods, $monitor->Method()); } else { - echo htmlSelect('newMonitor[Method]', $rtspMethods, $monitor->Method() ); + echo htmlSelect('newMonitor[Method]', $rtspMethods, $monitor->Method()); } ?> - + Type() == 'File' ) { ?> - + Type() == 'cURL' ) { ?> @@ -818,15 +846,15 @@ include('_monitor_source_nvsocket.php'); () - + () - + - + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { @@ -873,8 +901,8 @@ include('_monitor_source_nvsocket.php'); () - - + + translate('Custom'), '176x120'=>'176x120 QCIF', @@ -978,8 +1006,11 @@ include('_monitor_source_nvsocket.php'); ?> - - +  () + + + + Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> @@ -1000,11 +1031,11 @@ include('_monitor_source_nvsocket.php'); - + - + @@ -1018,27 +1049,27 @@ include('_monitor_source_nvsocket.php'); ?> - + - + - + - + - + - + - ControlId()); + ControlId()); if ( canEdit('Control') ) { echo ' '.makePopupLink('?view=controlcaps', 'zmControlCaps', 'controlcaps', translate('Edit')); } @@ -1068,7 +1099,7 @@ if ( canEdit('Control') ) { - + @@ -1076,11 +1107,11 @@ if ( canEdit('Control') ) { - + - translate('None'), '0' => translate('Home'), @@ -1090,7 +1121,7 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL - + ReturnL - + - + - + - + - + - + @@ -1167,7 +1198,7 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL - + @@ -1182,7 +1213,7 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL      - sync + sync diff --git a/web/skins/classic/views/monitorprobe.php b/web/skins/classic/views/monitorprobe.php index 9cab32b3e..a76c67810 100644 --- a/web/skins/classic/views/monitorprobe.php +++ b/web/skins/classic/views/monitorprobe.php @@ -39,16 +39,18 @@ function probeV4L() { } $monitors = array(); - foreach ( dbFetchAll("SELECT Id, Name, Device,Channel FROM Monitors WHERE Type = 'Local' ORDER BY Device, Channel" ) as $monitor ) + foreach ( dbFetchAll("SELECT Id, Name, Device, Channel FROM Monitors WHERE Type = 'Local' ORDER BY Device, Channel" ) as $monitor ) $monitors[$monitor['Device'].':'.$monitor['Channel']] = $monitor; $devices = array(); $preferredStandards = array('PAL', 'NTSC'); $preferredFormats = array('BGR3', 'RGB3', 'YUYV', 'UYVY', 'JPEG', 'MJPG', '422P', 'YU12', 'GREY'); foreach ( $output as $line ) { - if ( !preg_match('/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches) ) - ZM\Fatal("Can't parse command output '$line'"); - $standards = explode('/',$deviceMatches[2]); + if ( !preg_match('/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches) ) { + ZM\Error("Can't parse command output '$line'"); + continue; + } + $standards = explode('/', $deviceMatches[2]); $preferredStandard = false; foreach ( $preferredStandards as $standard ) { if ( in_array( $standard, $standards ) ) { @@ -56,7 +58,7 @@ function probeV4L() { break; } } - $formats = explode('/',$deviceMatches[3]); + $formats = explode('/', $deviceMatches[3]); $preferredFormat = false; foreach ( $preferredFormats as $format ) { if ( in_array($format, $formats) ) { @@ -73,8 +75,10 @@ function probeV4L() { ); $inputs = array(); for ( $i = 0; $i < $deviceMatches[4]; $i++ ) { - if ( !preg_match('/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches) ) - ZM\Fatal("Can't parse input '".$deviceMatches[5]."'"); + if ( !preg_match('/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches) ) { + ZM\Error("Can't parse input '".$deviceMatches[5]."'"); + continue; + } if ( $inputMatches[2] == 'Camera' ) { $input = array( 'index' => $i, @@ -101,7 +105,7 @@ function probeV4L() { $inputMonitor['Colours'] = 1; $inputMonitor['SignalCheckColour'] = '#000023'; } - $inputDesc = base64_encode(serialize($inputMonitor)); + $inputDesc = base64_encode(json_encode($inputMonitor)); $inputString = $deviceMatches[1].', chan '.$i.($input['free']?(' - '.translate('Available')):(' ('.$monitors[$input['id']]['Name'].')')); $inputs[] = $input; $cameras[$inputDesc] = $inputString; @@ -288,7 +292,7 @@ function probeNetwork() { if ( isset($macBases[$macRoot]) ) { $macBase = $macBases[$macRoot]; $camera = call_user_func($macBase['probeFunc'], $ip); - $sourceDesc = base64_encode(serialize($camera['monitor'])); + $sourceDesc = base64_encode(json_encode($camera['monitor'])); $sourceString = $camera['model'].' @ '.$host; if ( isset($monitors[$ip]) ) { $monitor = $monitors[$ip]; @@ -330,7 +334,7 @@ xhtmlHeaders(__FILE__, translate('MonitorProbe') );

- + 'configureButtons(this)')); ?>

@@ -202,23 +201,28 @@ if ( $showZones ) {
connKey(); // Minor hack ?>
+ data-width="ViewWidth(), $monitor->PopupScale()); ?>" + data-height="ViewHeight(), $monitor->PopupScale()); ?>" + > connKey(); + + ZM\Logger::Debug("Options: " . print_r($monitor_options,true)); if (0 and $Positions ) { $monitor_options['width'] = '100%'; $monitor_options['height'] = '100%'; @@ -237,8 +241,8 @@ foreach ( $monitors as $monitor ) { echo getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(), - (isset($options['width']) ? $options['width'] : reScale($monitor->Width(), $scale).'px' ), - ( isset($options['height']) ? $options['height'] : reScale($monitor->Height(), $scale).'px' ), + (isset($options['width']) ? $options['width'].'px' : reScale($monitor->ViewWidth(), $scale).'px' ), + ( isset($options['height']) ? $options['height'].'px' : reScale($monitor->ViewHeight(), $scale).'px' ), $monitor->Name() ); } else { @@ -251,17 +255,17 @@ foreach ( $monitors as $monitor ) { $width = $options['width']; if ( !$options['height'] ) { $scale = (int)( 100 * $options['width'] / $monitor->Width() ); - $height = reScale($monitor->Height(), $scale).'px'; + $height = reScale($monitor->Height(), $scale); } } else if ( $options['height'] ) { $height = $options['height']; if ( !$options['width'] ) { $scale = (int)( 100 * $options['height'] / $monitor->Height() ); - $width = reScale($monitor->Width(), $scale).'px'; + $width = reScale($monitor->Width(), $scale); } } else if ( $scale ) { - $width = reScale($monitor->Width(), $scale).'px'; - $height = reScale($monitor->Height(), $scale).'px'; + $width = reScale($monitor->Width(), $scale); + $height = reScale($monitor->Height(), $scale); } $zones = array(); @@ -272,8 +276,8 @@ foreach ( $monitors as $monitor ) { limitPoints($row['Points'], 0, 0, $monitor->Width(), $monitor->Height()); } else { limitPoints($row['Points'], 0, 0, - ( $width ? $width-1 : $monitor->Width()-1 ), - ( $height ? $height-1 : $monitor->Height()-1 ) + ( $width ? $width-1 : $monitor->ViewWidth()-1 ), + ( $height ? $height-1 : $monitor->ViewHeight()-1 ) ); } $row['Coords'] = pointsToCoords($row['Points']); @@ -282,7 +286,7 @@ foreach ( $monitors as $monitor ) { } // end foreach Zone ?> - + '; diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index e1a77b874..76457dfbf 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -139,36 +139,37 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $
- - - - - - - - - - - - - + + + + + + + + + + + + +
@@ -379,7 +379,7 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ $result = $dbConn->query('SELECT * FROM `Config` ORDER BY `Id` ASC'); if ( !$result ) echo mysql_error(); - while( $row = dbFetchNext($result) ) { + while ( $row = dbFetchNext($result) ) { $config[$row['Name']] = $row; if ( !($configCat = &$configCats[$row['Category']]) ) { $configCats[$row['Category']] = array(); diff --git a/web/skins/classic/views/settings.php b/web/skins/classic/views/settings.php index 7a19c00cf..b594cdc0f 100644 --- a/web/skins/classic/views/settings.php +++ b/web/skins/classic/views/settings.php @@ -26,12 +26,14 @@ $monitor = ZM\Monitor::find_one(array('Id'=>$_REQUEST['mid'])); $zmuCommand = getZmuCommand(' -m '.escapeshellarg($_REQUEST['mid']).' -B -C -H -O'); $zmuOutput = exec( $zmuCommand ); -list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput); +if ( $zmuOutput ) { + list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput); -$monitor->Brightness() = $brightness; -$monitor->Contrast() = $contrast; -$monitor->Hue() = $hue; -$monitor->Colour() = $colour; + $monitor->Brightness($brightness); + $monitor->Contrast($contrast); + $monitor->Hue($hue); + $monitor->Colour($colour); +} $focusWindow = true; @@ -47,23 +49,23 @@ xhtmlHeaders(__FILE__, validHtmlStr($monitor->Name()).' - '.translate('Settings' -
+
- + - + - + - +
disabled="disabled"/> disabled="disabled" />
/> />
/> />
/> />
diff --git a/web/skins/classic/views/shutdown.php b/web/skins/classic/views/shutdown.php index 5e032cd1c..1fbe838c1 100644 --- a/web/skins/classic/views/shutdown.php +++ b/web/skins/classic/views/shutdown.php @@ -33,15 +33,23 @@ xhtmlHeaders(__FILE__, translate('Shutdown').' '.translate('Restart'));

+ZM_PATH_SHUTDOWN is not defined. This is normally configured in /etc/zm/conf.d/01-system-paths.conf
'; + } else if ( !file_exists(ZM_PATH_SHUTDOWN) ) { + echo '
Path does not exist for ZM_PATH_SHUTDOWN. Current value is '.ZM_PATH_SHUTDOWN.'
'; + } else { +?> +
'.implode('
', $output).'

'; - } - if ( isset($_POST['when']) and ($_POST['when'] != 'NOW') and ($action != 'cancel') ) { - echo '

You may cancel this shutdown by clicking '.translate('Cancel').'

'; - } + if ( isset($output) ) { + echo '

'.implode('
', $output).'

'; + } + if ( isset($_POST['when']) and ($_POST['when'] != 'NOW') and ($action != 'cancel') ) { + echo '

You may cancel this shutdown by clicking '.translate('Cancel').'

'; + } ?>

Warning

This command will either shutdown or restart all ZoneMinder Servers
@@ -52,17 +60,20 @@ xhtmlHeaders(__FILE__, translate('Shutdown').' '.translate('Restart'));

+
diff --git a/web/skins/classic/views/user.php b/web/skins/classic/views/user.php index 78c3e206b..320c6bf64 100644 --- a/web/skins/classic/views/user.php +++ b/web/skins/classic/views/user.php @@ -18,7 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -$selfEdit = ZM_USER_SELF_EDIT && $_REQUEST['uid'] == $user['Id']; +$selfEdit = ZM_USER_SELF_EDIT && ($_REQUEST['uid'] == $user['Id']); if ( !canEdit('System') && !$selfEdit ) { $view = 'error'; @@ -45,10 +45,12 @@ $nve = array( 'None'=>translate('None'), 'View'=>translate('View'), 'Edit'=>tran $bandwidths = array_merge( array( ''=>'' ), $bandwidth_options ); $langs = array_merge( array( ''=>'' ), getLanguages() ); -$sql = 'SELECT Id,Name FROM Monitors ORDER BY Sequence ASC'; +$sql = 'SELECT Id, Name FROM Monitors ORDER BY Sequence ASC'; $monitors = array(); -foreach( dbFetchAll($sql) as $monitor ) { - $monitors[] = $monitor; +foreach ( dbFetchAll($sql) as $monitor ) { + if ( visibleMonitor($monitor['Id']) ) { + $monitors[$monitor['Id']] = $monitor; + } } $focusWindow = true; @@ -61,11 +63,8 @@ xhtmlHeaders(__FILE__, translate('User').' - '.$newUser['Username']);

-
- - + - - + - + - + - + - + - + - + - + - + - + - +
/>
- +'multiple')); ?>
- +
diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 70e5f410a..9c3dfce80 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -67,7 +67,7 @@ xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); Type() == 'Local' ) { ?> -
Id(), 'zmSettings'.$monitor->Id(), 'settings', translate('Settings'), true, 'id="settingsLink"') ?>
+
UrlToIndex().'?view=settings&mid='.$monitor->Id().'&'.get_auth_relay(), 'zmSettings'.$monitor->Id(), 'settings', translate('Settings'), true, 'id="settingsLink"') ?>
diff --git a/web/skins/classic/views/zone.php b/web/skins/classic/views/zone.php index 6b5801744..0a2c9155b 100644 --- a/web/skins/classic/views/zone.php +++ b/web/skins/classic/views/zone.php @@ -18,12 +18,12 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView('Monitors') ) { +$mid = empty($_REQUEST['mid']) ? 0 : validInt($_REQUEST['mid']); +if ( !($mid and canEdit('Monitors', $mid)) ) { $view = 'error'; return; } -$mid = validInt($_REQUEST['mid']); $zid = (!empty($_REQUEST['zid'])) ? validInt($_REQUEST['zid']) : 0; $scale = SCALE_BASE; @@ -174,29 +174,36 @@ xhtmlHeaders(__FILE__, translate('Zone')); 'applyZoneType()')); ?> + array('data-on-change'=>'applyZoneType')); ?> 'applyPreset()', 'onblur'=>'this.selectedIndex=0') ) + array('data-on-change'=>'applyPreset', 'onblur'=>'this.selectedIndex=0') ) ?> - 'applyZoneUnits()') ) ?> + +'applyZoneUnits') + ); + # Used later for number inputs + $step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : ''; +?> + - + / - + / - + @@ -205,45 +212,45 @@ xhtmlHeaders(__FILE__, translate('Zone')); - - + + - - + + - + - - + min="0"/> + min="0"/> - - + min="0"/> + min="0"/> - - + min="0"/> + min="0"/> - - + + - + - + diff --git a/web/skins/classic/views/zones.php b/web/skins/classic/views/zones.php index f8f758f46..4d03f03de 100644 --- a/web/skins/classic/views/zones.php +++ b/web/skins/classic/views/zones.php @@ -18,12 +18,12 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView('Monitors') ) { +$mid = empty($_REQUEST['mid']) ? 0 : validInt($_REQUEST['mid']); +if ( !($mid and canEdit('Monitors', $mid)) ) { $view = 'error'; return; } -$mid = validInt($_REQUEST['mid']); $monitor = new ZM\Monitor($mid); # ViewWidth() and ViewHeight() are already rotated $minX = 0; @@ -62,7 +62,7 @@ xhtmlHeaders(__FILE__, translate('Zones')); -