diff --git a/.gitignore b/.gitignore index ec021d210..926f6da37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ configure config.h.in +config.h.in~ autom4te.cache aclocal.m4 depcomp @@ -12,18 +13,23 @@ scripts/ZoneMinder/blib Makefile.in Makefile docs/_build - +compile config.guess config.h config.log config.status config.sub db/zm_create.sql +libtool +ltmain.sh +m4/* misc/apache.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules misc/logrotate.conf misc/syslog.conf +misc/plugins.conf +scripts/ZoneMinder/MYMETA.json scripts/ZoneMinder/MYMETA.yml scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm @@ -49,7 +55,11 @@ scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl src/.deps/ +src/.libs/ +src/*.la +src/*.lo src/*.o +src/libzmplugins.pc src/zm_config.h src/zm_config_defines.h src/zma diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cc0cc127..a120ef02c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ # cmake_minimum_required (VERSION 2.6) project (zoneminder) -set(zoneminder_VERSION "1.28.0") +set(zoneminder_VERSION "1.28.0wps1") # CMake does not allow out-of-source build if CMakeCache.exists in the source folder. Abort and notify the user to save him from headache why it doesn't work. if((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) diff --git a/Makefile.am b/Makefile.am index 84c18bcd4..8a709ecaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,7 @@ AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 + # And these to the user and group of your webserver webuser = @WEB_USER@ webgroup = @WEB_GROUP@ diff --git a/bootstrap.sh b/bootstrap.sh index 681d5b319..1c27b6d2b 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,4 +1,5 @@ #!/bin/bash +libtoolize aclocal autoheader automake --add-missing diff --git a/configure.ac b/configure.ac index 545106a7d..eb3763214 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,9 @@ AC_PREREQ(2.59) -AC_INIT(zm,1.28.0,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) +AC_INIT(zm,1.28.0wps1,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE +LT_PREREQ([2.4.2]) +LT_INIT([dlopen]) +AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) @@ -197,6 +200,50 @@ AC_ARG_WITH(cgidir, ) AC_SUBST(CGI_PREFIX) +ENABLE_PLUGIN_SUPPORT=no +AC_ARG_ENABLE([plugin-support], + [ --enable-plugin-support= enable or disable plugin support, default disabled], + [ENABLE_PLUGIN_SUPPORT=$enable_plugin_support], + [AC_MSG_WARN([You can call configure with the --enable-plugin-support= + or --disable-plugin-support option. + This tells configure whether to compile ZoneMinder with plugin support. + e.g. --enable-plugin-support=yes or --disable-plugin-support])] +) + +if test "$ENABLE_PLUGIN_SUPPORT" == "yes"; then + if test "$enable_shared" = "no"; then + AC_MSG_ERROR([Plugin support is enabled and shared library are disabled. + If you really want plugin support you must not call configure with + --enable-shared=no or --disabled-shared]) + fi + AC_ARG_WITH(pluginsdir, + [ --with-pluginsdir= plugins directory (requires --enable-plugin-support=yes)], + [PLUGINSLIBDIR=$with_pluginsdir], + AC_MSG_ERROR([Plugin support is enabled. + You must call configure with the --with-pluginsdir options. + This tells configure where to install plugin library files. + e.g. --with-pluginsdir=/usr/lib/zoneminder/plugins]) + ) + AC_SUBST(PLUGINSLIBDIR) + PLUGINSCONFDIR="${sysconfdir}/plugins.d" + AC_ARG_WITH(pluginsconfdir, + [ --with-pluginsconfdir= directory of plugin configuration files (requires --enable-plugin-support=yes)], + [PLUGINSCONFDIR=$with_pluginsconfdir], + AC_MSG_WARN([You can call configure with the --with-pluginsconfdir options. + This tells configure where to install the plugin configuration files. + The default is "${PLUGINSCONFDIR}". + e.g. --with-pluginsconfdir=/etc/zm/plugins.d]) + ) + AC_SUBST(PLUGINSCONFDIR) + PLUGINSWEBDIR="$WEB_PREFIX/plugins" + AC_SUBST(PLUGINSWEBDIR) + AC_DEFINE(ZM_PLUGINS_ON,1,"Whether plugin support is switched on and compiled") + PLUGINSEXT=".so" + AC_SUBST(PLUGINSEXT) + AC_DEFINE_UNQUOTED(DEFAULT_PLUGIN_EXT,"${PLUGINSEXT}",[File extension to detect plugins]) +fi +AM_CONDITIONAL([ZM_HAS_PLUGIN_SUPPORT], [test "x$ENABLE_PLUGIN_SUPPORT" = xyes]) + WEB_USER=apache AC_ARG_WITH(webuser, [ --with-webuser= name of web user, default apache], @@ -269,7 +316,6 @@ AC_PROG_CXX AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S -AC_PROG_RANLIB AC_PROG_MAKE_SET # Checks for typedefs, structures, and compiler characteristics. @@ -311,10 +357,18 @@ fi AC_SEARCH_LIBS(mysql_init,[mysqlclient mariadbclient],,AC_MSG_ERROR(zm requires libmysqlclient.a or libmariadbclient.a)) AC_CHECK_LIB(jpeg,jpeg_start_compress,,AC_MSG_ERROR(zm requires libjpeg.a)) AC_CHECK_LIB(pthread,pthread_create,,AC_MSG_ERROR(zm requires libpthread.a)) -AC_CHECK_LIB(dl,dlsym,,AC_MSG_ERROR(zm requires libdl.a)) +AC_CHECK_LIB([dl], + [dlsym], + [AC_SUBST([DL_LIBS],["-ldl"])AC_DEFINE([HAVE_LIBDL],[1],[Define to 1 if you have the 'dl' library (-ldl).])], + [AC_MSG_ERROR(zm requires libdl.a)] +) if test "$ZM_SSL_LIB" == "openssl"; then AC_CHECK_HEADERS(openssl/md5.h,,AC_MSG_WARN(zm requires openssl/md5.h header to be installed for openssl),) -AC_CHECK_LIB(crypto,MD5,,AC_MSG_WARN([libcrypto.a is required for authenticated streaming - use ZM_SSL_LIB option to select gnutls instead])) +AC_CHECK_LIB([crypto], + [MD5], + [AC_SUBST([CRYPTO_LIBS],["-lcrypto"])AC_DEFINE([HAVE_LIBCRYPTO],[1],[Define to 1 if you have the 'crypto' library (-lcrypto).])], + [AC_MSG_WARN([libcrypto.a is required for authenticated streaming - use ZM_SSL_LIB option to select gnutls instead])] +) else AC_CHECK_HEADERS(gnutls/openssl.h,AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,1),AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,0),) AC_CHECK_HEADERS(gnutls/gnutls.h,AC_SUBST(ZM_HAS_GNUTLS,1),AC_SUBST(ZM_HAS_GNUTLS,0),) @@ -322,28 +376,80 @@ if test "$ZM_HAS_GNUTLS_OPENSSL" == "0" && test "$ZM_HAS_GNUTLS" == "0"; then AC_MSG_WARN(gnutls is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead) fi AC_CHECK_HEADERS(gcrypt.h,,AC_MSG_WARN(zm requires libgcrypt headers to be installed for gnutls),) -AC_CHECK_LIB(gcrypt,gcry_check_version,,AC_MSG_WARN([libgcrypt.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) -AC_CHECK_LIB(gnutls,gnutls_fingerprint,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) +AC_CHECK_LIB([gcrypt], + [gcry_check_version], + [AC_SUBST([GCRYPT_LIBS],["-lgcrypt"])AC_DEFINE([HAVE_LIBGCRYPT],[1],[Define to 1 if you have the 'gcrypt' library (-lgcrypt).])], + [AC_MSG_WARN([libgcrypt.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])] +) +AC_CHECK_LIB([gnutls], + [gnutls_fingerprint], + [AC_SUBST([GNUTLS_LIBS],["-lgnutls"])AC_DEFINE([HAVE_LIBGNUTLS],[1],[Define to 1 if you have the 'gnutls' library (-lgnutls).])], + [AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])] +) if test "$ZM_HAS_GNUTLS_OPENSSL" == "1"; then -AC_CHECK_LIB(gnutls-openssl,MD5,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) +AC_CHECK_LIB([gnutls-openssl], + [MD5], + [AC_SUBST([GNUTLS_OPENSSL_LIBS],["-lgnutls-openssl"])AC_DEFINE([HAVE_LIBGNUTLS_OPENSSL],[1],[Define to 1 if you have the 'gnutls-openssl' library (-lgnutls-openssl).])], + [AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])] +) fi fi -AC_CHECK_LIB(pcre,pcre_compile,,AC_MSG_WARN(libpcre.a may be required for remote/network camera support)) +AC_CHECK_LIB([pcre], + [pcre_compile], + [AC_SUBST([PCRE_LIBS],["-lpcre"])AC_DEFINE([HAVE_LIBPCRE],[1],[Define to 1 if you have the 'pcre' library (-lpcre).])], + [AC_MSG_WARN([libpcre.a may be required for remote/network camera support])] +) AC_CHECK_LIB(z,zlibVersion) -AC_CHECK_LIB(x264,x264_predict_16x16_init) -AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming)) +AC_CHECK_LIB([x264], + [x264_predict_16x16_init], + [AC_SUBST([X264_LIBS],["-lx264"])AC_DEFINE([HAVE_LIBX264],[1],[Define to 1 if you have the 'x264' library (-lx264).])], +) +AC_CHECK_LIB([avutil], + [av_malloc], + [AC_SUBST([AVUTIL_LIBS],["-lavutil"])AC_DEFINE([HAVE_LIBAVUTIL],[1],[Define to 1 if you have the 'avutil' library (-lavutil).])], + [AC_MSG_WARN([libavutil.a may be required for MPEG streaming])] +) # Don't bother to warn about this one -AC_CHECK_LIB(avcore,av_image_copy,,) -AC_CHECK_LIB(avcodec,avcodec_version,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) -AC_CHECK_LIB(avformat,avformat_version,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) +AC_CHECK_LIB([avcore], + [av_image_copy], + [AC_SUBST([AVCORE_LIBS],["-lavcore"])AC_DEFINE([HAVE_LIBAVCORE],[1],[Define to 1 if you have the 'avcore' library (-lavcore).])] +) +AC_CHECK_LIB([avcodec], + [avcodec_version], + [AC_SUBST([AVCODEC_LIBS],["-lavcodec"])AC_DEFINE([HAVE_LIBAVCODEC],[1],[Define to 1 if you have the 'avcodec' library (-lavcodec).])], + [AC_MSG_WARN([libavcodec.a is required for MPEG streaming])] +) +AC_CHECK_LIB([avformat], + [avformat_version], + [AC_SUBST([AVFORMAT_LIBS],["-lavformat"])AC_DEFINE([HAVE_LIBAVFORMAT],[1],[Define to 1 if you have the 'avformat' library (-lavformat).])], + [AC_MSG_WARN([libavformat.a is required for MPEG streaming])] +) #AC_CHECK_LIB(avcodec,avcodec_open,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) #AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) -AC_CHECK_LIB(avdevice,avdevice_register_all,,AC_MSG_WARN(libavdevice.a may be required for MPEG streaming)) -AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) -AC_CHECK_LIB(vlc,libvlc_new,,AC_MSG_WARN(libvlc.a may be required for streaming)) -AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg)) +AC_CHECK_LIB([avdevice], + [avdevice_register_all], + [AC_SUBST([AVDEVICE_LIBS],["-lavdevice"])AC_DEFINE([HAVE_LIBAVDEVICE],[1],[Define to 1 if you have the 'avdevice' library (-lavdevice).])], + [AC_MSG_WARN([libavdevice.a may be required for MPEG streaming])] +) +AC_CHECK_LIB([swscale], + [sws_scale], + [AC_SUBST([SWSCALE_LIBS],["-lswscale"])AC_DEFINE([HAVE_LIBSWSCALE],[1],[Define to 1 if you have the 'swscale' library (-lswscale).])] +) +AC_CHECK_LIB([vlc], + [libvlc_new], + [AC_SUBST([VLC_LIBS],["-lvlc"])AC_DEFINE([HAVE_LIBVLC],[1],[Define to 1 if you have the 'vlc' library (-lvlc).])], + [AC_MSG_WARN([libvlc.a may be required for streaming])] +) +AC_CHECK_LIB([bz2], + [BZ2_bzCompress], + [AC_SUBST([BZ2_LIBS],["-lbz2"])AC_DEFINE([HAVE_LIBBZ2],[1],[Define to 1 if you have the 'bz2' library (-lbz2).])], + [AC_MSG_WARN([zm requires libbz2.a for recent versions of ffmpeg])] +) AC_CHECK_LIB(z,compress,,) -AC_CHECK_LIB(curl,curl_global_init,,) +AC_CHECK_LIB([curl], + [curl_global_init], + [AC_SUBST([CURL_LIBS],["-lcurl"])AC_DEFINE([HAVE_LIBCURL],[1],[Define to 1 if you have the 'curl' library (-lcurl).])] +) # Checks for header files. AC_FUNC_ALLOCA @@ -452,7 +558,7 @@ AC_SUBST(PERL_MM_PARMS) AC_SUBST(EXTRA_PERL_LIB) -AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql misc/Makefile misc/apache.conf misc/logrotate.conf misc/syslog.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmcamtool.pl scripts/zmsystemctl.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/css/classic/Makefile web/skins/classic/css/classic/views/Makefile web/skins/classic/css/flat/Makefile web/skins/classic/css/flat/views/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile]) +AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql misc/Makefile misc/apache.conf misc/logrotate.conf misc/syslog.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules misc/plugins.conf scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmcamtool.pl scripts/zmsystemctl.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h src/libzmplugins.pc web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/css/classic/Makefile web/skins/classic/css/classic/views/Makefile web/skins/classic/css/flat/Makefile web/skins/classic/css/flat/views/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile]) # Create the definitions for compilation and defaults for the database AC_CONFIG_COMMANDS([src/zm_config_defines.h],[perl ./zmconfgen.pl]) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 675e8866d..cdb2e8aab 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -377,6 +377,7 @@ CREATE TABLE `Monitors` ( `SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `WebColour` varchar(32) NOT NULL default 'red', `Sequence` smallint(5) unsigned default NULL, + `DoNativeMotDet` tinyint(3) unsigned NOT NULL default '1', PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -516,6 +517,25 @@ CREATE TABLE `Zones` ( KEY `MonitorId` (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; +DROP TABLE IF EXISTS `PluginsConfig`; +CREATE TABLE `PluginsConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `Name` varchar(32) NOT NULL DEFAULT '', + `Value` text NOT NULL, + `Type` tinytext NOT NULL, + `Choices` text default NULL, + `Min` int(10) unsigned NULL default NULL, + `Max` int(10) unsigned NULL default NULL, + `MonitorId` int(10) unsigned NOT NULL, + `ZoneId` int(10) unsigned NOT NULL, + `pluginName` varchar(64) NOT NULL, + PRIMARY KEY (`Id`), + KEY `ZoneId` (`ZoneId`), + KEY `MonitorId` (`MonitorId`), + KEY `Name` (`Name`), + KEY `pluginName` (`pluginName`) +) ENGINE=@ZM_MYSQL_ENGINE@; + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/db/zm_update-1.28.1.sql b/db/zm_update-1.28.1.sql index b7204905e..1bfa8af25 100644 --- a/db/zm_update-1.28.1.sql +++ b/db/zm_update-1.28.1.sql @@ -1 +1,21 @@ ALTER TABLE Monitors MODIFY Device tinytext; + +CREATE TABLE `PluginsConfig` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `Name` varchar(32) NOT NULL DEFAULT '', + `Value` text NOT NULL, + `Type` tinytext NOT NULL, + `Choices` text default NULL, + `Min` int(10) unsigned NULL default NULL, + `Max` int(10) unsigned NULL default NULL, + `MonitorId` int(10) unsigned NOT NULL, + `ZoneId` int(10) unsigned NOT NULL, + `pluginName` varchar(255) NOT NULL, + PRIMARY KEY (`Id`), + KEY `ZoneId` (`ZoneId`), + KEY `MonitorId` (`MonitorId`), + KEY `Name` (`Name`), + KEY `pluginName` (`pluginName`) +) ENGINE=InnoDB; + +ALTER TABLE `Monitors` ADD `DoNativeMotDet` tinyint(3) unsigned NOT NULL default '1' AFTER `Sequence`; diff --git a/distros/debian/control b/distros/debian/control index 81ed17252..718112fb8 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -23,9 +23,7 @@ Description: A video camera security and surveillance solution Package: zoneminder-dbg Architecture: any -Depends: - zoneminder (= ${binary:Version}), - ${misc:Depends} +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} Description: debugging syumbols for zoneminder. ZoneMinder is a video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security @@ -38,3 +36,11 @@ Description: debugging syumbols for zoneminder. 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: zoneminder-dev +Section: libdevel +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Development files for ZoneMinder plugins + This package add the necessary files to develop video analysis plugins for + the ZoneMinder camera security and surveillance solution. diff --git a/distros/debian/zoneminder-dev.install b/distros/debian/zoneminder-dev.install new file mode 100644 index 000000000..2bc6d91ab --- /dev/null +++ b/distros/debian/zoneminder-dev.install @@ -0,0 +1,3 @@ +usr/include/zoneminder/* +usr/lib/*/libzmplugins.a +usr/lib/*/pkgconfig/libzmplugins.pc diff --git a/distros/debian8/changelog b/distros/debian8/changelog index 410e17821..9831b352d 100644 --- a/distros/debian8/changelog +++ b/distros/debian8/changelog @@ -1,3 +1,10 @@ +zoneminder (1.28.1-0.1) testing; urgency=medium + + * Non-maintainer upload. + * Add plugin support + + -- Emmanuel Papin Sat, 13 Dec 2014 21:43:14 +0100 + zoneminder (1.28.0-0.2) testing; urgency=medium * Non-maintainer upload. diff --git a/distros/debian8/control b/distros/debian8/control index 613abe30b..cc13248c8 100644 --- a/distros/debian8/control +++ b/distros/debian8/control @@ -38,3 +38,12 @@ Description: Debugging symbols for zoneminder. 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: zoneminder-dev +Priority: optional +Section: libdevel +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Development files for ZoneMinder plugins + This package add the necessary files to develop video analysis plugins for + the ZoneMinder camera security and surveillance solution. diff --git a/distros/debian8/rules b/distros/debian8/rules index 4e97df736..92e4ecfae 100755 --- a/distros/debian8/rules +++ b/distros/debian8/rules @@ -53,7 +53,8 @@ override_dh_auto_configure: --with-mariadb=/usr --with-webdir=/usr/share/zoneminder \ --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \ --with-webuser=www-data --with-webgroup=www-data \ - --enable-crashtrace=no --enable-mmap=yes $(DEBOPT) + --enable-crashtrace=no --enable-mmap=yes $(DEBOPT) \ + --enable-plugin-support --with-pluginsdir=/usr/lib/zoneminder/plugins override_dh_clean: # Add here commands to clean up after the build process. diff --git a/distros/debian8/zoneminder-dev.install b/distros/debian8/zoneminder-dev.install new file mode 100644 index 000000000..6327b3321 --- /dev/null +++ b/distros/debian8/zoneminder-dev.install @@ -0,0 +1,3 @@ +usr/include/zoneminder +usr/lib/*/libzmplugins.a +usr/lib/*/pkgconfig/libzmplugins.pc diff --git a/misc/Makefile.am b/misc/Makefile.am index b854d66f6..53fe3a780 100644 --- a/misc/Makefile.am +++ b/misc/Makefile.am @@ -6,7 +6,8 @@ EXTRA_DIST = \ syslog.conf.in \ zoneminder.service.in \ com.zoneminder.systemctl.policy.in \ - com.zoneminder.systemctl.rules.in + com.zoneminder.systemctl.rules.in \ + plugins.conf.in polkit_actiondir = @POLKIT_PREFIX@/share/polkit-1/actions dist_polkit_action_DATA = com.zoneminder.systemctl.policy @@ -14,3 +15,5 @@ dist_polkit_action_DATA = com.zoneminder.systemctl.policy polkit_rulesdir = @POLKIT_PREFIX@/share/polkit-1/rules.d dist_polkit_rules_DATA = com.zoneminder.systemctl.rules +sysconf_DATA = plugins.conf + diff --git a/misc/plugins.conf.in b/misc/plugins.conf.in new file mode 100644 index 000000000..3aff36a1e --- /dev/null +++ b/misc/plugins.conf.in @@ -0,0 +1,13 @@ +# Zoneminder global configuration file for plugins +# Add here a section for each plugins or add separated configuration files +# under @PLUGINSCONFDIR@ +# +# Syntax: +# [plugin_name_A] +# parameter1 = value1 +# parameter2 = value2 +# ... +# [plugin_name_B] +# parameter1 = value1 +# parameter2 = value2 +# ... diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 54bd14e26..8ca336622 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -1926,6 +1926,54 @@ body = "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% s type => $types{string}, category => "eyeZm", }, + { + name => "ZM_PATH_PLUGINS", + default => "@PLUGINSLIBDIR@", + description => "Path to the plugin folder", + help => "3d-party plugins have to be placed here.", + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PLUGIN_EXTENSION", + default => "@PLUGINSEXT@", + description => "Default extension of plugins to found.", + help => "Default extension of plugins to found.", + type => $types{rel_path}, + category => "paths", + }, + { + name => "ZM_PLUGINS_CONFIG_PATH", + default => "@SYSCONFDIR@/plugins.conf", + description => "Path to the config file for plugins.", + help => "Path to the config file for plugins.", + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PLUGINS_CONFIG_DIR", + default => "@PLUGINSCONFDIR@", + description => "Path to the config folder for plugins.", + help => "Path to the config folder for plugins.", + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_LOAD_PLUGINS", + default => "no", + description => "Load and use 3d-party plugins", + help => "3d-party plugins will be loaded and used for analysing.", + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_TURNOFF_NATIVE_ANALYSIS", + default => "no", + description => "Turn native ZM\'s image analysis possibility off", + help => "Image analysis with ZM\'s motion detected function will be turned off. Only detection functions from loaded plugins will be used. Note, that if no plugins have be loaded, no detection will be done.", + type => $types{boolean}, + category => "config", + }, ); our %options_hash = map { ( $_->{name}, $_ ) } @options; diff --git a/src/Makefile.am b/src/Makefile.am index 9314daac0..68b67d4ce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,9 @@ AUTOMAKE_OPTIONS = gnu AM_CPPFLAGS = @MYSQL_CFLAGS@ @MARIADB_CFLAGS@ @FFMPEG_CFLAGS@ -Wall -finline-functions -fomit-frame-pointer -#AM_CXXFLAGS = -frepo +# Compile zm binaries with -rdynamic to enable visiblily of global static +# variables in shared libraries (plugins) +AM_CXXFLAGS = -rdynamic CLEANFILES = *.rpo @@ -24,24 +26,18 @@ zm_SOURCES = \ zm_buffer.cpp \ zm_camera.cpp \ zm_comms.cpp \ - zm_config.cpp \ zm_coord.cpp \ zm_curl_camera.cpp \ zm.cpp \ - zm_db.cpp \ - zm_logger.cpp \ zm_event.cpp \ zm_exception.cpp \ zm_file_camera.cpp \ zm_ffmpeg_camera.cpp \ - zm_image.cpp \ - zm_jpeg.cpp \ zm_libvlc_camera.cpp \ zm_local_camera.cpp \ zm_monitor.cpp \ zm_ffmpeg.cpp \ zm_mpeg.cpp \ - zm_poly.cpp \ zm_regexp.cpp \ zm_remote_camera.cpp \ zm_remote_camera_http.cpp \ @@ -55,13 +51,47 @@ zm_SOURCES = \ zm_sdp.cpp \ zm_signal.cpp \ zm_stream.cpp \ - zm_thread.cpp \ zm_time.cpp \ zm_timer.cpp \ zm_user.cpp \ + $(SOURCES_common) + +# These source files are used both for zm binaries and the libzmplugins +# convenience library +SOURCES_common = \ + zm_config.cpp \ + zm_db.cpp \ + zm_image.cpp \ + zm_jpeg.cpp \ + zm_logger.cpp \ + zm_poly.cpp \ zm_utils.cpp \ + zm_thread.cpp \ zm_zone.cpp +# These header files are used both for zm binaries and the libzmplugins +# convenience library +HEADERS_common = \ + jinclude.h \ + zm.h \ + zm_box.h \ + zm_config_defines.h \ + zm_config.h \ + zm_coord.h \ + zm_db.h \ + zm_event.h \ + zm_ffmpeg.h \ + zm_image.h \ + zm_jpeg.h \ + zm_logger.h \ + zm_mem_utils.h \ + zm_mpeg.h \ + zm_poly.h \ + zm_rgb.h \ + zm_stream.h \ + zm_utils.h \ + zm_zone.h + zmc_SOURCES = zmc.cpp $(zm_SOURCES) zma_SOURCES = zma.cpp $(zm_SOURCES) zms_SOURCES = zms.cpp $(zm_SOURCES) @@ -69,40 +99,37 @@ zmu_SOURCES = zmu.cpp $(zm_SOURCES) zmf_SOURCES = zmf.cpp $(zm_SOURCES) zmstreamer_SOURCES = zmstreamer.cpp $(zm_SOURCES) +# These libraries are linked only to zm binaries in order to avoid detection +# of unwanted dependencies during plugin packaging +zm_LDADD = @DL_LIBS@ @PCRE_LIBS@ @CURL_LIBS@ @BZ2_LIBS@ @X264_LIBS@ \ + @SWSCALE_LIBS@ @AVFORMAT_LIBS@ @AVCODEC_LIBS@ @AVUTIL_LIBS@ \ + @AVDEVICE_LIBS@ @AVCORE_LIBS@ @VLC_LIBS@ @CRYPTO_LIBS@ @GCRYPT_LIBS@ \ + @GNUTLS_LIBS@ @GNUTLS_OPENSSL_LIBS@ + +zmc_LDADD = $(zm_LDADD) +zma_LDADD = $(zm_LDADD) +zms_LDADD = $(zm_LDADD) +zmu_LDADD = $(zm_LDADD) +zmf_LDADD = $(zm_LDADD) +zmstreamer_LDADD = $(zm_LDADD) + noinst_HEADERS = \ - jinclude.h \ - zm_box.h \ zm_buffer.h \ zm_camera.h \ zm_comms.h \ - zm_config_defines.h \ - zm_config.h \ - zm_coord.h \ zm_curl_camera.h \ - zm_db.h \ - zm_logger.h \ - zm_event.h \ zm_exception.h \ zmf.h \ zm_file_camera.h \ zm_ffmpeg_camera.h \ zm_font.h \ - zm_font.h \ - zm.h \ - zm_image.h \ - zm_jpeg.h \ zm_libvlc_camera.h \ zm_local_camera.h \ - zm_mem_utils.h \ zm_monitor.h \ - zm_ffmpeg.h \ - zm_mpeg.h \ - zm_poly.h \ zm_regexp.h \ zm_remote_camera.h \ zm_remote_camera_http.h \ zm_remote_camera_rtsp.h \ - zm_rgb.h \ zm_rtp_ctrl.h \ zm_rtp_data.h \ zm_rtp.h \ @@ -110,13 +137,48 @@ noinst_HEADERS = \ zm_rtsp.h \ zm_sdp.h \ zm_signal.h \ - zm_stream.h \ zm_thread.h \ zm_time.h \ zm_timer.h \ - zm_user.h \ - zm_utils.h \ - zm_zone.h + zm_user.h + +if ZM_HAS_PLUGIN_SUPPORT +# Add objects to zm binaries for plugins management +zm_SOURCES += \ + zm_detector.cpp \ + zm_image_analyser.cpp \ + zm_plugin.cpp \ + zm_plugin_manager.cpp + +# Build a convenience library for plugin development +noinst_LTLIBRARIES = libzmplugins.la +libzmplugins_la_SOURCES = zm_detector.cpp $(SOURCES_common) + +# A hack to avoid conflicts between objects created both with libtool +# and without (objects will be prefixed with "libzmplugins_la") +libzmplugins_la_CPPFLAGS = $(AM_CPPFLAGS) + +# Install the necessary headers for plugin development +pkginclude_HEADERS = \ + zm_detector.h \ + zm_image_analyser.h \ + zm_plugin.h \ + zm_plugin_manager.h \ + $(HEADERS_common) + +# Install a pkg-config file for a proper handling of the libzmplugins +# convenience library +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libzmplugins.pc +else +# Don't intall plugin headers if no support +noinst_HEADERS += \ + zm_detector.h \ + zm_plugin.h \ + zm_plugin_manager.h \ + zm_image_analyser.h \ + $(HEADERS_common) +endif EXTRA_DIST = \ zm_config.h.in \ @@ -126,10 +188,14 @@ dist-hook: @( rm $(distdir)/zm_config.h ) # Yes, you are correct. This is a HACK! +# And the manual installation of libzmplugins.a is necessary because libtool has +# no option to force installation of convenience libraries. install-exec-hook: ( cd $(DESTDIR)@bindir@; mkdir -p $(DESTDIR)$(cgidir); mv zms $(DESTDIR)$(cgidir) ) ( cd $(DESTDIR)$(cgidir); chown $(webuser):$(webgroup) zms; ln -f zms nph-zms ) + install -D -m 644 .libs/libzmplugins.a $(DESTDIR)$(libdir)/libzmplugins.a uninstall-hook: ( cd $(DESTDIR)$(cgidir); rm -f zms nph-zms ) + ( cd $(DESTDIR)$(libdir); rm -f libzmplugins.a ) diff --git a/src/libzmplugins.pc.in b/src/libzmplugins.pc.in new file mode 100644 index 000000000..46336af14 --- /dev/null +++ b/src/libzmplugins.pc.in @@ -0,0 +1,17 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=@libdir@ +pkglibdir = @PLUGINSLIBDIR@ +includedir=@includedir@ +sysconfdir = @PLUGINSCONFDIR@ +webdir = @PLUGINSWEBDIR@ + +Name: : ZoneMinder convenience library for plugin development. +Description: ZoneMinder convenience library for plugin development. +Version: @PACKAGE_VERSION@ +Requires: +Requires.private: +Conflicts: +Libs: -L${libdir} -lzmplugins @LIBS@ +Libs.private : +Cflags: -I${includedir}/@PACKAGE@ diff --git a/src/zm_config.h.in b/src/zm_config.h.in index 1df1edf16..e311ebdbc 100644 --- a/src/zm_config.h.in +++ b/src/zm_config.h.in @@ -42,6 +42,8 @@ #define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT) #define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS) +#define ZM_NOTES_MAX_SIZE 4096 // The maximum size of a note + #define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP #define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP diff --git a/src/zm_detector.cpp b/src/zm_detector.cpp new file mode 100644 index 000000000..c8d8dd5bf --- /dev/null +++ b/src/zm_detector.cpp @@ -0,0 +1,232 @@ +#include "zm_detector.h" + + + +/*!\fn Detector::Detector(const Detector& source) + * \param source is the object to copy + */ +Detector::Detector(const Detector& source) + : m_sDetectionCause(source.m_sDetectionCause), + m_fMinAlarmScore(source.m_fMinAlarmScore), + m_fMaxAlarmScore(source.m_fMaxAlarmScore), + m_fImageScaleFactor(source.m_fImageScaleFactor), + m_nNewWidth(source.m_nNewWidth), + m_nNewHeight(source.m_nNewHeight), + m_sLogPrefix(source.m_sLogPrefix), + m_sConfigSectionName(source.m_sConfigSectionName), + m_vnPluginZones(source.m_vnPluginZones) +{ + m_bIsPluginEnabled = false; +} + + + +/*!\fn Detector& ImageAnalyser::Detector::operator=(const ImageAnalyser::Detector& source) + * \param source is the object to copy + */ +Detector& Detector::operator=(const Detector& source) +{ + m_sDetectionCause = source.m_sDetectionCause; + m_fMinAlarmScore = source.m_fMinAlarmScore; + m_fMaxAlarmScore = source.m_fMaxAlarmScore; + m_fImageScaleFactor = source.m_fImageScaleFactor; + m_sLogPrefix = source.m_sLogPrefix; + m_nNewWidth = source.m_nNewWidth; + m_nNewHeight = source.m_nNewHeight; + m_sConfigSectionName = source.m_sConfigSectionName; + m_vnPluginZones = source.m_vnPluginZones; + + return *this; +} + + + +/*!\fn Detector::getDetectionCause() + * return detection cause as string + */ +string Detector::getDetectionCause() +{ + return m_sDetectionCause; +} + + +/*!\fn Detector::getConfigSectionName() + * return plugin name as string + */ +string Detector::getPluginName() +{ + return m_sConfigSectionName; +} + + +/*!\fn Detector::EnablePlugin(vector zoneList) + * \param vnZoneList is the list of enabled zones for the plugin + */ +void Detector::EnablePlugin(vector vnZoneList) +{ + m_vnPluginZones = vnZoneList; + m_bIsPluginEnabled = true; +} + + +/*!\fn Detector::getPluginZones() + * \return the list of zone which have the plugin enabled + */ +vector Detector::getPluginZones() +{ + return m_vnPluginZones; +} + + +/*! \fn Detector::log(int nLogLevel, string sLevel, string sMessage) + */ +void Detector::log(int nLogLevel, string sLevel, string sMessage) +{ + string sMessageToLog = sLevel + string(" [") + m_sLogPrefix + string(": ") + sMessage + string("]"); + syslog(nLogLevel, "%s", sMessageToLog.c_str()); +} + + + +/*! \fn int Detector::Detect(const Image &image, Event::StringSet &zoneSet) + * \param zmImage is an image to detect faces on + * \param zoneSet is set of zone names (see zm_zone.h) + * \param score is the detection score + * \return true if detection is effective + */ +bool Detector::Detect(const Image* zmImage, Zone** zones, Event::StringSet &zoneSet, unsigned int &score) +{ + bool alarm = false; + char szMessage[100]; + score = 0; + + if (!m_bIsPluginEnabled) return (alarm); + + // Check preclusive zones first + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsPreclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking preclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, *zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + + if ( alarm ) + { + alarm = false; + score = 0; + } + else + { + // Find all alarm pixels in active zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsActive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + if (checkZone(zone, *it, *zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + + if ( alarm ) + { + // Checking inclusive zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsInclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking inclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, *zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + } + else + { + // Find all alarm pixels in exclusive zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsExclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking exclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, *zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + } + } + + return alarm; +} diff --git a/src/zm_detector.h b/src/zm_detector.h new file mode 100644 index 000000000..cd21a2b8f --- /dev/null +++ b/src/zm_detector.h @@ -0,0 +1,137 @@ +#ifndef ZM_DETECTOR_H +#define ZM_DETECTOR_H + + +#include +#include +#include +#include + +#include "zm_image.h" +#include "zm_zone.h" +#include "zm_event.h" + +#define DEFAULT_DETECTION_CAUSE "Object Detected" +#define DEFAULT_MIN_ALARM_SCORE 1.0 +#define DEFAULT_MAX_ALARM_SCORE 99.0 +#define DEFAULT_IMAGE_SCALE_FACTOR 1.0 + +#define DEFAULT_LOG_PREFIX "ZM PLUGIN" +#define LOG_LEVEL LOG_NOTICE +#define DEFAULT_CONFIGFILE_SECTION "libzm_vscvl_plugin" + +using namespace std; + + +//! Base class for object detectors, defined in plugins. +class Detector +{ + +public: + + //! Destructor + virtual ~Detector() {} + + //! Default constructor + Detector() +{ + m_sLogPrefix = DEFAULT_LOG_PREFIX; + m_sDetectionCause = DEFAULT_DETECTION_CAUSE; + m_fMinAlarmScore = DEFAULT_MIN_ALARM_SCORE; + m_fMaxAlarmScore = DEFAULT_MAX_ALARM_SCORE; + m_fImageScaleFactor = DEFAULT_IMAGE_SCALE_FACTOR; + m_sConfigSectionName = DEFAULT_CONFIGFILE_SECTION; + m_nNewWidth = 0; + m_nNewHeight = 0; +} + + //! Constructor with section name parameter. + Detector(string sPluginFileName) +{ + m_sLogPrefix = DEFAULT_LOG_PREFIX; + + char* szPluginFileName = strdup(sPluginFileName.c_str()); + + string sPluginFileNameName = string(basename(szPluginFileName)); + + size_t idx = sPluginFileNameName.rfind('.'); + + if (idx == string::npos) + m_sConfigSectionName = sPluginFileNameName; + else + m_sConfigSectionName = sPluginFileNameName.substr(0, idx); + + m_sDetectionCause = DEFAULT_DETECTION_CAUSE; + m_fMinAlarmScore = DEFAULT_MIN_ALARM_SCORE; + m_fMaxAlarmScore = DEFAULT_MAX_ALARM_SCORE; + m_fImageScaleFactor = DEFAULT_IMAGE_SCALE_FACTOR; + m_nNewWidth = 0; + m_nNewHeight = 0; +} + + //! Copy constructor + Detector(const Detector& source); + + //! Assignment operator + Detector& operator=(const Detector& source); + + //! Detect (in an image later) + bool Detect(const Image* image, Zone** zones, Event::StringSet &zoneSet, unsigned int &score); + + //! Load detector's parameters. + virtual int loadConfig(string sConfigFileName, map > mapPluginConf) = 0; + + //! Returns detection case string. + string getDetectionCause(); + + //! Returns plugin name as string. + string getPluginName(); + + //! Enable the plugin for the given zones. + void EnablePlugin(vector zoneList); + + //! Return the list of enabled zones + vector getPluginZones(); + +protected: + + //! Do detection inside one given zone. + virtual bool checkZone(Zone *zone, unsigned int n_zone, const Image &zmImage) = 0; + + //! Log messages to the SYSLOG. + void log(int, string sLevel, string sMessage); + + //! String to be shown as detection cause for event. + string m_sDetectionCause; + + //! Minimum score value to consider frame as to be alarmed. + double m_fMinAlarmScore; + + //! Maximum score value to consider frame as to be alarmed. + double m_fMaxAlarmScore; + + //! Maximum allowed width of frame image. + double m_fImageScaleFactor; + + //! Width of image to resize. + int m_nNewWidth; + + //! Height of image to resize. + int m_nNewHeight; + + //! String prefix for SYSLOG messages. + string m_sLogPrefix; + + //! Name of config file section to search parameters. + string m_sConfigSectionName; + + //! List of zones enabled for the plugin + vector m_vnPluginZones; + + //! Plugin status regarding zone settings + bool m_bIsPluginEnabled; + +}; + + +#endif // ZM_DETECTOR_H diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 1c7e13e7d..910a1444a 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -63,6 +63,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string std::string notes; createNotes( notes ); + static char escapedNotes[ZM_NOTES_MAX_SIZE*2 + 1]; + mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); bool untimedEvent = false; if ( !start_time.tv_sec ) @@ -71,10 +73,10 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string gettimeofday( &start_time, 0 ); } - static char sql[ZM_SQL_MED_BUFSIZ]; + static char sql[ZM_SQL_LGE_BUFSIZ]; struct tm *stime = localtime( &start_time.tv_sec ); - snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); + snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), escapedNotes ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event: %s", mysql_error( &dbconn ) ); @@ -185,7 +187,7 @@ Event::~Event() struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + snprintf( sql, sizeof(sql), "update Events set Name='%s%d', Cause='%s', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, cause.c_str(), end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); @@ -198,16 +200,34 @@ void Event::createNotes( std::string ¬es ) notes.clear(); for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) { - notes += mapIter->first; - notes += ": "; + // No need to display the cause inside the note + //notes += mapIter->first; + //notes += ": "; const StringSet &stringSet = mapIter->second; for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) { - if ( setIter != stringSet.begin() ) - notes += ", "; + // Don't format here + //if ( setIter != stringSet.begin() ) + // notes += ", "; notes += *setIter; } } + if (notes.length() > ZM_NOTES_MAX_SIZE) + { + std::string sTrunc = "... (content truncated)"; + notes = notes.substr(0, (ZM_NOTES_MAX_SIZE - sTrunc.length())); + notes += sTrunc; + } +} + +void Event::AddCause( const std::string new_cause ) +{ + if ( cause.find( new_cause ) == std::string::npos ) + { + if ( cause.length() ) + cause += ", "; + cause += new_cause; + } } int Event::sd = -1; @@ -421,12 +441,12 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) std::string notes; createNotes( notes ); - Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); - static char sql[ZM_SQL_MED_BUFSIZ]; + Debug( 2, "Updating notes for event %d", id ); + static char sql[ZM_SQL_LGE_BUFSIZ]; #if USE_PREPARED_SQL static MYSQL_STMT *stmt = 0; - char notesStr[ZM_SQL_MED_BUFSIZ] = ""; + char notesStr[ZM_NOTES_MAX_SIZE] = ""; unsigned long notesLen = 0; if ( !stmt ) @@ -475,7 +495,7 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); } #else - static char escapedNotes[ZM_SQL_MED_BUFSIZ]; + static char escapedNotes[ZM_NOTES_MAX_SIZE*2 + 1]; mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); diff --git a/src/zm_event.h b/src/zm_event.h index f50a5bf06..980b4016d 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -132,6 +132,7 @@ public: void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); + void AddCause( const std::string new_cause ); private: void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); diff --git a/src/zm_image_analyser.cpp b/src/zm_image_analyser.cpp index 1445e9864..9d51b514e 100644 --- a/src/zm_image_analyser.cpp +++ b/src/zm_image_analyser.cpp @@ -1,6 +1,16 @@ #include "zm_image_analyser.h" - +ImageAnalyser::ImageAnalyser( int nMonitorId ) +{ + if ( nMonitorId > 0 ) + { + m_nMonitorId = nMonitorId; + m_bIsAnalyserEnabled = getMonitorZones(); + } + else + m_bIsAnalyserEnabled = false; + m_bIsNativeDetEnabled = false; +} /*!\fn ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) * \param source is the object to copy @@ -33,32 +43,445 @@ ImageAnalyser::~ImageAnalyser() -/*!\fn ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) +/*!\fn ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, Event::StringSetMap noteSetMap, string& det_cause) * \param comp_image is the image to analyse * \param zones is the zones array to analyse - * \param n_numZones is the number of zones * \param noteSetMap is the map of events descriptions * \param det_cause is a string describing detection cause + * \param score is the plugin score */ -int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) +bool ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, Event::StringSetMap& noteSetMap, string& det_cause, unsigned int& score) { Event::StringSet zoneSet; - int score = 0; + score = 0; + bool alarm = false; - for(DetectorsList::iterator It = m_Detectors.begin(); + for ( DetectorsList::iterator It = m_Detectors.begin(); It != m_Detectors.end(); - ++It) + ++It ) { - int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet); - if (detect_score) + unsigned int detect_score = 0; + if ( (*It)->Detect( &comp_image, zones, zoneSet, detect_score ) ) { + alarm = true; score += detect_score; - noteSetMap[(*It)->getDetectionCause()] = zoneSet; - if (det_cause.length()) - det_cause += ", "; - det_cause += (*It)->getDetectionCause(); + std::string new_cause = (*It)->getDetectionCause(); + noteSetMap[new_cause] = zoneSet; + if ( det_cause.find( new_cause ) == std::string::npos ) + { + if ( det_cause.length() ) + det_cause += ", "; + det_cause += new_cause; + } } } - return score; + return alarm; } + + +/*!\fn ImageAnalyser::configurePlugins(string sConfigFileName) + *\param sConfigFileName is the path to the configuration file, where parameters for all plugins are given. + * \param bDoNativeDet is true if native detection will be performed +*/ +void ImageAnalyser::configurePlugins(string sConfigFileName, bool bDoNativeDet) +{ + string sLoadedPlugins; + if ( !m_bIsAnalyserEnabled ) return; + m_bIsNativeDetEnabled = bDoNativeDet; + for ( DetectorsList::iterator It = m_Detectors.begin(); It != m_Detectors.end(); ++It ) + { + string sPluginName = (*It)->getPluginName(); + try + { + if ( isValidConfigFile( sPluginName, sConfigFileName ) ) + { + Info("Configure plugin '%s' with config file '%s'.", sPluginName.c_str(), sConfigFileName.c_str()); + map > mapPluginConf; + vector vnPluginZones; + bool plugEnabled = getEnabledZonesForPlugin( sPluginName, vnPluginZones ); + if ( getPluginConfig( sPluginName, vnPluginZones, mapPluginConf ) + && (*It)->loadConfig( sConfigFileName, mapPluginConf ) ) + { + mapRegPluginGenConf[sPluginName].Configured = true; + if ( plugEnabled ) + { + (*It)->EnablePlugin( vnPluginZones ); + if ( sLoadedPlugins.length() ) + sLoadedPlugins += ", "; + sLoadedPlugins += "'" + sPluginName + "'"; + } + } + } + } + catch(...) + { + Error("Plugin '%s' couldn't be loaded", sPluginName.c_str()); + } + } + getZonesConfig( sLoadedPlugins ); +} + + + +/*!\fn ImageAnalyser::isValidConfigFile(string sPluginName, string sConfigFileName) + * \param sPluginName is the name of the plugin (filename without extension) + * \param sConfigFileName is the path to the configuration file which should include configuration directives for the plugin + * \return true if the config file contains the right section name + */ +bool ImageAnalyser::isValidConfigFile(string sPluginName, string sConfigFileName) +{ + ifstream ifs(sConfigFileName.c_str()); + string line; + bool rtnVal = false; + while (getline(ifs, line)) + { + if (line == "[" + sPluginName + "]") + { + rtnVal = true; + break; + } + } + ifs.close(); + return rtnVal; +} + + +/*!\fn ImageAnalyser::getMonitorZones() + * \return true if at least a zone is configured for the monitor + */ +bool ImageAnalyser::getMonitorZones() +{ + static char sql[ZM_SQL_MED_BUFSIZ]; + + // We use the same ordering as in Monitor::Load + snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Type` FROM `Zones` WHERE `MonitorId` = %d ORDER BY `Type`, `Id`;", m_nMonitorId); + + if (mysql_query(&dbconn, sql)) + { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + MYSQL_RES *result = mysql_store_result(&dbconn); + if (!result) + { + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + if (mysql_num_rows(result) > 0) + { + for (unsigned int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) + { + if (mysql_errno(&dbconn)) + { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + zSetting zone; + zone.id = (unsigned int)strtoul(dbrow[0], NULL, 0); + zone.name = string(dbrow[1]); + zone.type = string(dbrow[2]); + m_vMonitorZones.push_back(zone); + } + } + mysql_free_result(result); + + return ( m_vMonitorZones.size() ); +} + + + +/*!\fn ImageAnalyser::getPluginConfig(string sPluginName, map >& mapPluginConf) + * \param sPluginName is the name of the plugin (filename without extension) + * \param vnPluginZones is a vector containing the index of zones enabled for the plugin (not the zone Id in the database) + * \param mapPluginConf is the map filled with configuration parameters for the plugin + * \return true if all found parameters are applied to the map + */ +bool ImageAnalyser::getPluginConfig(string sPluginName, vector vnPluginZones, map >& mapPluginConf) +{ + static char sql[ZM_SQL_MED_BUFSIZ]; + + // Get plugin configuration parameters from `PluginsConfig` table + snprintf(sql, sizeof(sql), "SELECT `ZoneId`, `Name`, `Value` FROM `PluginsConfig` WHERE `MonitorId`=%d AND `pluginName`='%s' ORDER BY `ZoneId` ASC;", m_nMonitorId, sPluginName.c_str()); + + if (mysql_query(&dbconn, sql)) + { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + MYSQL_RES *result = mysql_store_result(&dbconn); + if (!result) + { + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + size_t nParamCnt = 0; + size_t nParamNum = mysql_num_rows(result); + + if (nParamNum > 0) + { + vector vRows; + for (unsigned int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) + { + if (mysql_errno(&dbconn)) + { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + mysql_free_result(result); + exit(mysql_errno(&dbconn)); + } + vRows.push_back(dbrow); + } + // Iterate over the zones + for (size_t i = 0; i < m_vMonitorZones.size(); i++) + { + // Iterate over the configuration parameters + for (vector::iterator it = vRows.begin(); it != vRows.end(); it++) + { + // Add the parameter to the map if the zone id is found + if ( (unsigned int)strtoul((*it)[0], NULL, 0) == m_vMonitorZones[i].id ) + { + nParamCnt++; + string name((*it)[1]); + string value((*it)[2]); + if((name == "Enabled") && (value == "Yes")) { + mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].Enabled = true; + } else if((name == "RequireNatDet") && (value == "Yes")) { + mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].RequireNatDet = true; + } else if((name == "IncludeNatDet") && (value == "Yes")) { + mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].IncludeNatDet = true; + } else if((name == "ReInitNatDet") && (value == "Yes")) { + mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].ReInitNatDet = true; + } + // Keep only enabled zones in mapPluginConf + if (binary_search(vnPluginZones.begin(), vnPluginZones.end(), i)) { + mapPluginConf[i][name] = value; + } + } + } + if ( mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].Enabled + && mapRegPluginZoneConf[sPluginName][m_vMonitorZones[i].id].RequireNatDet + && !m_bIsNativeDetEnabled ) + Warning("Plugin '%s' will never enter in alarm because native detection is required but not enabled", sPluginName.c_str()); + } + } + mysql_free_result(result); + + return ( nParamNum == nParamCnt ); +} + + + +/*!\fn ImageAnalyser::getEnabledZonesForPlugin(string sPluginName, vector& vnPluginZones) + * \param sPluginName is the name of the plugin (filename without extension) + * \param vnPluginZones is the vector list filled with zones enabled for this plugin + * \return true if at least one active or exclusive zone exist + */ +bool ImageAnalyser::getEnabledZonesForPlugin(string sPluginName, vector& vnPluginZones) +{ + static char sql[ZM_SQL_MED_BUFSIZ]; + bool bPluginEnabled = false; + string sZones; + + // Get the sorted list of zones ids which have the plugin enabled + snprintf(sql, sizeof(sql), "SELECT `ZoneId` FROM `PluginsConfig` WHERE `MonitorId`=%d AND `pluginName`='%s' AND `Name`='Enabled' AND `Value`='yes' ORDER BY `ZoneId` ASC;", m_nMonitorId, sPluginName.c_str()); + + if (mysql_query( &dbconn, sql)) + { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + MYSQL_RES *result = mysql_store_result(&dbconn); + if (!result) + { + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + + if (mysql_num_rows(result) > 0) + { + vector vnEnabledZoneIds; + for (unsigned int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) + { + if (mysql_errno(&dbconn)) + { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + mysql_free_result(result); + exit(mysql_errno(&dbconn)); + } + vnEnabledZoneIds.push_back(atoi(dbrow[0])); + } + + // Iterate over the zones + for (size_t i = 0; i < m_vMonitorZones.size(); i++) + { + if (binary_search(vnEnabledZoneIds.begin(), vnEnabledZoneIds.end(), m_vMonitorZones[i].id)) + { + // Add the index to the vector if the zone id is found + vnPluginZones.push_back(i); + string sZoneType = m_vMonitorZones[i].type; + if ((sZoneType == "Active") || (sZoneType == "Exclusive")) + bPluginEnabled = true; + if ( sZones.length() ) + sZones += ", "; + sZones += m_vMonitorZones[i].name + " (" + sZoneType + ")"; + } + } + } + mysql_free_result(result); + + if (bPluginEnabled) + { + Info("Plugin '%s' is enabled for zone(s): %s", sPluginName.c_str(), sZones.c_str()); + } + else + { + Info("Plugin '%s' is disabled (not enabled for any active or exclusive zones)", sPluginName.c_str()); + } + return bPluginEnabled; +} + + +/*!\fn ImageAnalyser::getZonesConfig() + * \param sLoadedPlugins is the formatted list of loaded plugins + */ +bool ImageAnalyser::getZonesConfig(string sLoadedPlugins) +{ + static char sql[ZM_SQL_MED_BUFSIZ]; + + if ( !sLoadedPlugins.length() ) return false; + + // Get the sorted list of zones and which have a setting enabled + snprintf(sql, sizeof(sql), "SELECT DISTINCT `ZoneId`, `Name` FROM `PluginsConfig` WHERE `MonitorId` = %d AND `pluginName` IN (%s) AND `Name` IN ('RequireNatDet', 'IncludeNatDet', 'ReInitNatDet') AND `Value` = 'yes' ORDER BY `ZoneId` ASC;", m_nMonitorId, sLoadedPlugins.c_str()); + if (mysql_query(&dbconn, sql)) + { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + MYSQL_RES *result = mysql_store_result(&dbconn); + if (!result) + { + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); + } + if (mysql_num_rows(result) > 0) + { + vector vSettings; + for (unsigned int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) + { + if (mysql_errno(&dbconn)) + { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + mysql_free_result(result); + exit(mysql_errno(&dbconn)); + } + zIdName setting; + setting.zoneId = (unsigned int)strtoul(dbrow[0], NULL, 0); + setting.name = dbrow[1]; + vSettings.push_back(setting); + } + + // Iterate over the zones and add the index to the vector if the zone id is found + for (size_t i = 0; i != m_vMonitorZones.size(); i++) + { + zConf zoneConf; + for (vector::iterator it = vSettings.begin(); it != vSettings.end(); it++) + { + if (it->zoneId == m_vMonitorZones[i].id) + { + if (it->name == "RequireNatDet") + zoneConf.RequireNatDet = true; + else if (it->name == "IncludeNatDet") + zoneConf.IncludeNatDet = true; + else if (it->name == "ReInitNatDet") + zoneConf.ReInitNatDet = true; + } + } + m_vZonesConfig.push_back(zoneConf); + } + } + mysql_free_result(result); + + return true; +} + + +/*!\fn ImageAnalyser::getZoneConfig(int nZone, zConf& zoneConf) + * \param nZone is the zone index (not the id in sql database) + * \param zoneConf is a structure filled with the plugin settings of nZone + */ +bool ImageAnalyser::getZoneConfig(unsigned int nZone, zConf& zoneConf) +{ + if (nZone < m_vZonesConfig.size()) + zoneConf = m_vZonesConfig[nZone]; + else + return false; + return true; +} + + +/*!\fn ImageAnalyser::getRegPluginGenConf(string sPluginName, pGenConf& regPluginGenConf) + * \param sPluginName is the name of the plugin (filename without extension) + * \param regPluginGenConf is a structure filled with the general settings of the plugin + * \return false if no setting is found + */ +bool ImageAnalyser::getRegPluginGenConf(string sPluginName, pGenConf& regPluginGenConf) +{ + map::iterator it = mapRegPluginGenConf.find( sPluginName ); + if ( it == mapRegPluginGenConf.end() ) + return false; + regPluginGenConf = it->second; + return true; +} + + +/*!\fn ImageAnalyser::getRegPluginZoneConf(string sPluginName, PluginZoneConf& regPluginZoneConf) + * \param sPluginName is the name of the plugin (filename without extension) + * \param regPluginZoneConf is a map filled with the zone settings of the plugin + */ +void ImageAnalyser::getRegPluginZoneConf(string sPluginName, PluginZoneConf& regPluginZoneConf) +{ + map::iterator it = mapRegPluginZoneConf.find( sPluginName ); + + if ( it != mapRegPluginZoneConf.end() ) + regPluginZoneConf = it->second; + + pZoneConf empty; + + for (size_t i = 0; i != m_vMonitorZones.size(); i++) + { + PluginZoneConf::iterator it2 = regPluginZoneConf.find( m_vMonitorZones[i].id ); + if ( it2 == regPluginZoneConf.end() ) + regPluginZoneConf[m_vMonitorZones[i].id] = empty; + } +} + +void ImageAnalyser::cleanupPlugins() +{ + + string sPluginsToKeep; + string sRequest; + static char sql[ZM_SQL_MED_BUFSIZ]; + + for ( DetectorsList::iterator It = m_Detectors.begin(); It != m_Detectors.end(); ++It ) + { + if ( sPluginsToKeep.length() ) + sPluginsToKeep += ", "; + sPluginsToKeep += "'" + (*It)->getPluginName() + "'"; + } + + if ( sPluginsToKeep.length() ) + sRequest = " AND `pluginName` NOT IN (" + sPluginsToKeep + ")"; + + snprintf(sql, sizeof(sql), "DELETE FROM `PluginsConfig` WHERE `MonitorId` = %d%s;", m_nMonitorId, sRequest.c_str()); + + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't delete plugint: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } +} diff --git a/src/zm_image_analyser.h b/src/zm_image_analyser.h index d1d0c1da0..eb516653f 100644 --- a/src/zm_image_analyser.h +++ b/src/zm_image_analyser.h @@ -7,25 +7,58 @@ #include #include #include +#include #include +#include #include "zm.h" +#include "zm_detector.h" #include "zm_image.h" #include "zm_zone.h" #include "zm_event.h" +#include "zm_db.h" using namespace std; +//! List of available detectors. +typedef std::list DetectorsList; + +//! A structure to store the general configuration of a plugin +struct pGenConf { + bool Registered; + bool Configured; + pGenConf(): + Registered(false), + Configured(false) + {} +}; + +//! A structure to store the zone configuration of a plugin +struct pZoneConf { + bool Enabled; + bool RequireNatDet; + bool IncludeNatDet; + bool ReInitNatDet; + pZoneConf(): + Enabled(false), + RequireNatDet(false), + IncludeNatDet(false), + ReInitNatDet(false) + {} +}; + +//! Map of zone configuration for a plugin +typedef std::map PluginZoneConf; //! Class for handling image detection. class ImageAnalyser { public: - + //!Default constructor. - ImageAnalyser() {}; + ImageAnalyser( int nMonitorId = 0 ); //! Destructor. ~ImageAnalyser(); @@ -36,8 +69,83 @@ class ImageAnalyser { //! Overloaded operator=. ImageAnalyser& operator=(const ImageAnalyser& source); -private: + //! Adds new plugin's detector to the list of detectors. + void addDetector(std::auto_ptr Det) + { + m_Detectors.push_back(Det.release()); + } + //! Do detection in an image by calling all available detectors. + bool DoDetection(const Image &comp_image, Zone** zones, Event::StringSetMap& noteSetMap, std::string& det_cause, unsigned int& score); + + //! Configure all loaded plugins using given configuration file. + void configurePlugins(string sConfigFileName, bool bDoNativeDet = 0); + + //! Check if the configuration file contains the right section name + bool isValidConfigFile(string sPluginName, string sConfigFileName); + + //! Get index of enabled zones for this monitor (same ordering as in Monitor::Load) + bool getMonitorZones(); + + //! Get plugin configuration from database + bool getPluginConfig(string sPluginName, vector vnPluginZones, map >& mapPluginConf); + + //! Get enabled zones for the plugin + bool getEnabledZonesForPlugin(string sPluginName, vector& vnPluginZones); + + //! Get zones configuration from database + bool getZonesConfig(string sLoadedPlugins); + + //! Get Zone configuration from this class + bool getZoneConfig(unsigned int nZone, zConf& zoneConf); + + //! Get the general settings of a registered plugin + bool getRegPluginGenConf(string sPluginName, pGenConf& regPluginGenConf); + + //! Get the zone settings of a registered plugin + void getRegPluginZoneConf(string sPluginName, PluginZoneConf& regPluginZoneConf); + + //! Remove from db plugins no longer detected + void cleanupPlugins(); + + private: + + //! All available detectors. + DetectorsList m_Detectors; + + //! The monitor id + int m_nMonitorId; + + //! Native detection is enabled + bool m_bIsNativeDetEnabled; + + //! Analyser is enabled + bool m_bIsAnalyserEnabled; + + //! A structure to store a plugin parameter + struct zIdName { + unsigned int zoneId; + string name; + }; + + //! A vector filled with parameters of zones + vector m_vZonesConfig; + + //! A structure to store basic settings of a zone + struct zSetting { + unsigned int id; + string name; + string type; + }; + + //! A vector filled with settings of zones enabled for the monitor + vector m_vMonitorZones; + + //! A map to store the general configuration of registered plugins + map mapRegPluginGenConf; + + //! A map to store the zone configuration of registered plugins + map mapRegPluginZoneConf; }; diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index c64f6e7c0..8cb0caad2 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -35,9 +35,6 @@ bool Logger::smInitialised = false; Logger *Logger::smInstance = 0; -Logger::StringMap Logger::smCodes; -Logger::IntMap Logger::smSyslogPriorities; - #if 0 static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 ) { diff --git a/src/zm_logger.h b/src/zm_logger.h index 90238fd19..957b2aee4 100644 --- a/src/zm_logger.h +++ b/src/zm_logger.h @@ -84,8 +84,8 @@ private: static bool smInitialised; static Logger *smInstance; - static StringMap smCodes; - static IntMap smSyslogPriorities; + StringMap smCodes; + IntMap smSyslogPriorities; private: bool mInitialised; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 07194d284..faea9f7be 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -65,6 +65,23 @@ std::vector split(const std::string &s, char delim) { } return elems; } + +#if ZM_PLUGINS_ON +int conf_select(const struct direct *entry) +{ + char *ptr; + + if ((strcmp(entry->d_name, ".")== 0) || (strcmp(entry->d_name, "..") == 0)) + return 0; + + // Check for filename extensions. + ptr = rindex((char*)entry->d_name, '.'); + if ((ptr != NULL) && (strcmp(ptr, ".conf") == 0)) + return 1; + else + return 0; +} +#endif // ZM_PLUGINS_ON //============================================================================= @@ -286,7 +303,8 @@ Monitor::Monitor( Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones, - Zone *p_zones[] + Zone *p_zones[], + int p_DoNativeMotDet ) : id( p_id ), function( (Function)p_function ), enabled( p_enabled ), @@ -311,15 +329,15 @@ Monitor::Monitor( alarm_ref_blend_perc( p_alarm_ref_blend_perc ), track_motion( p_track_motion ), signal_check_colour( p_signal_check_colour ), - delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), - ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), purpose( p_purpose ), last_motion_score(0), camera( p_camera ), n_zones( p_n_zones ), zones( p_zones ), timestamps( 0 ), - images( 0 ) + images( 0 ), + iDoNativeMotDet( p_DoNativeMotDet ), + ThePluginManager( p_id ) { strncpy( name, p_name, sizeof(name) ); @@ -414,6 +432,35 @@ Monitor::Monitor( shared_data->alarm_y = -1; } +#if ZM_PLUGINS_ON + if ( purpose == ANALYSIS || purpose == QUERY ) + { + if ( config.load_plugins || purpose == QUERY ) + { + Info("Load plugins from the directory %s ... ", config.path_plugins); + ThePluginManager.setPluginExt(std::string(config.plugin_extension)); + unsigned int nNumPlugLoaded = 0; + unsigned int nNumPlugFound = ThePluginManager.findPlugins( + std::string(config.path_plugins), (purpose == ANALYSIS), nNumPlugLoaded ); + Info("Found %u plugin(s) - %u loaded", nNumPlugFound, nNumPlugLoaded); + if (nNumPlugFound > 0) + { + ThePluginManager.configurePlugins( + std::string(config.plugins_config_path), + (!config.turnoff_native_analysis && iDoNativeMotDet)); + struct direct **files; + int count = scandir(config.plugins_config_dir, &files, conf_select, alphasort); + if (count > 0) + Info("Load plugin configuration files from directory %s ... ", config.plugins_config_dir); + for (int i = 0; i < count; ++i) + ThePluginManager.configurePlugins( + join_paths(config.plugins_config_dir, files[i]->d_name), + (!config.turnoff_native_analysis && iDoNativeMotDet)); + } + } + } +#endif // ZM_PLUGINS_ON + if ( ( ! mem_ptr ) || ! shared_data->valid ) { if ( purpose != QUERY ) @@ -486,7 +533,6 @@ Monitor::Monitor( Warning( "Waiting for capture daemon" ); sleep( 1 ); } - ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); n_linked_monitors = 0; linked_monitors = 0; @@ -645,6 +691,24 @@ void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) delete[] zones; n_zones = p_n_zones; zones = p_zones; + for ( int i = 0; i < n_zones; i++ ) + { + if ( purpose == ANALYSIS ) + { + Zone *zone = zones[i]; + Debug( 4, "Assign reference image of zone %s", zone->Label() ); + zone->AssignRefImage( width, height, camera->Colours(), camera->SubpixelOrder(), + image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize() ); +#if ZM_PLUGINS_ON + zConf zone_conf; + if ( ThePluginManager.getImageAnalyser().getZoneConfig( i, zone_conf ) ) + { + Debug( 4, "Configure zone %s for plugins", zone->Label() ); + zone->SetConfig( zone_conf ); + } +#endif // ZM_PLUGINS_ON + } + } } Monitor::State Monitor::GetState() const @@ -1227,7 +1291,11 @@ bool Monitor::Analyse() { Info( "Received resume indication at count %d", image_count ); shared_data->active = true; - ref_image = *snap_image; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Debug( 4, "Set reference image of zone %s", zones[n_zone]->Label() ); + zones[n_zone]->SetRefImage(*snap_image); + } ready_count = image_count+(warmup_count/2); shared_data->alarm_x = shared_data->alarm_y = -1; } @@ -1238,7 +1306,11 @@ bool Monitor::Analyse() { Info( "Auto resuming at count %d", image_count ); shared_data->active = true; - ref_image = *snap_image; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Debug( 4, "Set reference image of zone %s", zones[n_zone]->Label() ); + zones[n_zone]->SetRefImage(*snap_image); + } ready_count = image_count+(warmup_count/2); auto_resume_time = 0; } @@ -1276,6 +1348,8 @@ bool Monitor::Analyse() cause += ", "; cause += trigger_data->trigger_cause; } + else + event->AddCause( trigger_data->trigger_cause ); Event::StringSet noteSet; noteSet.insert( trigger_data->trigger_text ); noteSetMap[trigger_data->trigger_cause] = noteSet; @@ -1303,39 +1377,85 @@ bool Monitor::Analyse() cause += ", "; cause += SIGNAL_CAUSE; } + else + event->AddCause( SIGNAL_CAUSE ); Event::StringSet noteSet; noteSet.insert( signalText ); noteSetMap[SIGNAL_CAUSE] = noteSet; shared_data->state = state = IDLE; shared_data->active = signal; - ref_image = *snap_image; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Debug( 4, "Set reference image of zone %s", zones[n_zone]->Label() ); + zones[n_zone]->SetRefImage(*snap_image); + } } else if ( signal && Active() && (function == MODECT || function == MOCORD) ) { - Event::StringSet zoneSet; - int motion_score = last_motion_score; - if ( !(image_count % (motion_frame_skip+1) ) ) +#if ZM_PLUGINS_ON + if ((config.turnoff_native_analysis && !config.load_plugins) || (!config.turnoff_native_analysis && (iDoNativeMotDet || (!iDoNativeMotDet && !config.load_plugins))) ) +#else // ZM_PLUGINS_ON + if (!config.turnoff_native_analysis && iDoNativeMotDet) +#endif // ZM_PLUGINS_ON { - // Get new score. - motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet ); + Event::StringSet zoneSet; + unsigned int motion_score = last_motion_score; + bool alarm = false; + if ( !(image_count % (motion_frame_skip+1) ) ) + { + // Get new score. + alarm = DetectMotion( *snap_image, zoneSet, motion_score ); + last_motion_score = motion_score; + } + //int motion_score = DetectBlack( *snap_image, zoneSet ); + if ( alarm ) + { + if ( motion_score ) + { + score += motion_score; + if ( !event ) + { + if ( cause.length() ) + cause += ", "; + cause += MOTION_CAUSE; + } + else + { + event->AddCause( MOTION_CAUSE ); + } + noteSetMap[MOTION_CAUSE] = zoneSet; + } + } } - //int motion_score = DetectBlack( *snap_image, zoneSet ); - if ( motion_score ) + else { - if ( !event ) + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { - score += motion_score; - if ( cause.length() ) - cause += ", "; - cause += MOTION_CAUSE; + zones[n_zone]->ResetStats(); } - else - { - score += motion_score; - } - noteSetMap[MOTION_CAUSE] = zoneSet; - } +#if ZM_PLUGINS_ON + if (config.load_plugins) + { + std::string det_cause; // detection cause to fill in plugin's detectors + unsigned int plugin_score = 0; + if ( ThePluginManager.getImageAnalyser().DoDetection( *snap_image, zones, noteSetMap, det_cause, plugin_score ) ) + { + score += plugin_score; + if ( !event ) + { + if ( det_cause.length() ) + { + if ( cause.length() ) + cause += ", "; + cause += det_cause; + } + } + else + event->AddCause( det_cause ); + } + } +#endif // ZM_PLUGINS_ON shared_data->active = signal; } if ( (!signal_change && signal) && n_linked_monitors > 0 ) @@ -1358,6 +1478,8 @@ bool Monitor::Analyse() first_link = false; } } + else + event->AddCause( LINKED_CAUSE ); noteSet.insert( linked_monitors[i]->Name() ); score += 50; } @@ -1457,7 +1579,6 @@ bool Monitor::Analyse() pre_index = (pre_index+1)%image_buffer_count; pre_event_images--; } - event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); shared_data->last_event = event->Id(); @@ -1625,10 +1746,11 @@ bool Monitor::Analyse() } if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) { - if ( state == ALARM ) { - ref_image.Blend( *snap_image, alarm_ref_blend_perc ); - } else { - ref_image.Blend( *snap_image, ref_blend_perc ); + int ref_blend = ( state == ALARM ) ? alarm_ref_blend_perc : ref_blend_perc; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Debug( 4, "Blend reference image of zone %s", zones[n_zone]->Label() ); + zones[n_zone]->BlendRefImage( *snap_image, ref_blend ); } } last_signal = signal; @@ -1652,7 +1774,7 @@ void Monitor::Reload() closeEvent(); static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); + snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet, SignalCheckColour from Monitors where Id = '%d'", id ); if ( mysql_query( &dbconn, sql ) ) { @@ -1695,7 +1817,8 @@ void Monitor::Reload() ref_blend_perc = atoi(dbrow[index++]); alarm_ref_blend_perc = atoi(dbrow[index++]); track_motion = atoi(dbrow[index++]); - + + iDoNativeMotDet = atoi(dbrow[index++]); if ( dbrow[index][0] == '#' ) signal_check_colour = strtol(dbrow[index]+1,0,16); @@ -1844,11 +1967,11 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose static char sql[ZM_SQL_MED_BUFSIZ]; if ( !device[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); } if ( mysql_query( &dbconn, sql ) ) { @@ -1934,6 +2057,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + int doNativeMotDet = atoi(dbrow[col]); col++; + int signal_check_colour; if ( dbrow[col][0] == '#' ) signal_check_colour = strtol(dbrow[col]+1,0,16); @@ -1996,7 +2121,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); signal_check_colour, purpose, 0, - 0 + 0, + doNativeMotDet ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); @@ -2020,11 +2146,11 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c static char sql[ZM_SQL_MED_BUFSIZ]; if ( !protocol ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2091,6 +2217,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + int doNativeMotDet = atoi(dbrow[col]); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2169,8 +2296,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c RGB_WHITE, purpose, 0, - 0 - + 0, + doNativeMotDet ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); @@ -2193,11 +2320,11 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2260,6 +2387,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + int doNativeMotDet = atoi(dbrow[col]); col++; + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2306,7 +2435,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu RGB_WHITE, purpose, 0, - 0 + 0, + doNativeMotDet ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); @@ -2330,11 +2460,11 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); + strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); } else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); + snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); } if ( mysql_query( &dbconn, sql ) ) { @@ -2399,6 +2529,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + int doNativeMotDet = atoi(dbrow[col]); col++; + int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); @@ -2447,7 +2579,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose RGB_WHITE, purpose, 0, - 0 + 0, + doNativeMotDet ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); @@ -2469,7 +2602,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id ); + snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, DoNativeMotDet, SignalCheckColour from Monitors where Id = %d", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -2563,6 +2696,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; + int doNativeMotDet = atoi(dbrow[col]); col++; + int signal_check_colour; if ( dbrow[col][0] == '#' ) signal_check_colour = strtol(dbrow[col]+1,0,16); @@ -2760,8 +2895,8 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); signal_check_colour, purpose, 0, - 0 - + 0, + doNativeMotDet ); int n_zones = 0; @@ -3174,33 +3309,32 @@ unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zon -unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) +unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet, unsigned int &score ) { bool alarm = false; - unsigned int score = 0; + score = 0; if ( n_zones <= 0 ) return( alarm ); - if ( config.record_diag_images ) + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) + Zone *zone = zones[n_zone]; + if ( config.record_diag_images ) { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); + static char diag_path[PATH_MAX] = ""; + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-r.jpg", config.dir_events, id, zone->Id() ); + zone->WriteRefImage( diag_path ); } - ref_image.WriteJpeg( diag_path ); - } - ref_image.Delta( comp_image, &delta_image); + Debug( 4, "Set delta image of zone %s", zone->Label() ); + zone->SetDeltaImage( comp_image ); - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) + if ( config.record_diag_images ) { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); + static char diag_path[PATH_MAX] = ""; + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-d.jpg", config.dir_events, id, zone->Id() ); + zone->WriteDeltaImage( diag_path ); } - delta_image.WriteJpeg( diag_path ); } // Blank out all exclusion zones @@ -3215,7 +3349,7 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z continue; } Debug( 3, "Blanking inactive zone %s", zone->Label() ); - delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); + zone->FillDeltaImage( RGB_BLACK ); } // Check preclusive zones first @@ -3229,13 +3363,16 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z int old_zone_score = zone->Score(); bool old_zone_alarmed = zone->Alarmed(); Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" ); - if ( zone->CheckAlarms( &delta_image ) ) + if ( zone->CheckAlarms( &comp_image ) ) { alarm = true; score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); + if ( !zone->IsPostProcEnabled() ) + { + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() ); + } //zone->ResetStats(); } else { // check if end of alarm @@ -3273,24 +3410,26 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z continue; } Debug( 3, "Checking active zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) + if ( zone->CheckAlarms( &comp_image ) ) { alarm = true; - score += zone->Score(); zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) + score += zone->Score(); + if ( !zone->IsPostProcEnabled() ) { - if ( (int)zone->Score() > top_score ) + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() ); + if ( config.opt_control && track_motion ) { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); + if ( (int)zone->Score() > top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } } } } } - if ( alarm ) { for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) @@ -3301,19 +3440,22 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z continue; } Debug( 3, "Checking inclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) + if ( zone->CheckAlarms( &comp_image ) ) { alarm = true; - score += zone->Score(); zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) + score += zone->Score(); + if ( !zone->IsPostProcEnabled() ) { - if ( zone->Score() > (unsigned int)top_score ) + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() ); + if ( config.opt_control && track_motion ) { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); + if ( zone->Score() > (unsigned int)top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } } } } @@ -3330,13 +3472,16 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z continue; } Debug( 3, "Checking exclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) + if ( zone->CheckAlarms( &comp_image ) ) { alarm = true; - score += zone->Score(); zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); + score += zone->Score(); + if ( !zone->IsPostProcEnabled() ) + { + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( ("[Zone " + std::string(zone->Label()) + "]\n").c_str() ); + } } } } @@ -3354,9 +3499,8 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z shared_data->alarm_x = shared_data->alarm_y = -1; } - // This is a small and innocent hack to prevent scores of 0 being returned in alarm state - return( score?score:alarm ); -} + return alarm; +} bool Monitor::DumpSettings( char *output, bool verbose ) { @@ -4286,3 +4430,36 @@ void Monitor::SingleImageZip( int scale) fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); fwrite( img_buffer, img_buffer_size, 1, stdout ); } + +#if ZM_PLUGINS_ON +void Monitor::DumpPluginStatus() +{ + map mapPluginGenConf; + unsigned int nNumPlugins = ThePluginManager.getPluginsGenConf(mapPluginGenConf); + bool bDoNativeDet = !config.turnoff_native_analysis && iDoNativeMotDet; + + if ( nNumPlugins == 0) + { + printf("No plugin found\n"); + return; + } + printf("%79sNATIVE DETECTION\n", " "); + printf("PLUGIN NAME%*sREGISTERED CONFIGURED ZONE ENABLED ACTIVE REQUIRE INCLUDE REINIT\n", 19, " "); + for (map::iterator it = mapPluginGenConf.begin() ; it != mapPluginGenConf.end(); ++it) + { + PluginZoneConf mapPluginZoneConf; + ThePluginManager.getPluginZoneConf( it->first, mapPluginZoneConf ); + int padLen = 34 - it->first.length(); + if(padLen < 0) padLen = 0; + printf("%s%*s%d%*s%d%*s", it->first.c_str(), padLen, " ", it->second.Registered, 10, " ", it->second.Configured, 8, " "); + for (PluginZoneConf::iterator it2 = mapPluginZoneConf.begin() ; it2 != mapPluginZoneConf.end(); ++it2) + { + if (it2 != mapPluginZoneConf.begin()) + printf("%*s", 54, " "); + bool bIsActive = it2->second.Enabled && ( !it2->second.RequireNatDet || ( it2->second.RequireNatDet && bDoNativeDet ) ); + printf("%d%*s%d%*s%d%*s%d%*s%d%*s%d\n", it2->first, 7, " ", it2->second.Enabled, 7, " ", bIsActive, 8, " ", it2->second.RequireNatDet, 7, " ", it2->second.IncludeNatDet, 6, " ", it2->second.ReInitNatDet); + } + } + ThePluginManager.getImageAnalyser().cleanupPlugins(); +} +#endif // ZM_PLUGINS_ON diff --git a/src/zm_monitor.h b/src/zm_monitor.h index e3d201266..ac57c3c92 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -32,7 +32,10 @@ #include "zm_camera.h" #include "zm_utils.h" +#if ZM_PLUGINS_ON +#include "zm_plugin_manager.h" #include "zm_image_analyser.h" +#endif #include #include @@ -41,6 +44,9 @@ #define MOTION_CAUSE "Motion" #define LINKED_CAUSE "Linked" + +int conf_select(const struct direct *entry); + // // This is the main class for monitors. Each monitor is associated // with a camera and is effectively a collector for events. @@ -244,8 +250,6 @@ protected: Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected double fps; - Image delta_image; - Image ref_image; Purpose purpose; // What this monitor has been created to do int event_count; @@ -289,14 +293,18 @@ protected: Image **images; int iDoNativeMotDet; - +#if ZM_PLUGINS_ON + PluginManager ThePluginManager; +#else + int ThePluginManager; +#endif int n_linked_monitors; MonitorLink **linked_monitors; public: // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. //bool OurCheckAlarms( Zone *zone, const Image *pImage ); - Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); + Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0, int p_DoNativeMotDet=1 ); ~Monitor(); void AddZones( int p_n_zones, Zone *p_zones[] ); @@ -388,6 +396,7 @@ public: } unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); + unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet, unsigned int &score ); // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); bool CheckSignal( const Image *image ); @@ -403,6 +412,8 @@ public: bool DumpSettings( char *output, bool verbose ); void DumpZoneImage( const char *zone_string=0 ); + void DumpPluginStatus(); + #if ZM_HAS_V4L static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); #endif // ZM_HAS_V4L diff --git a/src/zm_plugin.cpp b/src/zm_plugin.cpp new file mode 100644 index 000000000..16adcd4fc --- /dev/null +++ b/src/zm_plugin.cpp @@ -0,0 +1,105 @@ +#include "zm_plugin.h" + + + +/*!\fn Plugin::Plugin(const std::string &sFilename) + * \param sFilename is the name of plugin file to load + */ +Plugin::Plugin(const std::string &sFilename) + : m_sPluginFileName(sFilename), + m_hDLL(0), + m_pDLLRefCount(0), + m_pfnGetEngineVersion(0), + m_pfnRegisterPlugin(0) +{ + + // Try to load the plugin as a dynamic library + m_hDLL = dlopen(sFilename.c_str(), RTLD_LAZY|RTLD_GLOBAL); + + if(!m_hDLL) // if library hasn't been loaded successfully + { + throw runtime_error("Could not load '" + sFilename + "' (" + dlerror() + ")"); + } + + // Locate the plugin's exported functions + try + { + m_pfnGetEngineVersion = reinterpret_cast(dlsym(m_hDLL, "getEngineVersion")); + m_pfnRegisterPlugin = reinterpret_cast(dlsym(m_hDLL, "registerPlugin")); + + // If the functions aren't found, we're going to assume this is + // a plain simple DLL and not one of our plugins + if(!m_pfnGetEngineVersion || ! m_pfnRegisterPlugin) + throw runtime_error("'" + sFilename + "' is not a valid plugin"); + + // Initialize a new DLL reference counter + m_pDLLRefCount = new size_t(1); + } + catch(runtime_error &ex) + { + dlclose(m_hDLL); + throw ex; + } + catch(...) + { + dlclose(m_hDLL); + throw runtime_error("Unknown exception while loading plugin '" + sFilename + "'"); + } +} + + + +/*!\fn Plugin::Plugin(const Plugin &Other) + * \param Other is the other plugin instance to copy + */ +Plugin::Plugin(const Plugin &Other) + : m_sPluginFileName(Other.m_sPluginFileName), + m_hDLL(Other.m_hDLL), + m_pDLLRefCount(Other.m_pDLLRefCount), + m_pfnGetEngineVersion(Other.m_pfnGetEngineVersion), + m_pfnRegisterPlugin(Other.m_pfnRegisterPlugin) +{ + // Increase DLL reference counter + ++*m_pDLLRefCount; +} + + + +/*!\fn Plugin::operator=(const Plugin &Other) + * \param Other is the other plugin instance to copy + * return copy of object + */ +Plugin& Plugin::operator=(const Plugin &Other) +{ + m_hDLL = Other.m_hDLL; + m_pfnGetEngineVersion = Other.m_pfnGetEngineVersion; + m_pfnRegisterPlugin = Other.m_pfnRegisterPlugin; + m_pDLLRefCount = Other.m_pDLLRefCount; + m_sPluginFileName = Other.m_sPluginFileName; + // Increase DLL reference counter + ++*m_pDLLRefCount; + return *this; + +} + + + +Plugin::~Plugin() +{ + // Only unload the DLL if there are no more references to it + if(!--*m_pDLLRefCount) + { + delete m_pDLLRefCount; + dlclose(m_hDLL); + } +} + + + +/*!\fn Plugin::registerPlugin(PluginManager &K) + * \param K is the pointer to plugin manager + */ +void Plugin::registerPlugin(PluginManager &K) +{ + m_pfnRegisterPlugin(K, m_sPluginFileName); +} diff --git a/src/zm_plugin.h b/src/zm_plugin.h new file mode 100644 index 000000000..4434b8f88 --- /dev/null +++ b/src/zm_plugin.h @@ -0,0 +1,77 @@ +#ifndef ZM_PLUGIN_H +#define ZM_PLUGIN_H + + + +#include +#include +#include + +#include + + + +using namespace std; + + + +class PluginManager; + + + +//! Signature for the version query function +typedef int fnGetEngineVersion(); + +//! Signature for the plugin's registration function +typedef void fnRegisterPlugin(PluginManager &, string); + + + +//! Representation of a plugin. +/*! Use for loading plugin's shared library + * and registration of it to the PluginManager. + */ +class Plugin +{ + +public: + + //! Initialize and load plugin + Plugin(const std::string &sFilename); + + //! Copy existing plugin instance + Plugin(const Plugin &Other); + + //! Operator =. + Plugin &operator =(const Plugin &Other); + + //! Unload a plugin + ~Plugin(); + + //! Query the plugin for its expected engine version + int getEngineVersion() const { return m_pfnGetEngineVersion();} + + //! Register the plugin to a PluginManager + void registerPlugin(PluginManager &K); + +private: + + //! Shared file name. + string m_sPluginFileName; + + //! DLL handle + void* m_hDLL; + + //! Number of references to the DLL + size_t *m_pDLLRefCount; + + //! Version query function + fnGetEngineVersion *m_pfnGetEngineVersion; + + //! Plugin registration function + fnRegisterPlugin *m_pfnRegisterPlugin; +}; + + + +#endif //ZM_PLUGIN_H diff --git a/src/zm_plugin_manager.cpp b/src/zm_plugin_manager.cpp new file mode 100644 index 000000000..2f2a06bb7 --- /dev/null +++ b/src/zm_plugin_manager.cpp @@ -0,0 +1,160 @@ +#include "zm_plugin_manager.h" + + + +/*! \fn file_select(const struct direct *entry) + * A functor for selection of files with specified extension. + * \param entry is file structure + * \return 1 if file match selection criteria and + * 0 otherwise. + * NOTE: file extension is specified by PluginManager::m_sPluginExt + * static variable. + */ +int file_select(const struct direct *entry) +{ + char *ptr; + + if ((strcmp(entry->d_name, ".")== 0) || (strcmp(entry->d_name, "..") == 0)) + return 0; + + // Check for filename extensions. + ptr = rindex((char*)entry->d_name, '.'); + if ((ptr != NULL) && (strcmp(ptr, (PluginManager::m_sPluginExt).c_str()) == 0)) + return 1; + else + return 0; +} + + + + +/*! \fn join_paths(const string& p1, const string& p2) + * \param p1 is the first part of desired path + * \param p2 is the second part of desired path + * \return joined path string. + */ +string join_paths(const string& p1, const string& p2) +{ + char sep = '/'; + string tmp = p1; + +#ifdef _WIN32 + sep = '\\'; +#endif + + if (p1[p1.length()] != sep) + { // Need to add a path separator + tmp += sep; + return(tmp + p2); + } + else + return(p1 + p2); +} + + + +string PluginManager::m_sPluginExt = DEFAULT_PLUGIN_EXT; + + +PluginManager::PluginManager() {} + + +PluginManager::PluginManager( + int nMonitorId +) : + m_ImageAnalyser( nMonitorId ) +{} + + + +/*!\fn PluginManager::loadPlugin(const string &sFilename)) + * \param sFilename is the name of plugin file to load + */ +bool PluginManager::loadPlugin(const string &sFilename) +{ + try + { + if(m_LoadedPlugins.find(sFilename) == m_LoadedPlugins.end()) + m_LoadedPlugins.insert(PluginMap::value_type(sFilename, Plugin(sFilename))).first->second.registerPlugin(*this); + } + catch(runtime_error &ex) + { + Error("Runtime error: %s", ex.what()); + return false; + } + catch(...) + { + Error("Unknown exception. Could not load %s.", sFilename.c_str()); + return false; + } + return true; +} + + +/*!\fn PluginManager::findPlugins(const string &sPath, bool loadPlugins) + * \param sPath is the path to folder to search plugins + * \param loadPlugins is a flag to allow loading of plugins + * \param nNumPlugLoaded is the number of loaded plugins + * \return the number of found plugins + */ +int PluginManager::findPlugins(const string sPath, bool loadPlugins, unsigned int& nNumPlugLoaded) +{ + struct direct **files; + int count = scandir(sPath.c_str(), &files, file_select, alphasort); + if(count <= 0) count = 0; + + for (int i = 0; i < count; ++i) + { + string sFileName = string(files[i]->d_name); + string sFullPath = join_paths(sPath, sFileName); + size_t idx = sFileName.rfind('.'); + if (idx != string::npos) + sFileName = sFileName.substr(0, idx); + bool IsPluginRegistered = false; + if(config.load_plugins || loadPlugins) + { + Info("Loading plugin %s ... ", sFullPath.c_str()); + IsPluginRegistered = loadPlugin(sFullPath); + } + mapPluginReg.insert( pair(sFileName, IsPluginRegistered) ); + if (IsPluginRegistered) nNumPlugLoaded++; + } + return count; +} + + +/*!\fn PluginManager::configurePlugins(string sConfigFileName) + * \param sConfigFileName is the path to the configuration file, where parameters for all plugins are given. + * \param bDoNativeDet is true if native detection will be performed +*/ +void PluginManager::configurePlugins(string sConfigFileName, bool bDoNativeDet) +{ + m_ImageAnalyser.configurePlugins(sConfigFileName, bDoNativeDet); +} + + +/*!\fn PluginManager::getPluginsGenConf(map& mapPluginGenConf) + * \param mapPluginGenConf is the map of general settings for the plugins + * \param mapPluginZoneConf is the map of zone settings for the plugins + * \return the number of found plugins + */ +unsigned long PluginManager::getPluginsGenConf(map& mapPluginGenConf) +{ + for (map::iterator it = mapPluginReg.begin() ; it != mapPluginReg.end(); ++it) + { + pGenConf plugGenConf; + m_ImageAnalyser.getRegPluginGenConf( it->first, plugGenConf ); + plugGenConf.Registered = it->second; + mapPluginGenConf.insert( pair(it->first, plugGenConf) ); + } + return mapPluginGenConf.size(); +} + + +/*!\fn PluginManager::getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf) + * \param mapPluginZoneConf is the map of zone settings for the plugin + */ +void PluginManager::getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf) +{ + m_ImageAnalyser.getRegPluginZoneConf( sPluginName, mapPluginZoneConf ); +} diff --git a/src/zm_plugin_manager.h b/src/zm_plugin_manager.h new file mode 100644 index 000000000..f3c8fa465 --- /dev/null +++ b/src/zm_plugin_manager.h @@ -0,0 +1,94 @@ +#ifndef ZM_PLUGIN_MANAGER_H +#define ZM_PLUGIN_MANAGER_H + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zm_image_analyser.h" +#include "zm_detector.h" +#include "zm_plugin.h" + + + +using namespace std; + + +#define ZM_ENGINE_VERSION 24 + + +//! Map of plugins by their associated file names. +typedef std::map PluginMap; + + +//! External function for sorting of files in directory. +extern int alphasort(); + + +//! Function to select files with plugins by extension. +int file_select(const struct direct *entry); + + +//! Join two path strings. +string join_paths(const string& p1, const string& p2); + + + +//! Class for managing all loaded plugins. +class PluginManager +{ +public: + //! Default constructor. + PluginManager(); + + //! Constructor with parameters + PluginManager(int nMonitorId); + + //! Access the image analyser. + ImageAnalyser &getImageAnalyser() {return m_ImageAnalyser;} + + //! Loads a plugin. + bool loadPlugin(const string &sFilename); + + //! Find all plugins from given directory, load them if required and + //! return the number of found plugins and the number of loaded plugins + int findPlugins(const string sPath, bool loadPlugins, unsigned int& nNumPlugLoaded); + + //! Get general settings of plugins + unsigned long getPluginsGenConf(map& mapPluginGenConf); + + //! Get zone settings of a plugin + void getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf); + + //! Configure all loaded plugins using given configuration file. + void configurePlugins(string sConfigFileName, bool bDoNativeDet); + + //! Set plugin extension. + void setPluginExt(string sPluginExt) { m_sPluginExt = sPluginExt; } + + //! Extension for zm plugins. + static string m_sPluginExt; + +private: + + //! All plugins currently loaded. + PluginMap m_LoadedPlugins; + + //! The image analyser. + ImageAnalyser m_ImageAnalyser; + + //! Plugin list + map mapPluginReg; +}; + + + +#endif //ZM_PLUGIN_MANAGER_H diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 90c954d9e..4e37147b7 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -60,10 +60,20 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_ max_blob_size = 0; image = 0; score = 0; + text = ""; + post_proc_enabled = false; + post_proc_in_progress = false; + include_nat_det = true; + reinit_nat_det = false; overload_count = 0; extend_alarm_count = 0; + delta_image = Image( monitor->Width(), monitor->Height(), ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ); + ref_image = Image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder()); + bl_image = Image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder()); + bl_image.Fill( RGB_BLACK ); + pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); pg_image->Clear(); pg_image->Fill( 0xff, polygon ); @@ -91,14 +101,10 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_ } } } - - if ( config.record_diag_images ) + if ( config.record_diag_images && (id > 0)) { static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); - } + snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); pg_image->WriteJpeg( diag_path ); } } @@ -142,6 +148,37 @@ void Zone::SetScore(unsigned int nScore) score = nScore; } +void Zone::SetText(std::string sText) +{ + text = "[Zone "; + text += label; + text += "]\n" + sText; +} + +void Zone::AssignRefImage( 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 ) +{ + ref_image.Assign( p_width, p_height, p_colours, p_subpixelorder, new_buffer, buffer_size); +} + +void Zone::SetRefImage(const Image &srcImage) +{ + ref_image = srcImage; +} + +void Zone::BlendRefImage( const Image &srcImage, int transparency ) +{ + ref_image.Blend( srcImage, transparency ); +} + +void Zone::SetDeltaImage( const Image &srcImage ) +{ + ref_image.Delta( srcImage, &delta_image ); +} + +void Zone::FillDeltaImage( Rgb colour ) +{ + delta_image.Fill( colour, polygon); +} void Zone::SetAlarmImage(const Image* srcImage) { @@ -149,6 +186,16 @@ void Zone::SetAlarmImage(const Image* srcImage) image = new Image(*srcImage); } +bool Zone::WriteRefImage( const char *filename, int quality_override ) const +{ + return ref_image.WriteJpeg( filename, quality_override ); +} + +bool Zone::WriteDeltaImage( const char *filename, int quality_override ) const +{ + return delta_image.WriteJpeg( filename, quality_override ); +} + int Zone::GetOverloadCount() { return overload_count; @@ -194,9 +241,33 @@ bool Zone::CheckExtendAlarmCount() //=========================================================================== +void Zone::SetConfig( zConf zone_conf ) +{ + post_proc_enabled = zone_conf.RequireNatDet; + include_nat_det = zone_conf.IncludeNatDet; + reinit_nat_det = zone_conf.ReInitNatDet; + + if ( post_proc_enabled ) { + std::string sMessage; + if ( include_nat_det ) { + sMessage = "(native detection included"; + } + if ( reinit_nat_det ) { + if (sMessage.empty()) { + sMessage = "(native detection will be reinitialized"; + } else { + sMessage += ", reinitialization is required"; + } + } + if (!sMessage.empty()) { + sMessage += ")"; + } + Info("Post processing enabled for zone '%s' %s", label, sMessage.c_str()); + } +} -bool Zone::CheckAlarms( const Image *delta_image ) +bool Zone::CheckAlarms( const Image *comp_image ) { ResetStats(); @@ -205,12 +276,19 @@ bool Zone::CheckAlarms( const Image *delta_image ) Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); overload_count--; + post_proc_in_progress = false; return( false ); } + if ( reinit_nat_det ) + { + Debug( 4, "Update reference image of zone %s", label ); + ref_image = *comp_image; + } + delete image; // Get the difference image - Image *diff_image = image = new Image( *delta_image ); + Image *diff_image = image = new Image( delta_image ); int diff_width = diff_image->Width(); uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); uint8_t* pdiff; @@ -245,10 +323,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); - } + snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); diff_image->WriteJpeg( diag_path ); } @@ -259,14 +334,17 @@ bool Zone::CheckAlarms( const Image *delta_image ) if( alarm_pixels ) { if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { /* Not enough pixels alarmed */ + post_proc_in_progress = false; return (false); } else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { /* Too many pixels alarmed */ overload_count = overload_frames; + post_proc_in_progress = false; return (false); } } else { /* No alarmed pixels */ + post_proc_in_progress = false; return (false); } @@ -342,10 +420,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); - } + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); diff_image->WriteJpeg( diag_path ); } @@ -354,14 +429,17 @@ bool Zone::CheckAlarms( const Image *delta_image ) if( alarm_filter_pixels ) { if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { /* Not enough pixels alarmed */ + post_proc_in_progress = false; return (false); } else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { /* Too many pixels alarmed */ overload_count = overload_frames; + post_proc_in_progress = false; return (false); } } else { /* No filtered pixels */ + post_proc_in_progress = false; return (false); } @@ -583,15 +661,13 @@ bool Zone::CheckAlarms( const Image *delta_image ) if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); - } + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); diff_image->WriteJpeg( diag_path ); } if ( !alarm_blobs ) { + post_proc_in_progress = false; return( false ); } @@ -642,10 +718,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); - } + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); diff_image->WriteJpeg( diag_path ); } Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); @@ -653,14 +726,17 @@ bool Zone::CheckAlarms( const Image *delta_image ) if( alarm_blobs ) { if( min_blobs && (alarm_blobs < min_blobs) ) { /* Not enough pixels alarmed */ + post_proc_in_progress = false; return (false); } else if(max_blobs && (alarm_blobs > max_blobs) ) { /* Too many pixels alarmed */ overload_count = overload_frames; + post_proc_in_progress = false; return (false); } } else { /* No blobs */ + post_proc_in_progress = false; return (false); } @@ -797,13 +873,19 @@ bool Zone::CheckAlarms( const Image *delta_image ) } } } - - if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { - image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); - } else { - image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); + if ( post_proc_enabled ) { + post_proc_in_progress = true; + } + if ( include_nat_det ) { + if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { + image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); + } else { + image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); + } + } else { + score = 0; + image = new Image( bl_image ); } - // Only need to delete this when 'image' becomes detached and points somewhere else delete diff_image; } diff --git a/src/zm_zone.h b/src/zm_zone.h index c431f88a0..6e9267b1c 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -28,6 +28,19 @@ class Monitor; +//! A structure to store the post processing configuration for the zone +struct zConf +{ + bool RequireNatDet; + bool IncludeNatDet; + bool ReInitNatDet; + zConf(): + RequireNatDet(false), + IncludeNatDet(false), + ReInitNatDet(false) + {} +}; + // // This describes a 'zone', or an area of an image that has certain // detection characteristics. @@ -87,12 +100,20 @@ protected: Box alarm_box; Coord alarm_centre; unsigned int score; + std::string text; Image *pg_image; Range *ranges; Image *image; + Image delta_image; + Image ref_image; + Image bl_image; int overload_count; int extend_alarm_count; + bool post_proc_enabled; + bool include_nat_det; + bool reinit_nat_det; + bool post_proc_in_progress; protected: void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ); @@ -130,7 +151,14 @@ public: inline void ClearAlarm() { alarmed = false; } inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline unsigned int Score() const { return( score ); } - + inline std::string Text() const { return( text ); } + void SetConfig( zConf zone_conf ); + inline bool IsPostProcEnabled() const { return post_proc_enabled; } + inline bool IsNatDetIncluded() const { return include_nat_det; } + inline bool IsNatDetReInitialized() const { return reinit_nat_det; } + inline void StartPostProcessing() { post_proc_in_progress = true; } + inline void StopPostProcessing() { post_proc_in_progress = false; } + inline bool IsPostProcInProgress() { return post_proc_in_progress; } inline void ResetStats() { alarmed = false; @@ -142,9 +170,10 @@ public: min_blob_size = 0; max_blob_size = 0; score = 0; + text = ""; } void RecordStats( const Event *event ); - bool CheckAlarms( const Image *delta_image ); + bool CheckAlarms( const Image *comp_image ); bool DumpSettings( char *output, bool verbose ); static bool ParsePolygonString( const char *polygon_string, Polygon &polygon ); @@ -161,7 +190,15 @@ public: void SetExtendAlarmCount(int nOverCount); int GetExtendAlarmFrames(); void SetScore(unsigned int nScore); + void SetText(std::string sText); void SetAlarmImage(const Image* srcImage); + void AssignRefImage( 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 SetRefImage( const Image &srcImage); + void BlendRefImage( const Image &srcImage, int transparency=12 ); + void SetDeltaImage( const Image &srcImage ); + void FillDeltaImage( Rgb colour ); + bool WriteRefImage( const char *filename, int quality_override=0 ) const; + bool WriteDeltaImage( const char *filename, int quality_override=0 ) const; inline const Image *getPgImage() const { return( pg_image ); } inline const Range *getRanges() const { return( ranges ); } diff --git a/src/zmu.cpp b/src/zmu.cpp index 9348012db..4eaf88811 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -68,7 +68,9 @@ void Usage( int status=-1 ) fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); fprintf( stderr, " -P, --password : password combination of the given user\n" ); fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); - +#if ZM_PLUGINS_ON + fprintf( stderr, " -p, --plugin-status : Display the status of detected plugins\n" ); +#endif // ZM_PLUGINS_ON exit( status ); } @@ -96,6 +98,7 @@ typedef enum { ZMU_SUSPEND = 0x00400000, ZMU_RESUME = 0x00800000, ZMU_LIST = 0x10000000, + ZMU_LIST_PLUG = 0x20000000 } Function; bool ValidateAccess( User *user, int mon_id, int function ) @@ -111,7 +114,7 @@ bool ValidateAccess( User *user, int mon_id, int function ) if ( user->getEvents() < User::PERM_VIEW ) allowed = false; } - if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) + if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST|ZMU_LIST_PLUG) ) { if ( user->getMonitors() < User::PERM_VIEW ) allowed = false; @@ -180,6 +183,9 @@ int main( int argc, char *argv[] ) {"version", 1, 0, 'V'}, {"help", 0, 0, 'h'}, {"list", 0, 0, 'l'}, +#if ZM_PLUGINS_ON + {"list_plugins", 0, 0, 'p'}, +#endif // ZM_PLUGINS_ON {0, 0, 0, 0} }; @@ -209,7 +215,7 @@ int main( int argc, char *argv[] ) { int option_index = 0; - int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); + int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlpB::C::H::O::U:P:A:V:", long_options, &option_index); if (c == -1) { break; @@ -327,6 +333,11 @@ int main( int argc, char *argv[] ) case 'l': function |= ZMU_LIST; break; +#if ZM_PLUGINS_ON + case 'p': + function |= ZMU_LIST_PLUG; + break; +#endif // ZM_PLUGINS_ON case '?': Usage(); break; @@ -415,7 +426,7 @@ int main( int argc, char *argv[] ) if ( mon_id > 0 ) { - Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); + Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES|ZMU_LIST_PLUG), Monitor::QUERY ); if ( monitor ) { if ( verbose ) @@ -655,6 +666,12 @@ int main( int argc, char *argv[] ) have_output = true; } } +#if ZM_PLUGINS_ON + if ( function & ZMU_LIST_PLUG ) + { + monitor->DumpPluginStatus(); + } +#endif // ZM_PLUGINS_ON if ( have_output ) { printf( "\n" ); diff --git a/web/includes/actions.php b/web/includes/actions.php index bfe62190f..ee8282a6b 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -372,11 +372,14 @@ if ( !empty($action) ) $changes=0; foreach( $pconfs as $pconf ) { - $value=$_REQUEST['pluginOpt'][$pconf['Name']]; - if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) + if(isset($_REQUEST['pluginOpt'][$pconf['Name']])) { - dbQuery("UPDATE PluginsConfig SET Value=? WHERE id=?", array( $value, $pconf['Id'] ) ); - $changes++; + $value=$_REQUEST['pluginOpt'][$pconf['Name']]; + if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) + { + dbQuery("UPDATE PluginsConfig SET Value=? WHERE id=?", array( $value, $pconf['Id'] ) ); + $changes++; + } } } if($changes>0) @@ -409,6 +412,7 @@ if ( !empty($action) ) foreach( $_REQUEST['markZids'] as $markZid ) { dbQuery( "delete from Zones WHERE MonitorId=? AND Id=?", array( $mid, $markZid) ); + dbQuery( "delete from PluginsConfig WHERE MonitorId=? AND ZoneId=?", array( $mid, $markZid) ); $deletedZid = 1; } if ( $deletedZid ) @@ -593,6 +597,7 @@ if ( !empty($action) ) // This is the important stuff dbQuery( "delete from Monitors where Id = ?", array($markMid) ); dbQuery( "delete from Zones where MonitorId = ?", array($markMid) ); + dbQuery( "delete from PluginsConfig where MonitorId = ?", array($markMid) ); if ( ZM_OPT_X10 ) dbQuery( "delete from TriggersX10 where MonitorId=?", array($markMid) ); diff --git a/web/lang/big5_big5.php b/web/lang/big5_big5.php index f218109c1..cc5aac827 100644 --- a/web/lang/big5_big5.php +++ b/web/lang/big5_big5.php @@ -92,6 +92,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => '警告', 'All' => '全部', 'Apply' => '確定', @@ -375,6 +376,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => '包含', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => '反轉', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -547,6 +549,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => '全部播放', 'PleaseWait' => 'Please Wait', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => '點', 'PostEventImageBuffer' => '後置事件影像緩衝', @@ -562,6 +565,7 @@ $SLANG = array( 'Record' => '錄影', 'RefImageBlendPct' => '參考影像混合 %ge', 'Refresh' => '更新', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => '遠端主機名稱', 'RemoteHostPath' => '遠端主機路徑', @@ -575,6 +579,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => '重新啟動', diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index b0b76ba93..5c1281f63 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => '报警最大帧率FPS', 'AlarmPx' => '报警像素', 'AlarmRGBUnset' => '你必须设置一个报警颜色(RGB)', + 'AlarmScore' => 'Alarme Score', 'Alert' => '警报', 'All' => '全部', 'Apply' => '应用', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => '影像', 'In' => '在', 'Include' => '包含', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => '反向', 'Iris' => '光圈', 'KeyString' => '密钥字符', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => '播放', 'PlayAll' => '播放全部', 'PleaseWait' => '请等待', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => '点', 'PostEventImageBuffer' => '事件之后影像数', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => '记录', 'RefImageBlendPct' => '参考影像混合 %ge', 'Refresh' => '刷新', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => '远程', 'RemoteHostName' => '远程主机名', 'RemoteHostPath' => '远程主机路径', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => '全部事件', 'ReplayGapless' => '无间隙事件', 'ReplaySingle' => '单一事件', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => '重置', 'ResetEventCounts' => '重置事件数', 'Restart' => '重启动', diff --git a/web/lang/cs_cz.php b/web/lang/cs_cz.php index 3d996b003..f7d382dc3 100644 --- a/web/lang/cs_cz.php +++ b/web/lang/cs_cz.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Pozor', 'All' => 'Vechny', 'Apply' => 'Pout', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'Dovnit', 'Include' => 'Vloit', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Pevrcen', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Pehrt ve', 'PleaseWait' => 'Prosm ekejte', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Pozznamov bufer', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => 'Nahrvat', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Obnovit', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Sov', 'RemoteHostName' => 'Adresa', 'RemoteHostPath' => 'Cesta', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetovat poty zznam', 'Restart' => 'Restartovat', diff --git a/web/lang/de_de.php b/web/lang/de_de.php index 28002aec8..26025e4df 100644 --- a/web/lang/de_de.php +++ b/web/lang/de_de.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm-Maximum-FPS', 'AlarmPx' => 'Alarm-Pixel', 'AlarmRGBUnset' => 'Sie müssen eine RGB-Alarmfarbe setzen', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alarm', 'All' => 'Alle', 'Apply' => 'OK', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Bilder', 'In' => 'In', 'Include' => 'Einschluss', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertiert', 'Iris' => 'Iris', 'KeyString' => 'Schlüsselwort', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Abspielen', 'PlayAll' => 'Alle zeigen', 'PleaseWait' => 'Bitte warten', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Punkt', 'PostEventImageBuffer' => 'Nachereignispuffer', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => 'Aufnahme', 'RefImageBlendPct' => 'Referenz-Bildblende', 'Refresh' => 'Aktualisieren', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Entfernt', 'RemoteHostName' => 'Entfernter Hostname', 'RemoteHostPath' => 'Entfernter Hostpfad', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Ereignisse', 'ReplayGapless' => 'Lückenlose Ereignisse', 'ReplaySingle' => 'Einzelereignis', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Zurücksetzen', 'ResetEventCounts' => 'Lösche Ereignispunktzahl', 'Restart' => 'Neustart', diff --git a/web/lang/dk_dk.php b/web/lang/dk_dk.php index 927440513..6c72d7e62 100644 --- a/web/lang/dk_dk.php +++ b/web/lang/dk_dk.php @@ -89,6 +89,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alarm', 'All' => 'Alle', 'Apply' => 'Aktiver', @@ -372,6 +373,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'Ind', 'Include' => 'Inkluder', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Inverteret', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -544,6 +546,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Afspil Alle', 'PleaseWait' => 'Vent venligst', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Efter Event Billed Buffer', @@ -559,6 +562,7 @@ $SLANG = array( 'Record' => 'Optag', 'RefImageBlendPct' => 'Reference Billede Blend %ge', 'Refresh' => 'Opdater', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Navn', 'RemoteHostPath' => 'Remote Host Stig', @@ -572,6 +576,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Nulstil', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Genstart', diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 92462f430..235b76223 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -97,6 +97,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alert', 'All' => 'All', 'Apply' => 'Apply', @@ -377,6 +378,7 @@ $SLANG = array( 'Image' => 'Image', 'Images' => 'Images', 'Include' => 'Include', + 'IncludeNatDet' => 'Include Native Detection', 'In' => 'In', 'Inverted' => 'Inverted', 'Iris' => 'Iris', @@ -544,6 +546,7 @@ $SLANG = array( 'Pixels' => 'pixels', 'PlayAll' => 'Play All', 'Play' => 'Play', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'PleaseWait' => 'Please Wait', 'Point' => 'Point', @@ -560,6 +563,7 @@ $SLANG = array( 'Record' => 'Record', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Refresh', + 'ReInitNatDet' => 'Reinit. Native Detection', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', 'RemoteHostSubPath' => 'Remote Host SubPath', @@ -573,6 +577,7 @@ $SLANG = array( 'ReplayGapless' => 'Gapless Events', 'Replay' => 'Replay', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'ResetEventCounts' => 'Reset Event Counts', 'Reset' => 'Reset', 'Restarting' => 'Restarting', diff --git a/web/lang/es_ar.php b/web/lang/es_ar.php index ee1508723..1e1313a9d 100644 --- a/web/lang/es_ar.php +++ b/web/lang/es_ar.php @@ -39,6 +39,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alerta', 'All' => 'Todo', 'Apply' => 'Aplicar', @@ -322,6 +323,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'Incluir', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertido', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -494,6 +496,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => 'Espere por favor', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Buffer Imagenes despues evento', @@ -509,6 +512,7 @@ $SLANG = array( 'Record' => 'Registro', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Actualizar', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => 'Nombre Servidor Remoto', 'RemoteHostPath' => 'Enlace Servidor Remoto', @@ -522,6 +526,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Borrar Contador Eventos', 'Restart' => 'Reiniciar', diff --git a/web/lang/es_es.php b/web/lang/es_es.php index 1e80f6500..16421c9c5 100644 --- a/web/lang/es_es.php +++ b/web/lang/es_es.php @@ -87,6 +87,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Máximos MPS alarma', 'AlarmPx' => 'Px alarma', 'AlarmRGBUnset' => 'Debe establecer un color RGB para alarma', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alerta', 'All' => 'Todo', 'Apply' => 'Aplicar', @@ -368,6 +369,7 @@ $SLANG = array( 'Images' => 'Imágenes', 'In' => 'En', 'Include' => 'Incluir', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertido', 'Iris' => 'Iris', 'KeyString' => 'Cadena clave', @@ -540,6 +542,8 @@ $SLANG = array( 'Play' => 'Reproducir', 'PlayAll' => 'Reproducir rodo', 'PleaseWait' => 'Espere por favor', + 'Plugin' => 'Plugin', + 'Plugins' => 'Plugins', 'Point' => 'Punto', 'PostEventImageBuffer' => 'Cuenta de imagen post evento', 'PreEventImageBuffer' => 'Cuenta de imagen pre evento', @@ -554,6 +558,7 @@ $SLANG = array( 'Record' => 'Grabar', 'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge', 'Refresh' => 'Refrescar', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remoto', 'RemoteHostName' => 'Nombre del host remoto', 'RemoteHostPath' => 'Nombre de ruta del host', @@ -567,6 +572,7 @@ $SLANG = array( 'ReplayAll' => 'Todos los eventos', 'ReplayGapless' => 'Eventos sin espacios', 'ReplaySingle' => 'Evento individual', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Restablecer', 'ResetEventCounts' => 'Restablecer número de eventos', 'Restart' => 'Reiniciar', @@ -669,6 +675,7 @@ $SLANG = array( 'UpdateNotNecessary' => 'No es necesario actualizar.', 'Updated' => 'Actualizado', 'Upload' => 'Upload', // Added - 2011-08-23 + 'UsedPlugins' => 'Used Plugins', 'UseFilter' => 'Usar filtro', 'UseFilterExprsPost' => ' filtros de expresión', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Usar ', // This is used at the beginning of the phrase 'use N filter expressions' diff --git a/web/lang/et_ee.php b/web/lang/et_ee.php index e79550698..3991db4c5 100644 --- a/web/lang/et_ee.php +++ b/web/lang/et_ee.php @@ -89,6 +89,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarmi Maksimaalne FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'Sa pead panema alarmi RGB värvi', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Hoiatus', 'All' => 'All', 'Apply' => 'Apply', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Pildid', 'In' => 'In', 'Include' => 'Include', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Inverted', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play Kõike', 'PleaseWait' => 'Palun Oota', + 'Plugin' => 'Plugin', 'Plugins' => 'Pluginad', 'Point' => 'Punkt', 'PostEventImageBuffer' => 'Post Event Image Count', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => 'Salvesta', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Värskenda', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'Kõik sündmused', 'ReplayGapless' => 'Lünkadeta sündmused', 'ReplaySingle' => 'Üksik sündmus', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Taaskäivita', @@ -673,6 +678,7 @@ $SLANG = array( 'UpdateNotNecessary' => 'Uuendus ei ole vajalik.', 'Updated' => 'Uuendatud', // Added - 2011-06-16 'Upload' => 'Üles laadimine', // Added - 2011-08-23 + 'UsedPlugins' => 'Used Plugins', 'UseFilter' => 'Kasuta Filtrit', 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Use ', // This is used at the beginning of the phrase 'use N filter expressions' diff --git a/web/lang/fr_fr.php b/web/lang/fr_fr.php index def9d1575..7f63f25e9 100644 --- a/web/lang/fr_fr.php +++ b/web/lang/fr_fr.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Px Alarme', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmeScore' => 'Score alarme', 'Alert' => 'Alerte', 'All' => 'Tous', 'Apply' => 'Appliquer', @@ -371,7 +372,8 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'Inclure', - 'Inverted' => 'Inversé', + 'IncludeNatDet' => 'Inclure dtection native', + 'Inverted' => 'Invers', 'Iris' => 'Iris', 'KeyString' => 'Key String', 'Label' => 'Label', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => 'Attendez', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Post Event Image Count', @@ -557,7 +560,8 @@ $SLANG = array( 'Real' => 'Réel', 'Record' => 'Enreg.', 'RefImageBlendPct' => 'Reference Image Blend %ge', - 'Refresh' => 'Rafraîchir', + 'Refresh' => 'Rafrachir', + 'ReInitNatDet' => 'Rinit. dtection native', 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Dtection native requise', 'Reset' => 'Reset', 'ResetEventCounts' => 'Rem. à 0 comptage des évts', 'Restart' => 'Redémarrer', diff --git a/web/lang/he_il.php b/web/lang/he_il.php index 83f165cef..d67a2fa6e 100644 --- a/web/lang/he_il.php +++ b/web/lang/he_il.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => ' Px', 'AlarmRGBUnset' => ' ', + 'AlarmScore' => 'Alarme Score', 'Alert' => '', 'All' => '', 'Apply' => '', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => '', 'In' => '', 'Include' => '', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => '', 'Iris' => 'Iris', 'KeyString' => ' ', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => ' ', 'PleaseWait' => ' ', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => '', 'PostEventImageBuffer' => 'Post Event Image Count', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => '', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => '', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => '', 'RemoteHostName' => ' ', 'RemoteHostPath' => ' ', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => '', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => '', diff --git a/web/lang/hu_hu.php b/web/lang/hu_hu.php index 130899a27..437a40edd 100644 --- a/web/lang/hu_hu.php +++ b/web/lang/hu_hu.php @@ -130,6 +130,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Maximális FPS riasztott állapotban', 'AlarmPx' => 'Riasztó képpont', 'AlarmRGBUnset' => 'Be kell állítani egy RGB színt a riasztáshoz', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Figyelem', 'All' => 'Mind', 'Apply' => 'Alkalmaz', @@ -411,6 +412,7 @@ $SLANG = array( 'Images' => 'Kép', 'In' => 'In', 'Include' => 'Beágyaz', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertálva', 'Iris' => 'Írisz', 'KeyString' => 'Kulcs karaktersor', @@ -583,6 +585,8 @@ $SLANG = array( 'Play' => 'Lejátszás', 'PlayAll' => 'Mind lejátszása', 'PleaseWait' => 'Kérlek várj...', + 'Plugin' => 'Plugin', + 'Plugins' => 'Plugins', 'Point' => 'Pont', 'PostEventImageBuffer' => 'Esemény utáni képkockák a pufferben', 'PreEventImageBuffer' => 'Esemény elötti képkockák a pufferben', @@ -597,6 +601,7 @@ $SLANG = array( 'Record' => 'Felvétel', 'RefImageBlendPct' => 'Változás a referenciaképtől %-ban', 'Refresh' => 'Frissítés', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Hálózati', 'RemoteHostName' => 'Hálózati IP cím/hosztnév', 'RemoteHostPath' => 'A kép elérési útvonala', @@ -610,6 +615,7 @@ $SLANG = array( 'ReplayAll' => 'Mindet', 'ReplayGapless' => 'Szünet nélkülieket', 'ReplaySingle' => 'Egyenként', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Alapértékre', 'ResetEventCounts' => 'Eseményszámláló nullázása', 'Restart' => 'A szolgáltatás újraindítása', @@ -712,6 +718,7 @@ $SLANG = array( 'UpdateNotNecessary' => 'Nem szükséges a frissítés.', 'Updated' => 'Frissítve', 'Upload' => 'Feltöltés', + 'UsedPlugins' => 'Used Plugins', 'UseFilter' => 'Szűrőt használ', 'UseFilterExprsPost' => ' szürés  használata', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => ' ', // This is used at the beginning of the phrase 'use N filter expressions' diff --git a/web/lang/it_it.php b/web/lang/it_it.php index 8842cfacf..9ac1b4d91 100644 --- a/web/lang/it_it.php +++ b/web/lang/it_it.php @@ -93,6 +93,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'FPS massimi durante l\'allarme', 'AlarmPx' => 'Pixel Allarme', 'AlarmRGBUnset' => 'Devi settare un colore RGB di allarme', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Attenzione', 'All' => 'Tutto', 'Apply' => 'Applica', @@ -376,6 +377,7 @@ $SLANG = array( 'Images' => 'Immagini', 'In' => 'In', 'Include' => 'Includi', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertito', 'Iris' => 'Iris', 'KeyString' => 'Stringa Chiave', @@ -548,6 +550,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Vedi tutti', 'PleaseWait' => 'Attendere prego', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Punto', 'PostEventImageBuffer' => 'Buffer di immagini Dopo Evento', @@ -563,6 +566,7 @@ $SLANG = array( 'Record' => 'Registra', 'RefImageBlendPct' => 'Riferimento Miscela Immagine percentuale', 'Refresh' => 'Aggiorna', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remoto', 'RemoteHostName' => 'Nome dell\'Host Remoto', 'RemoteHostPath' => 'Percorso dell\'Host Remoto', @@ -576,6 +580,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Resetta', 'ResetEventCounts' => 'Resetta Contatore Eventi', 'Restart' => 'Riavvia', diff --git a/web/lang/ja_jp.php b/web/lang/ja_jp.php index 89f19bb1d..9449bc063 100644 --- a/web/lang/ja_jp.php +++ b/web/lang/ja_jp.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'װ Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'x', 'All' => 'S', 'Apply' => 'Kp', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'gݍ', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => ']', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => '҂', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => ' Ұ ޯ̧', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => '^', 'RefImageBlendPct' => 'Ұ Q %', 'Refresh' => 'ŐV̏ɍXV', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Ӱ', 'RemoteHostName' => 'Ӱ ν ', 'RemoteHostPath' => 'Ӱ ν ߽', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => ' ؾ', 'Restart' => 'ċN', diff --git a/web/lang/nl_nl.php b/web/lang/nl_nl.php index 877fddec0..2810ef049 100644 --- a/web/lang/nl_nl.php +++ b/web/lang/nl_nl.php @@ -87,6 +87,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'U moet een RGB alarm kleur keizen', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Waarschuwing', 'All' => 'Alle', 'Apply' => 'Voer uit', @@ -368,6 +369,7 @@ $SLANG = array( 'Images' => 'Fotos', 'In' => 'In', 'Include' => 'voeg in', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Omgedraaid', 'Iris' => 'Iris', 'KeyString' => 'Sleutel waarde', @@ -540,6 +542,8 @@ $SLANG = array( 'Play' => 'Speel', 'PlayAll' => 'Speel Alles', 'PleaseWait' => 'Wacht A.U.B.', + 'Plugin' => 'Plugin', + 'Plugins' => 'Plugins', 'Point' => 'Punt', 'PostEventImageBuffer' => 'Post gebeurtenis foto Buffer', 'PreEventImageBuffer' => 'Pre gebeurtenis foto Buffer', @@ -554,6 +558,7 @@ $SLANG = array( 'Record' => 'Record', 'RefImageBlendPct' => 'Referentie foto Blend %ge', 'Refresh' => 'Ververs', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Naam', 'RemoteHostPath' => 'Remote Host Pad', @@ -567,6 +572,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Gebeurtenissen', 'ReplayGapless' => 'Opvolgende Gebeurtenissen', 'ReplaySingle' => 'Enkele Gebeurtenis', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Herstel', 'ResetEventCounts' => 'Herstel gebeurtenis teller', 'Restart' => 'Herstart', @@ -668,6 +674,7 @@ $SLANG = array( 'UpdateAvailable' => 'Een update voor ZoneMinder is beschikbaar', 'UpdateNotNecessary' => 'Geen update noodzakelijk', 'Updated' => 'Ververst', // Added - 2011-06-16 + 'UsedPlugins' => 'Used Plugins', 'UseFilter' => 'Gebruik Filter', 'UseFilterExprsPost' => ' filter expressies', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Gebruik ', // This is used at the beginning of the phrase 'use N filter expressions' diff --git a/web/lang/pl_pl.php b/web/lang/pl_pl.php index 9ba171fcb..43b16c131 100644 --- a/web/lang/pl_pl.php +++ b/web/lang/pl_pl.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Gotowosc', 'All' => 'Wszystko', 'Apply' => 'Zastosuj', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'Docz', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Odwrcony', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => 'Prosz czeka', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Bufor obrazw po zdarzeniu', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => 'Zapis', 'RefImageBlendPct' => 'Miks z obrazem odniesienia', 'Refresh' => 'Odwie', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Zdalny', 'RemoteHostName' => 'Nazwa zdalnego hosta', 'RemoteHostPath' => 'Scieka zdalnego hosta', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Kasuj licznik zdarze', 'Restart' => 'Restart', diff --git a/web/lang/pt_br.php b/web/lang/pt_br.php index 91830ebf4..0b36944bb 100644 --- a/web/lang/pt_br.php +++ b/web/lang/pt_br.php @@ -28,6 +28,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Pixel de Alarme', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alerta', 'All' => 'Tudo', 'Apply' => 'Aplicar', @@ -311,6 +312,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'Incluir', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Invertido', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -483,6 +485,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => 'Por Favor Espere', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Buffer de imagem ps evento', @@ -498,6 +501,7 @@ $SLANG = array( 'Record' => 'Gravar', 'RefImageBlendPct' => 'Referncia de imagem Blend %ge', 'Refresh' => 'Atualizar', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remoto', 'RemoteHostName' => 'Nome do host remoto', 'RemoteHostPath' => 'Caminho do host remoto', @@ -511,6 +515,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetar contagem de eventos', 'Restart' => 'Reiniciar', diff --git a/web/lang/ro_ro.php b/web/lang/ro_ro.php index 74da43406..a81ffdb12 100644 --- a/web/lang/ro_ro.php +++ b/web/lang/ro_ro.php @@ -59,6 +59,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => 'Alarm Px', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Alert', 'All' => 'Toate', 'Apply' => 'Accept', @@ -342,6 +343,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => 'Includ', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Inversă', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -514,6 +516,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => 'Vă rugăm aşteptaţi', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Zona tampon post eveniment', @@ -529,6 +532,7 @@ $SLANG = array( 'Record' => 'Înregistrare', 'RefImageBlendPct' => 'Combinare imagine referinta(%)', 'Refresh' => 'Actualizează', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', @@ -542,6 +546,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Reporneşte', diff --git a/web/lang/ru_ru.php b/web/lang/ru_ru.php index 0c4bfbf3b..de86a53c0 100644 --- a/web/lang/ru_ru.php +++ b/web/lang/ru_ru.php @@ -88,6 +88,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Alarm Maximum FPS', 'AlarmPx' => ' .', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', + 'AlarmScore' => 'Alarme Score', 'Alert' => '', 'All' => '', 'Apply' => '', @@ -371,6 +372,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'In', 'Include' => '', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => '', 'Iris' => 'Iris', 'KeyString' => 'Key String', @@ -543,6 +545,7 @@ $SLANG = array( 'Play' => 'Play', 'PlayAll' => 'Play All', 'PleaseWait' => ' ', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => ' ', @@ -558,6 +561,7 @@ $SLANG = array( 'Record' => 'Record', 'RefImageBlendPct' => ' , %', 'Refresh' => '', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => '', 'RemoteHostName' => ' ', 'RemoteHostPath' => ' ', @@ -571,6 +575,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'Reset', 'ResetEventCounts' => ' ', 'Restart' => '', diff --git a/web/lang/se_se.php b/web/lang/se_se.php index 968804fed..8a48addf5 100644 --- a/web/lang/se_se.php +++ b/web/lang/se_se.php @@ -89,6 +89,7 @@ $SLANG = array( 'AlarmMaximumFPS' => 'Max. ramar/s fr larm', 'AlarmPx' => 'Larmpunkter', 'AlarmRGBUnset' => 'Du mste stta en frg fr RGB-larm', + 'AlarmScore' => 'Alarme Score', 'Alert' => 'Varning', 'All' => 'Alla', 'Apply' => 'Lgg till', @@ -372,6 +373,7 @@ $SLANG = array( 'Images' => 'Images', 'In' => 'I', 'Include' => 'Inkludera', + 'IncludeNatDet' => 'Include Native Detection', 'Inverted' => 'Inverterad', 'Iris' => 'Iris', 'KeyString' => 'Nyckelstrng', @@ -544,6 +546,7 @@ $SLANG = array( 'Play' => 'Spela', 'PlayAll' => 'Visa alla', 'PleaseWait' => 'Vnta...', + 'Plugin' => 'Plugin', 'Plugins' => 'Plugins', 'Point' => 'Punkt', 'PostEventImageBuffer' => 'Post Event Image Count', @@ -559,6 +562,7 @@ $SLANG = array( 'Record' => 'Spela in', 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Uppdatera', + 'ReInitNatDet' => 'Reinit. Native Detection', 'Remote' => 'Fjrr', 'RemoteHostName' => 'Fjrrnamn', 'RemoteHostPath' => 'Fjrrskvg', @@ -572,6 +576,7 @@ $SLANG = array( 'ReplayAll' => 'Alla hndelser', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Ensam hndelse', + 'RequireNatDet' => 'Require Native Detection', 'Reset' => 'terstll', 'ResetEventCounts' => 'terstll hndelserknare', 'Restart' => 'terstart', diff --git a/web/skins/classic/css/classic/views/Makefile.am b/web/skins/classic/css/classic/views/Makefile.am index d07eaac4f..c59c117d3 100644 --- a/web/skins/classic/css/classic/views/Makefile.am +++ b/web/skins/classic/css/classic/views/Makefile.am @@ -23,6 +23,7 @@ dist_web_DATA = \ montage.css \ montage_freeform.css \ options.css \ + plugin.css \ stats.css \ timeline.css \ timeline.css.php \ diff --git a/web/skins/classic/css/classic/views/plugin.css b/web/skins/classic/css/classic/views/plugin.css index 040e26558..1490026fc 100644 --- a/web/skins/classic/css/classic/views/plugin.css +++ b/web/skins/classic/css/classic/views/plugin.css @@ -3,6 +3,10 @@ margin: 0 2px; } +#settingsPanel input[type=submit] { + margin: 8px 4px; +} + #pluginSettings { border-collapse: collapse; } diff --git a/web/skins/classic/css/classic/views/zone.css b/web/skins/classic/css/classic/views/zone.css index 2f6754275..f0a4a819f 100644 --- a/web/skins/classic/css/classic/views/zone.css +++ b/web/skins/classic/css/classic/views/zone.css @@ -93,3 +93,22 @@ #zonePoints table a { margin: 0 2px; } + +a.pluginNotEnabled { + text-decoration: none; + color: grey; +} + +a.pluginError { + text-decoration: none; + color: red; +} + +a.pluginNotActive { + color: orange; +} + +a.pluginActive { + color: green; +} + diff --git a/web/skins/classic/css/flat/views/Makefile.am b/web/skins/classic/css/flat/views/Makefile.am index dff0aa29b..50ceddb3f 100644 --- a/web/skins/classic/css/flat/views/Makefile.am +++ b/web/skins/classic/css/flat/views/Makefile.am @@ -23,6 +23,7 @@ dist_web_DATA = \ montage.css \ montage_freeform.css \ options.css \ + plugin.css \ stats.css \ timeline.css \ timeline.css.php \ diff --git a/web/skins/classic/css/flat/views/plugin.css b/web/skins/classic/css/flat/views/plugin.css index 040e26558..1490026fc 100644 --- a/web/skins/classic/css/flat/views/plugin.css +++ b/web/skins/classic/css/flat/views/plugin.css @@ -3,6 +3,10 @@ margin: 0 2px; } +#settingsPanel input[type=submit] { + margin: 8px 4px; +} + #pluginSettings { border-collapse: collapse; } diff --git a/web/skins/classic/css/flat/views/zone.css b/web/skins/classic/css/flat/views/zone.css index 2f6754275..f0a4a819f 100644 --- a/web/skins/classic/css/flat/views/zone.css +++ b/web/skins/classic/css/flat/views/zone.css @@ -93,3 +93,22 @@ #zonePoints table a { margin: 0 2px; } + +a.pluginNotEnabled { + text-decoration: none; + color: grey; +} + +a.pluginError { + text-decoration: none; + color: red; +} + +a.pluginNotActive { + color: orange; +} + +a.pluginActive { + color: green; +} + diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index b09a646da..4ccecbeee 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -55,6 +55,7 @@ var popupSizes = { 'montage': { 'width': -1, 'height': -1 }, 'optionhelp': { 'width': 400, 'height': 320 }, 'options': { 'width': 1000, 'height': 660 }, + 'plugin': { 'addWidth': 16, 'addHeight': 48 }, 'preset': { 'width': 300, 'height': 120 }, 'settings': { 'width': 220, 'height': 225 }, 'state': { 'width': 370, 'height': 134 }, diff --git a/web/skins/classic/views/Makefile.am b/web/skins/classic/views/Makefile.am index 5cd01dcc3..3d27f5af9 100644 --- a/web/skins/classic/views/Makefile.am +++ b/web/skins/classic/views/Makefile.am @@ -40,6 +40,7 @@ dist_web_DATA = \ none.php \ optionhelp.php \ options.php \ + plugin.php \ postlogin.php \ settings.php \ state.php \ diff --git a/web/skins/classic/views/js/Makefile.am b/web/skins/classic/views/js/Makefile.am index ba1845e0c..19acd4716 100644 --- a/web/skins/classic/views/js/Makefile.am +++ b/web/skins/classic/views/js/Makefile.am @@ -33,6 +33,8 @@ dist_web_DATA = \ montage.js \ montage.js.php \ options.js.php \ + plugin.js \ + plugin.js.php \ postlogin.js \ state.js \ state.js.php \ diff --git a/web/skins/classic/views/js/plugin.js b/web/skins/classic/views/js/plugin.js index f03768317..54d39a872 100644 --- a/web/skins/classic/views/js/plugin.js +++ b/web/skins/classic/views/js/plugin.js @@ -5,9 +5,49 @@ function validateForm( form ) function submitForm( form ) { + var cb = form.getElementsByTagName('input'); + for ( var i = 0; i < cb.length; i++) + { + if ( cb[i].type == 'checkbox' && !cb[i].checked ) // if this is an unchecked checkbox + { + cb[i].value = 0; // set the value to "off" + cb[i].checked = true; // make sure it submits + } + } form.submit(); } +function saveChanges( element ) +{ + var form = element.form; + if ( validateForm( form ) ) + { + submitForm( form ); + return( true ); + } + return( false ); +} + +function applyDependencies() +{ + var form = document.pluginForm; + for ( var option in dependencies ) + { + var enabled = true; + for ( var name in dependencies[option] ) + { + if (form.elements['pluginOpt[' + name + ']'].value != dependencies[option][name]) + { + form.elements['pluginOpt[' + option + ']'].disabled = true; + enabled = false; + break; + } + + } + if (enabled) + form.elements['pluginOpt[' + option + ']'].disabled = false; + } +} function limitRange( field, minValue, maxValue ) { diff --git a/web/skins/classic/views/js/plugin.js.php b/web/skins/classic/views/js/plugin.js.php index e69de29bb..d07a5e0b8 100644 --- a/web/skins/classic/views/js/plugin.js.php +++ b/web/skins/classic/views/js/plugin.js.php @@ -0,0 +1,17 @@ +var dependencies = {}; + +dependencies[''] = {}; + +dependencies[''][''] = ''; + diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 1260bb77e..a54fb9ee0 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -106,6 +106,7 @@ if ( ! empty($_REQUEST['mid']) ) { 'Triggers' => "", 'V4LMultiBuffer' => '', 'V4LCapturesPerFrame' => 1, + 'DoNativeMotDet' => true ); } @@ -471,6 +472,7 @@ if ( $tab != 'general' ) + + checked="checked"/> 0 ) { $monitor = dbFetchMonitor ( $mid ); $plugin = $_REQUEST['pl']; -$plugin_path = dirname(ZM_PLUGINS_CONFIG_PATH)."/".$plugin; +$plugin_path = dirname($_SERVER['SCRIPT_FILENAME'])."/plugins/".$plugin; $focusWindow = true; -xhtmlHeaders(__FILE__, $SLANG['Plugin'] ); - - -$pluginOptions=array( - 'Enabled'=>array( - 'Type'=>'select', - 'Name'=>'Enabled', - 'Choices'=>'yes,no', - 'Value'=>'no' - ) - ); +$generalOptions=array( + 'Enabled'=>array( + 'Type'=>'select', + 'Name'=>'Enabled', + 'Choices'=>'Yes,No', + 'Value'=>'No' + ), + 'RequireNatDet'=>array( + 'Type'=>'select', + 'Name'=>'RequireNatDet', + 'Choices'=>'Yes,No', + 'Value'=>'No', + 'Require'=>array( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ) + ) + ), + 'IncludeNatDet'=>array( + 'Type'=>'select', + 'Name'=>'IncludeNatDet', + 'Choices'=>'Yes,No', + 'Value'=>'No', + 'Require'=>array( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ), + array( + 'Name'=>'RequireNatDet', + 'Value'=>'Yes' + ) + ) + ), + 'ReInitNatDet'=>array( + 'Type'=>'select', + 'Name'=>'ReInitNatDet', + 'Choices'=>'Yes,No', + 'Value'=>'No', + 'Require'=>array( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ), + array( + 'Name'=>'RequireNatDet', + 'Value'=>'Yes' + ) + ) + ), + 'AlarmeScore'=>array( + 'Type'=>'integer', + 'Name'=>'AlarmeScore', + 'Min'=>'1', + 'Max'=>'100', + 'Value'=>'99', + 'Require'=>array( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ) + ) + ) +); +$options=$generalOptions; $optionNames=array(); if(file_exists($plugin_path."/config.php")) { include_once($plugin_path."/config.php"); -} + if(isset($pluginOptions)) + foreach( $pluginOptions as $optionKey => $optionValue ) + { + // Set default dependency information if not set in configuration file + if(!isset($optionValue['Require'])) + $optionValue['Require'] = array ( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ) + ); + $options[$optionKey]=$optionValue; + } +} $sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?'; foreach( dbFetchAll( $sql, NULL, array( $mid, $zid, $plugin ) ) as $popt ) { - if(array_key_exists($popt['Name'], $pluginOptions) - && $popt['Type']==$pluginOptions[$popt['Name']]['Type'] - && $popt['Choices']==$pluginOptions[$popt['Name']]['Choices'] - ) + if(array_key_exists($popt['Name'], $options) + && $popt['Type']==$options[$popt['Name']]['Type']) { - $pluginOptions[$popt['Name']]=$popt; array_push($optionNames, $popt['Name']); + + // Backup dependency information + $require = ''; + if(isset($options[$popt['Name']]['Require'])) + $require = $options[$popt['Name']]['Require']; + + // Set value from database + $options[$popt['Name']]=$popt; + + // Restore dependancy information from backup + if(!empty($require)) + $options[$popt['Name']]['Require'] = $require; + + // Set default dependancy information if not set in configuration + else if($popt['Name'] != 'Enabled') + $options[$popt['Name']]['Require'] = array ( + array( + 'Name'=>'Enabled', + 'Value'=>'Yes' + ) + ); } else { dbQuery('DELETE FROM PluginsConfig WHERE Id=?', array( $popt['Id'] ) ); } } -foreach($pluginOptions as $name => $values) + +foreach($options as $name => $values) { if(!in_array($name, $optionNames)) { - $popt=$pluginOptions[$name]; - $sql="INSERT INTO PluginsConfig VALUES ('',?,?,?,?,?,?,?)"; - dbQuery($sql, array( $popt['Name'], $popt['Value'], $popt['Type'], $popt['Choices'], $mid, $zid, $plugin ) ); + $popt=$options[$name]; + switch($popt['Type']) + { + case "select": + $sql="INSERT INTO PluginsConfig VALUES ('',?,?,?,?,'','',?,?,?)"; + dbQuery($sql, array( $popt['Name'], $popt['Value'], $popt['Type'], $popt['Choices'], $mid, $zid, $plugin ) ); + break; + case "integer": + $sql="INSERT INTO PluginsConfig VALUES ('',?,?,?,'',?,?,?,?,?)"; + dbQuery($sql, array( $popt['Name'], $popt['Value'], $popt['Type'], $popt['Min'], $popt['Max'], $mid, $zid, $plugin ) ); + break; + case "checkbox": + case "text": + default: + $sql="INSERT INTO PluginsConfig VALUES ('',?,?,?,'','','',?,?,?)"; + dbQuery($sql, array( $popt['Name'], $popt['Value'], $popt['Type'], $mid, $zid, $plugin ) ); + } } } $PLANG=array(); -if(file_exists($plugin_path."/lang/".$user['Language'].".php")) { - include_once($plugin_path."/lang/".$user['Language'].".php"); +$lang_path = $plugin_path."/lang"; +$userLangFile = $lang_path."/".$user['Language'].".php"; +if (isset($user['Language']) && file_exists($userLangFile)) { + include_once($userLangFile); +} else { + $systemLangFile = $lang_path."/".ZM_LANG_DEFAULT.".php"; + if (file_exists($systemLangFile)) { + include_once($systemLangFile); + } else { + $fallbackLangFile = $lang_path."/en_gb.php"; + if (file_exists($fallbackLangFile)) { + include_once($fallbackLangFile); + } + } } function pLang($name) { + global $SLANG; global $PLANG; - if(array_key_exists($name, $PLANG)) + if(array_key_exists($name, $SLANG)) + return $SLANG[$name]; + else if(array_key_exists($name, $PLANG)) return $PLANG[$name]; else return $name; } +function isEnabled($param) +{ + global $options; + $option = $options[$param]; + if (!isset($option['Require'])) + return true; + foreach($option['Require'] as $req_couple) + { + $name = $req_couple['Name']; + if (!array_key_exists($name, $options)) + continue; + if ($req_couple['Value'] != $options[$name]['Value']) + return false; + } + return true; +} +xhtmlHeaders(__FILE__, $SLANG['Plugin'] ); ?>
@@ -117,7 +250,7 @@ function pLang($name) $popt) +foreach($options as $name => $popt) { ?> @@ -125,13 +258,15 @@ foreach($pluginOptions as $name => $popt) switch($popt['Type']) { case "checkbox": - echo "CHECKBOX"; + ?> + + + + + + + "; } ?> diff --git a/web/skins/classic/views/zone.php b/web/skins/classic/views/zone.php index 121835913..8600ed41a 100644 --- a/web/skins/classic/views/zone.php +++ b/web/skins/classic/views/zone.php @@ -212,6 +212,60 @@ xhtmlHeaders(__FILE__, $SLANG['Zone'] ); + 0) +{ + // Get plugin status from zmu command line + exec(escapeshellcmd(getZmuCommand(" -p -m".$mid)), $cmdOutput, $retval); + if($retval == 0) + { + echo "\n"; + } +} +?>
> - onchange="applyDependencies()" > $popt) - >>
".$SLANG['Plugins']."\n"; + $plName = ""; + $plReg = "0"; + $plConf = "0"; + foreach($cmdOutput as $key => $line) + { + // Skip header lines or "No plugin found" + if($key < 2) continue; + // Parse line + $pl = preg_split ('/[\s]+/ ', trim($line)); + $offset = 0; + if(sizeof($pl) == 9) + { + $plName = $pl[0]; + $plReg = $pl[1]; + $plConf = $pl[2]; + $offset = 3; + } + // Skip line if zone mismatch + if($pl[$offset] != $zid) continue; + // Select class and set display setting + $class = ''; + $dspPlConf = true; + if(!ZM_LOAD_PLUGINS) { + $class = " class=\"pluginNotEnabled\""; + $dspPlConf = false; + } else if($plReg == "0" || (($plReg == "1") && ($plConf == "0"))) { + $class = " class=\"pluginError\""; + $dspPlConf = false; + } else if(($pl[1 + $offset] == "1") && ($pl[2 + $offset] == "0")) { + $class = " class=\"pluginNotActive\""; + } else if($pl[2 + $offset] == "1") { + $class = " class=\"pluginActive\""; + } + // Display plugin name and enable link + if($dspPlConf) + echo "".$plName."
\n"; + else + echo "".$plName."
\n"; + } + echo "