Merge branch 'master' into fix_event_viewing
This commit is contained in:
commit
087b7abb94
|
@ -137,11 +137,11 @@ set(ZM_WEB_USER "" CACHE STRING
|
||||||
set(ZM_WEB_GROUP "" CACHE STRING
|
set(ZM_WEB_GROUP "" CACHE STRING
|
||||||
"The group apache or the local web server runs on,
|
"The group apache or the local web server runs on,
|
||||||
Leave empty to be the same as the web user")
|
Leave empty to be the same as the web user")
|
||||||
set(ZM_DIR_EVENTS "events" CACHE PATH
|
set(ZM_DIR_EVENTS "${ZM_CONTENTDIR}/events" CACHE PATH
|
||||||
"Location where events are recorded to, default: events")
|
"Location where events are recorded to, default: ZM_CONTENTDIR/events")
|
||||||
set(ZM_DIR_IMAGES "events" CACHE PATH
|
set(ZM_DIR_IMAGES "${ZM_CONTENTDIR}/images" CACHE PATH
|
||||||
"Location where images, not directly associated with events,
|
"Location where images, not directly associated with events,
|
||||||
are recorded to, default: images")
|
are recorded to, default: ZM_CONTENTDIR/images")
|
||||||
set(ZM_DIR_SOUNDS "sounds" CACHE PATH
|
set(ZM_DIR_SOUNDS "sounds" CACHE PATH
|
||||||
"Location to look for optional sound files, default: sounds")
|
"Location to look for optional sound files, default: sounds")
|
||||||
set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH
|
set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH
|
||||||
|
@ -154,7 +154,7 @@ set(ZM_PATH_ARP "" CACHE PATH
|
||||||
"Full path to compatible arp binary. Leave empty for automatic detection.")
|
"Full path to compatible arp binary. Leave empty for automatic detection.")
|
||||||
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
|
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
|
||||||
"Location of ZoneMinder configuration, default system config directory")
|
"Location of ZoneMinder configuration, default system config directory")
|
||||||
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/conf.d" CACHE PATH
|
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/zm/conf.d" CACHE PATH
|
||||||
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
|
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
|
||||||
set(ZM_EXTRA_LIBS "" CACHE STRING
|
set(ZM_EXTRA_LIBS "" CACHE STRING
|
||||||
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
|
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
|
||||||
|
@ -801,11 +801,12 @@ endif(ZM_PERL_SEARCH_PATH)
|
||||||
|
|
||||||
# If this is an out-of-source build, copy the files we need to the binary directory
|
# If this is an out-of-source build, copy the files we need to the binary directory
|
||||||
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf.d")
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
|
||||||
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||||
|
|
||||||
# Generate files from the .in files
|
# Generate files from the .in files
|
||||||
configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY)
|
configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY)
|
||||||
|
configure_file(conf.d/01-system-paths.conf.in "${CMAKE_CURRENT_BINARY_DIR}/conf.d/01-system-paths.conf" @ONLY)
|
||||||
configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
|
configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
|
||||||
configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY)
|
configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY)
|
||||||
configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY)
|
configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY)
|
||||||
|
@ -850,7 +851,7 @@ endif(zmconfgen_result EQUAL 0)
|
||||||
|
|
||||||
# Install zm.conf and conf.d subfolder
|
# Install zm.conf and conf.d subfolder
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}")
|
||||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/conf.d/" DESTINATION "${ZM_CONFIG_SUBDIR}")
|
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/conf.d/" DESTINATION "${ZM_CONFIG_SUBDIR}" PATTERN "*.in" EXCLUDE)
|
||||||
|
|
||||||
# Uninstall target
|
# Uninstall target
|
||||||
configure_file(
|
configure_file(
|
||||||
|
|
11
INSTALL
11
INSTALL
|
@ -45,9 +45,20 @@ Possible configuration options:
|
||||||
ZM_DB_NAME Name of ZoneMinder database, default: zm
|
ZM_DB_NAME Name of ZoneMinder database, default: zm
|
||||||
ZM_DB_USER Name of ZoneMinder database user, default: zmuser
|
ZM_DB_USER Name of ZoneMinder database user, default: zmuser
|
||||||
ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
|
ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
|
||||||
|
ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled
|
||||||
|
ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled
|
||||||
|
ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled
|
||||||
ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
|
ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
|
||||||
ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
|
ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
|
||||||
|
ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events
|
||||||
|
ZM_DIR_IMAGES Location where images, not directly associated with events, are recorded to, default: ZM_CONTENTDIR/images
|
||||||
|
ZM_DIR_SOUNDS Location to look for optional sound files, default: sounds
|
||||||
|
ZM_PATH_ZMS Web url to zms streaming server, default: /cgi-bin/nph-zms
|
||||||
Advanced:
|
Advanced:
|
||||||
|
ZM_PATH_MAP Location to save mapped memory files, default: /dev/shm
|
||||||
|
ZM_PATH_ARP Full path to compatible arp binary. Leave empty for automatic detection.
|
||||||
|
ZM_CONFIG_DIR Location of the main ZoneMinder config file, zm.conf. default: /etc/zm
|
||||||
|
ZM_CONFIG_SUBDIR Location of custom config files. default: ZM_CONFIG_DIR/conf.d
|
||||||
ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora
|
ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora
|
||||||
ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB
|
ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB
|
||||||
ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
|
ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
|
||||||
|
|
|
@ -27,8 +27,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
|
||||||
|
|
||||||
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
|
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
|
||||||
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
||||||
- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/)
|
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||||
- Fedora via [zmrepo](http://zmrepo.zoneminder.com/)
|
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||||
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
||||||
- Mageia from their default repository
|
- Mageia from their default repository
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ echo "Database host : $ZM_DB_HOST"
|
||||||
echo "Database name : $ZM_DB_NAME"
|
echo "Database name : $ZM_DB_NAME"
|
||||||
echo "Database user : $ZM_DB_USER"
|
echo "Database user : $ZM_DB_USER"
|
||||||
echo "Database password : Not shown"
|
echo "Database password : Not shown"
|
||||||
|
echo "Database SSL CA Cert : $ZM_DB_SSL_CA_CERT"
|
||||||
|
echo "Database SSL Client Key : $ZM_DB_SSL_CLIENT_KEY"
|
||||||
|
echo "Database SSL Client Cert : $ZM_DB_SSL_CLIENT_CERT"
|
||||||
|
|
||||||
|
|
||||||
CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE"
|
CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE"
|
||||||
|
@ -72,6 +75,9 @@ echo "set(ZM_DB_HOST \"$ZM_DB_HOST\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CA_CERT \"$ZM_DB_SSL_CA_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CLIENT_KEY \"$ZM_DB_SSL_CLIENT_KEY\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CLIENT_CERT \"$ZM_DB_SSL_CLIENT_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Wrote zm_conf.cmake"
|
echo "Wrote zm_conf.cmake"
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder System Paths Configuration
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# This config file contains the variables previously found under Options -> Paths
|
||||||
|
#
|
||||||
|
# *** DO NOT EDIT THIS FILE ***
|
||||||
|
#
|
||||||
|
# To make custom changes to the variables below, create a new configuration
|
||||||
|
# file, with an extention of .conf, containing your desired modifications.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Full path to the folder events are recorded to.
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_EVENTS=@ZM_DIR_EVENTS@
|
||||||
|
|
||||||
|
# Full path to the folder images, not directly associated with events,
|
||||||
|
# are recorded to.
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_IMAGES=@ZM_DIR_IMAGES@
|
||||||
|
|
||||||
|
# Foldername under the webroot where ZoneMinder looks for optional sound files
|
||||||
|
# to play when an alarm is detected.
|
||||||
|
ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@
|
||||||
|
|
||||||
|
# Full path to the folder where exported archives are stored
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_EXPORTS=@ZM_TMPDIR@
|
||||||
|
|
||||||
|
# ZoneMinder url path to the zms streaming server
|
||||||
|
ZM_PATH_ZMS=@ZM_PATH_ZMS@
|
||||||
|
|
||||||
|
# Full Path to ZoneMinder's mapped memory files
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_MAP=@ZM_PATH_MAP@
|
||||||
|
|
||||||
|
# Full Path to ZoneMinder's socket folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_SOCKS=@ZM_SOCKDIR@
|
||||||
|
|
||||||
|
# Full path to ZoneMinder's log folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_LOGS=@ZM_LOGDIR@
|
||||||
|
|
||||||
|
# Full path to ZoneMinder's swap folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_SWAP=@ZM_TMPDIR@
|
||||||
|
|
||||||
|
# Full path to optional arp binary
|
||||||
|
# ZoneMinder will find the arp binary automatically on most systems
|
||||||
|
ZM_PATH_ARP=@ZM_PATH_ARP@
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder Multiserver Configuration
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
||||||
|
# You have been warned
|
||||||
|
#
|
||||||
|
# The name specified here must have a corresponding entry
|
||||||
|
# in the Servers tab under Options
|
||||||
|
#ZM_SERVER_HOST=
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
|
||||||
# You have been warned
|
|
||||||
#
|
|
||||||
# The name specified here must have a corresponding entry
|
|
||||||
# in the Servers tab under Options
|
|
||||||
ZM_SERVER_HOST=
|
|
||||||
|
|
|
@ -331,13 +331,13 @@ CREATE TABLE `Monitors` (
|
||||||
`Format` int(10) unsigned NOT NULL default '0',
|
`Format` int(10) unsigned NOT NULL default '0',
|
||||||
`V4LMultiBuffer` tinyint(1) unsigned,
|
`V4LMultiBuffer` tinyint(1) unsigned,
|
||||||
`V4LCapturesPerFrame` tinyint(3) unsigned,
|
`V4LCapturesPerFrame` tinyint(3) unsigned,
|
||||||
`Protocol` varchar(16) NOT NULL default '',
|
`Protocol` varchar(16) default '',
|
||||||
`Method` varchar(16) NOT NULL default '',
|
`Method` varchar(16) NOT NULL default '',
|
||||||
`Host` varchar(64),
|
`Host` varchar(64),
|
||||||
`Port` varchar(8) NOT NULL default '',
|
`Port` varchar(8) NOT NULL default '',
|
||||||
`SubPath` varchar(64) NOT NULL default '',
|
`SubPath` varchar(64) NOT NULL default '',
|
||||||
`Path` varchar(255),
|
`Path` varchar(255),
|
||||||
`Options` varchar(255) not null default '',
|
`Options` varchar(255) default '',
|
||||||
`User` varchar(64),
|
`User` varchar(64),
|
||||||
`Pass` varchar(64),
|
`Pass` varchar(64),
|
||||||
`Width` smallint(5) unsigned NOT NULL default '0',
|
`Width` smallint(5) unsigned NOT NULL default '0',
|
||||||
|
@ -602,6 +602,7 @@ INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
||||||
|
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Add some monitor preset values
|
-- Add some monitor preset values
|
||||||
|
|
|
@ -6,3 +6,4 @@ usr/share/perl5/ZoneMinder.pm
|
||||||
usr/share/zoneminder/db
|
usr/share/zoneminder/db
|
||||||
usr/share/zoneminder/www
|
usr/share/zoneminder/www
|
||||||
etc/zm
|
etc/zm
|
||||||
|
etc/zm/conf.d/*
|
||||||
|
|
|
@ -18,7 +18,6 @@ override_dh_auto_configure:
|
||||||
-DZM_TMPDIR=/var/tmp/zm \
|
-DZM_TMPDIR=/var/tmp/zm \
|
||||||
-DZM_LOGDIR=/var/log/zm \
|
-DZM_LOGDIR=/var/log/zm \
|
||||||
-DZM_WEBDIR=/usr/share/zoneminder/www \
|
-DZM_WEBDIR=/usr/share/zoneminder/www \
|
||||||
-DZM_CONTENTDIR=/var/cache/zoneminder \
|
|
||||||
-DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \
|
-DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \
|
||||||
-DZM_WEB_USER=www-data \
|
-DZM_WEB_USER=www-data \
|
||||||
-DZM_WEB_GROUP=www-data \
|
-DZM_WEB_GROUP=www-data \
|
||||||
|
|
|
@ -9,15 +9,15 @@ else(ZM_TARGET_DISTRO MATCHES "^el")
|
||||||
message([WARNING] "Unknown Build Option Detected" ...)
|
message([WARNING] "Unknown Build Option Detected" ...)
|
||||||
endif(ZM_TARGET_DISTRO MATCHES "^el")
|
endif(ZM_TARGET_DISTRO MATCHES "^el")
|
||||||
|
|
||||||
if((ZM_TARGET_DISTRO STREQUAL "el6") AND (ZM_WEB_USER STREQUAL "nginx"))
|
if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
|
||||||
message([FATAL_ERROR] "Nginx is Not a Supported Build Option on EL6 Target Distro" ...)
|
message([FATAL_ERROR] "Experimental Nginx support is currently only supported on Fedora" ...)
|
||||||
endif((ZM_TARGET_DISTRO STREQUAL "el6") AND (ZM_WEB_USER STREQUAL "nginx"))
|
endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
|
||||||
|
|
||||||
# Configure the zoneminder service files
|
# Configure the zoneminder service files
|
||||||
if(ZM_TARGET_DISTRO STREQUAL "el6")
|
if(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY)
|
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY)
|
||||||
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
||||||
configure_file(sysvinit/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||||
else(ZM_TARGET_DISTRO STREQUAL "el6")
|
else(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
||||||
if(ZM_WEB_USER STREQUAL "nginx")
|
if(ZM_WEB_USER STREQUAL "nginx")
|
||||||
|
@ -28,7 +28,7 @@ else(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
|
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
|
||||||
else(ZM_WEB_USER STREQUAL "nginx")
|
else(ZM_WEB_USER STREQUAL "nginx")
|
||||||
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
||||||
configure_file(systemd/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||||
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
||||||
endif(ZM_WEB_USER STREQUAL "nginx")
|
endif(ZM_WEB_USER STREQUAL "nginx")
|
||||||
endif(ZM_TARGET_DISTRO STREQUAL "el6")
|
endif(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
|
@ -55,10 +55,7 @@ install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WR
|
||||||
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
|
||||||
# Create symlinks
|
# Symlink the cake php temp folder to the ZoneMinder temp folder
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")")
|
install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")")
|
||||||
|
|
||||||
# Link to Cambozola
|
# Link to Cambozola
|
||||||
|
|
|
@ -16,7 +16,7 @@ Alias /zm "@ZM_WEBDIR@"
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
SSLRequireSSL
|
SSLRequireSSL
|
||||||
Options -Indexes +MultiViews +FollowSymLinks
|
Options -Indexes +MultiViews +FollowSymLinks
|
||||||
AllowOverride All
|
AllowOverride None
|
||||||
<IfModule mod_authz_core.c>
|
<IfModule mod_authz_core.c>
|
||||||
# Apache 2.4
|
# Apache 2.4
|
||||||
Require all granted
|
Require all granted
|
||||||
|
@ -31,7 +31,7 @@ Alias /zm "@ZM_WEBDIR@"
|
||||||
ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
|
ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
|
||||||
<Directory "@ZM_CGIDIR@">
|
<Directory "@ZM_CGIDIR@">
|
||||||
SSLRequireSSL
|
SSLRequireSSL
|
||||||
AllowOverride All
|
AllowOverride None
|
||||||
Options +ExecCGI +FollowSymLinks
|
Options +ExecCGI +FollowSymLinks
|
||||||
<IfModule mod_authz_core.c>
|
<IfModule mod_authz_core.c>
|
||||||
# Apache 2.4
|
# Apache 2.4
|
||||||
|
@ -44,3 +44,28 @@ ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
|
||||||
</IfModule>
|
</IfModule>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
|
# For better visibility, the following directives have been migrated from the
|
||||||
|
# default .htaccess files included with the CakePHP project.
|
||||||
|
# Parameters not set here are inherited from the parent directive above.
|
||||||
|
<Directory "@ZM_WEBDIR@/api">
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteRule ^$ app/webroot/ [L]
|
||||||
|
RewriteRule (.*) app/webroot/$1 [L]
|
||||||
|
RewriteBase /zm/api
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Directory "@ZM_WEBDIR@/api/app">
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteRule ^$ webroot/ [L]
|
||||||
|
RewriteRule (.*) webroot/$1 [L]
|
||||||
|
RewriteBase /zm/api
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Directory "@ZM_WEBDIR@/api/app/webroot">
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
|
RewriteBase /zm/api
|
||||||
|
</Directory>
|
||||||
|
|
|
@ -28,7 +28,7 @@ New installs
|
||||||
1. Unless you are already using MariaDB server, you need to ensure that the
|
1. Unless you are already using MariaDB server, you need to ensure that the
|
||||||
server is configured to start during boot and properly secured by running:
|
server is configured to start during boot and properly secured by running:
|
||||||
|
|
||||||
sudo dnf install mariadb-server
|
sudo yum install mariadb-server
|
||||||
sudo systemctl enable mariadb
|
sudo systemctl enable mariadb
|
||||||
sudo systemctl start mariadb.service
|
sudo systemctl start mariadb.service
|
||||||
mysql_secure_installation
|
mysql_secure_installation
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# ZoneMinder systemd unit file for CentOS 7
|
# ZoneMinder systemd unit file for RedHat distros and clones
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=ZoneMinder CCTV recording and security system
|
Description=ZoneMinder CCTV recording and security system
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
#
|
|
||||||
# ZoneMinder Apache configuration file
|
|
||||||
# With SSLRequire and HTTPS auto redirect
|
|
||||||
# Modify this configuration to suit your requirements
|
|
||||||
#
|
|
||||||
|
|
||||||
# Auto Redirect HTTP requests to HTTPS
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTPS} !=on
|
|
||||||
RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L]
|
|
||||||
|
|
||||||
Alias /zm "@ZM_WEBDIR@"
|
|
||||||
<Directory "@ZM_WEBDIR@">
|
|
||||||
SSLRequireSSL
|
|
||||||
Options -Indexes MultiViews FollowSymLinks
|
|
||||||
AllowOverride All
|
|
||||||
Order allow,deny
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
ScriptAlias /cgi-bin/zm "@ZM_CGIDIR@"
|
|
||||||
<Directory "@ZM_CGIDIR@">
|
|
||||||
SSLRequireSSL
|
|
||||||
AllowOverride All
|
|
||||||
Options ExecCGI FollowSymLinks
|
|
||||||
Order allow,deny
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
|
@ -109,6 +109,7 @@ Requires: perl(MIME::Lite)
|
||||||
Requires: perl(Net::SMTP)
|
Requires: perl(Net::SMTP)
|
||||||
Requires: perl(Net::FTP)
|
Requires: perl(Net::FTP)
|
||||||
Requires: perl(LWP::Protocol::https)
|
Requires: perl(LWP::Protocol::https)
|
||||||
|
Requires: ca-certificates
|
||||||
|
|
||||||
%{?with_init_systemd:Requires(post): systemd}
|
%{?with_init_systemd:Requires(post): systemd}
|
||||||
%{?with_init_systemd:Requires(post): systemd-sysv}
|
%{?with_init_systemd:Requires(post): systemd-sysv}
|
||||||
|
@ -162,7 +163,14 @@ too much degradation of performance.
|
||||||
%make_install
|
%make_install
|
||||||
|
|
||||||
# Remove unwanted files and folders
|
# Remove unwanted files and folders
|
||||||
find %{buildroot} \( -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
|
find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
|
||||||
|
|
||||||
|
# Recursively change shebang in all relevant scripts and set execute permission
|
||||||
|
find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!/usr/bin/bash\' {} \; -exec %{__chmod} 755 {} \;
|
||||||
|
|
||||||
|
# Use the system cacert file rather then the one bundled with CakePHP
|
||||||
|
%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
|
||||||
|
%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
|
||||||
|
|
||||||
%post
|
%post
|
||||||
%if 0%{?with_init_sysv}
|
%if 0%{?with_init_sysv}
|
||||||
|
@ -278,12 +286,18 @@ rm -rf %{_docdir}/%{name}-%{version}
|
||||||
%files
|
%files
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
|
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
|
||||||
|
|
||||||
|
# We want these two folders to have "normal" read permission
|
||||||
|
# compared to the folder contents
|
||||||
%dir %{_sysconfdir}/zm
|
%dir %{_sysconfdir}/zm
|
||||||
%dir %{_sysconfdir}/zm/conf.d
|
%dir %{_sysconfdir}/zm/conf.d
|
||||||
|
|
||||||
|
# Config folder contents contain sensitive info
|
||||||
|
# and should not be readable by normal users
|
||||||
%{_sysconfdir}/zm/conf.d/README
|
%{_sysconfdir}/zm/conf.d/README
|
||||||
# Always overwrite zm.conf now that ZoneMinder supports conf.d folder
|
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
|
||||||
%attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
|
|
||||||
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
|
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
|
||||||
|
%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
|
||||||
|
|
||||||
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf
|
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf
|
||||||
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
||||||
|
@ -297,6 +311,7 @@ rm -rf %{_docdir}/%{name}-%{version}
|
||||||
%{_unitdir}/zoneminder.service
|
%{_unitdir}/zoneminder.service
|
||||||
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
|
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
|
||||||
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
|
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
|
||||||
|
%{_bindir}/zmsystemctl.pl
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?with_init_sysv}
|
%if 0%{?with_init_sysv}
|
||||||
|
@ -318,7 +333,6 @@ rm -rf %{_docdir}/%{name}-%{version}
|
||||||
%{_bindir}/zmvideo.pl
|
%{_bindir}/zmvideo.pl
|
||||||
%{_bindir}/zmwatch.pl
|
%{_bindir}/zmwatch.pl
|
||||||
%{_bindir}/zmcamtool.pl
|
%{_bindir}/zmcamtool.pl
|
||||||
%{_bindir}/zmsystemctl.pl
|
|
||||||
%{_bindir}/zmtelemetry.pl
|
%{_bindir}/zmtelemetry.pl
|
||||||
%{_bindir}/zmx10.pl
|
%{_bindir}/zmx10.pl
|
||||||
%{_bindir}/zmonvif-probe.pl
|
%{_bindir}/zmonvif-probe.pl
|
||||||
|
|
|
@ -25,7 +25,6 @@ override_dh_auto_configure:
|
||||||
-DZM_SOCKDIR="/var/run/zm" \
|
-DZM_SOCKDIR="/var/run/zm" \
|
||||||
-DZM_TMPDIR="/tmp/zm" \
|
-DZM_TMPDIR="/tmp/zm" \
|
||||||
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
||||||
-DZM_CONTENTDIR="/var/cache/zoneminder" \
|
|
||||||
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
||||||
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
||||||
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
|
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
|
||||||
|
|
|
@ -4,3 +4,5 @@ var/cache/zoneminder/events
|
||||||
var/cache/zoneminder/images
|
var/cache/zoneminder/images
|
||||||
var/cache/zoneminder/temp
|
var/cache/zoneminder/temp
|
||||||
usr/share/zoneminder/db
|
usr/share/zoneminder/db
|
||||||
|
etc/zm
|
||||||
|
etc/zm/conf.d
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
etc/zm/zm.conf
|
etc/zm/zm.conf
|
||||||
|
etc/zm/conf.d/*
|
||||||
usr/bin
|
usr/bin
|
||||||
usr/lib/zoneminder
|
usr/lib/zoneminder
|
||||||
usr/share/polkit-1
|
usr/share/polkit-1
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
/var/cache/zoneminder/events /usr/share/zoneminder/www/events
|
|
||||||
/var/cache/zoneminder/images /usr/share/zoneminder/www/images
|
|
||||||
/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp
|
|
||||||
/tmp/zm /usr/share/zoneminder/www/api/app/tmp
|
/tmp/zm /usr/share/zoneminder/www/api/app/tmp
|
||||||
|
|
|
@ -14,6 +14,8 @@ if [ "$1" = "configure" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Do this every time the package is installed or upgraded
|
# Do this every time the package is installed or upgraded
|
||||||
|
# Ensure zoneminder is stopped
|
||||||
|
invoke-rc.d zoneminder stop || true
|
||||||
|
|
||||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
if [ -e "/etc/init.d/mysql" ]; then
|
if [ -e "/etc/init.d/mysql" ]; then
|
||||||
|
@ -34,12 +36,8 @@ if [ "$1" = "configure" ]; then
|
||||||
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure zoneminder is stopped
|
|
||||||
invoke-rc.d zoneminder stop || true
|
|
||||||
zmupdate.pl --nointeractive
|
zmupdate.pl --nointeractive
|
||||||
zmupdate.pl --nointeractive -f
|
zmupdate.pl --nointeractive -f
|
||||||
echo "Done Updating, starting ZoneMinder"
|
|
||||||
invoke-rc.d zoneminder start || true
|
|
||||||
else
|
else
|
||||||
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
|
||||||
fi
|
fi
|
||||||
|
@ -49,6 +47,8 @@ if [ "$1" = "configure" ]; then
|
||||||
else
|
else
|
||||||
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
|
||||||
fi
|
fi
|
||||||
|
echo "Done Updating, starting ZoneMinder"
|
||||||
|
invoke-rc.d zoneminder start || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#DEBHELPER#
|
#DEBHELPER#
|
||||||
|
|
|
@ -58,7 +58,6 @@ override_dh_auto_configure:
|
||||||
-DZM_TMPDIR=/var/tmp/zm \
|
-DZM_TMPDIR=/var/tmp/zm \
|
||||||
-DZM_LOGDIR=/var/log/zm \
|
-DZM_LOGDIR=/var/log/zm \
|
||||||
-DZM_WEBDIR=/usr/share/zoneminder \
|
-DZM_WEBDIR=/usr/share/zoneminder \
|
||||||
-DZM_CONTENTDIR=/var/cache/zoneminder \
|
|
||||||
-DZM_CGIDIR=/usr/lib/cgi-bin \
|
-DZM_CGIDIR=/usr/lib/cgi-bin \
|
||||||
-DZM_WEB_USER=www-data \
|
-DZM_WEB_USER=www-data \
|
||||||
-DZM_WEB_GROUP=www-data \
|
-DZM_WEB_GROUP=www-data \
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
etc/zm
|
etc/zm
|
||||||
|
etc/zm/conf.d/*
|
||||||
usr/bin
|
usr/bin
|
||||||
usr/share/polkit-1/actions
|
usr/share/polkit-1/actions
|
||||||
usr/share/polkit-1/rules.d
|
usr/share/polkit-1/rules.d
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
var/cache/zoneminder/events usr/share/zoneminder/www/events
|
|
||||||
var/cache/zoneminder/images usr/share/zoneminder/www/images
|
|
||||||
var/cache/zoneminder/temp usr/share/zoneminder/www/temp
|
|
|
@ -1 +0,0 @@
|
||||||
usr/lib/cgi-bin usr/share/zoneminder/cgi-bin
|
|
|
@ -1,16 +0,0 @@
|
||||||
Last-Update: 2015-08-16
|
|
||||||
Forwarded: no
|
|
||||||
Author: Dmitry Smirnov <onlyjob@member.fsf.org>
|
|
||||||
Description: correct path to CGI app according to default web server configuration.
|
|
||||||
|
|
||||||
--- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
|
|
||||||
+++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
|
|
||||||
@@ -428,7 +428,7 @@ our @options =
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name => "ZM_PATH_ZMS",
|
|
||||||
- default => "/cgi-bin/nph-zms",
|
|
||||||
+ default => "/zm/cgi-bin/nph-zms",
|
|
||||||
description => "Web path to zms streaming server",
|
|
||||||
help => qqq("
|
|
||||||
The ZoneMinder streaming server is required to send streamed
|
|
|
@ -25,7 +25,6 @@ override_dh_auto_configure:
|
||||||
-DZM_SOCKDIR="/var/run/zm" \
|
-DZM_SOCKDIR="/var/run/zm" \
|
||||||
-DZM_TMPDIR="/tmp/zm" \
|
-DZM_TMPDIR="/tmp/zm" \
|
||||||
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
||||||
-DZM_CONTENTDIR="/var/cache/zoneminder" \
|
|
||||||
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
||||||
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
||||||
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
|
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
etc/zm/zm.conf
|
etc/zm/zm.conf
|
||||||
|
etc/zm/conf.d/*
|
||||||
usr/bin
|
usr/bin
|
||||||
usr/lib/zoneminder
|
usr/lib/zoneminder
|
||||||
usr/share/polkit-1
|
usr/share/polkit-1
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
/var/cache/zoneminder/events /usr/share/zoneminder/www/events
|
|
||||||
/var/cache/zoneminder/images /usr/share/zoneminder/www/images
|
|
||||||
/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp
|
|
||||||
/var/tmp /usr/share/zoneminder/www/api/app/tmp
|
/var/tmp /usr/share/zoneminder/www/api/app/tmp
|
||||||
|
|
|
@ -21,6 +21,8 @@ if [ "$1" = "configure" ]; then
|
||||||
# Ensure zoneminder is stopped
|
# Ensure zoneminder is stopped
|
||||||
deb-systemd-invoke stop zoneminder.service || exit $?
|
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||||
|
|
||||||
|
# Ensure zoneminder is stopped
|
||||||
|
deb-systemd-invoke stop zoneminder.service || exit $?
|
||||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
|
|
||||||
if [ -e "/etc/init.d/mysql" ]; then
|
if [ -e "/etc/init.d/mysql" ]; then
|
||||||
|
|
|
@ -10,7 +10,7 @@ Background: RHEL, CentOS, and Clones
|
||||||
|
|
||||||
These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis.
|
These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis.
|
||||||
|
|
||||||
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of php, mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
||||||
|
|
||||||
The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party.
|
The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party.
|
||||||
|
|
||||||
|
@ -23,39 +23,77 @@ Fedora has a short life-cycle of just 6 months. However, Fedora, and consequentl
|
||||||
|
|
||||||
If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora.
|
If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora.
|
||||||
|
|
||||||
Zmrepo – A ZoneMinder RPM Repository
|
How To Avoid Known Installation Problems
|
||||||
------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
Zmrepo is a turn key solution. It will install all of ZoneMinder's dependencies for you. This is the easiest and the recommended way to install ZoneMinder on any system running a Redhat based distribution.
|
The following notes are based on real problems which have occurred by those who came before you:
|
||||||
|
|
||||||
Zmrepo supports the two most recent, major releases of each Redhat based distro.
|
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" may not work correctly.
|
||||||
|
|
||||||
The following notes are based on real problems which have occurred:
|
|
||||||
|
|
||||||
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" are not supported and may not work correctly.
|
|
||||||
|
|
||||||
- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example.
|
- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example.
|
||||||
|
|
||||||
- Be advised that you need to start with a clean system before using zmrepo.
|
- Be advised that you need to start with a clean system before installing ZoneMinder.
|
||||||
|
|
||||||
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files first (look under /usr/local). Issuing a "make uninstall" helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
||||||
|
|
||||||
- It is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
- Unlike Debian/Ubuntu distros, it is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
||||||
|
|
||||||
- Disable other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
- Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
||||||
|
|
||||||
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow all the steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
||||||
|
|
||||||
To begin the installation of ZoneMinder on your Redhat based distro, please navigate to: http://zmrepo.zoneminder.com
|
How to Install ZoneMinder
|
||||||
|
-------------------------
|
||||||
|
|
||||||
How to Build a (Custom) ZoneMinder Package
|
These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6.
|
||||||
|
|
||||||
|
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo dnf install zoneminder
|
||||||
|
|
||||||
|
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||||
|
|
||||||
|
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
|
||||||
|
|
||||||
|
How to Install ZoneMinder on RHEL/CentOS 6
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
If you are looking to do development or the packages in zmrepo just don't suit you, then you should follow these steps to learn how to build your own ZoneMinder RPM.
|
We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near.
|
||||||
|
|
||||||
|
Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo <https://www.zoneminder.com>`_.
|
||||||
|
|
||||||
|
How to Install Nightly Development Builds
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
ZoneMinder development packages, which represent the most recent build from our master branch, are available from `zmrepo <https://www.zoneminder.com>`_.
|
||||||
|
|
||||||
|
The feedback we get from those who use these development packages is extremely helpful. However, please understand these packages are intended for testing the latest master branch only. They are not intended to be used on any production system. There will be new bugs, and new features may not be documented. This is bleeding edge, and there might be breakage. Please keep that in mind when using this repo. We know from our user forum that this can't be stated enough.
|
||||||
|
|
||||||
|
How to Change from Zmrepo to RPM Fusion
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
|
||||||
|
|
||||||
|
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`_ and enable RPM Fusion on your system
|
||||||
|
- Now issue the following from the command line:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo dnf remove zmrepo
|
||||||
|
sudo dnf update
|
||||||
|
|
||||||
|
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||||
|
|
||||||
|
How to Build a Your Own ZoneMinder Package
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
If you are looking to do development or the available packages just don't suit you, then you can follow these steps to build your own ZoneMinder RPM.
|
||||||
|
|
||||||
Background
|
Background
|
||||||
**********
|
**********
|
||||||
The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
The following method documents how to build ZoneMinder into an RPM package, for Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
||||||
|
|
||||||
The method documented below was chosen because:
|
The method documented below was chosen because:
|
||||||
|
|
||||||
|
@ -67,22 +105,20 @@ The method documented below was chosen because:
|
||||||
|
|
||||||
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
|
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
|
||||||
|
|
||||||
The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire.
|
|
||||||
|
|
||||||
***IMPORTANT***
|
***IMPORTANT***
|
||||||
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build.
|
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build, or worse a broken system.
|
||||||
|
|
||||||
Set Up Your Environment
|
Set Up Your Environment
|
||||||
***********************
|
***********************
|
||||||
Before you begin, set up an rpmbuild environment by following `this guide <http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment>`_ by the CentOS developers.
|
Before you begin, set up an rpmbuild environment by following `this guide <http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment>`_ by the CentOS developers.
|
||||||
|
|
||||||
Next, navigate to `Zmrepo <http://zmrepo.zoneminder.com/>`_, and follow the instructions to enable zmrepo on your system.
|
In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_.
|
||||||
|
|
||||||
With zmrepo enabled, issue the following command:
|
With RPM Fusion enabled, issue the following command:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
sudo yum install zmrepo-mock-configs mock
|
sudo yum install mock-rpmfusion-free mock
|
||||||
|
|
||||||
|
|
||||||
Add your user account to the group mock:
|
Add your user account to the group mock:
|
||||||
|
@ -96,73 +132,67 @@ Your build environment is now set up.
|
||||||
|
|
||||||
Build from SRPM
|
Build from SRPM
|
||||||
***************
|
***************
|
||||||
To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the `Zmrepo <http://zmrepo.zoneminder.com/>`_ site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder.
|
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <http://zmrepo.zoneminder.com/>`_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
|
||||||
|
|
||||||
For CentOS 7, I have chosen the following SRPM:
|
For this example, I'll use one of the source rpms from zmrepo:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
|
|
||||||
Now comes the fun part. To build ZoneMinder, issue the following command:
|
Now comes the fun part. To build ZoneMinder, issue the following command:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
|
|
||||||
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
|
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm
|
mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
Notice that the buildzm.sh tool requires the following parameters:
|
Notice that the mock tool requires the following parameters:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh MOCKCONFIG ZONEMINDER_SRPM
|
mock -r MOCKCONFIG ZONEMINDER_SRPM
|
||||||
|
|
||||||
The list of available Mock config files are available here:
|
The list of available Mock config files are available here:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
ls /etc/mock/zmrepo*.cfg
|
ls /etc/mock/*rpmfusion_free.cfg
|
||||||
|
|
||||||
|
|
||||||
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
************
|
************
|
||||||
Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the `Zmrepo <http://zmrepo.zoneminder.com/>`_
|
Once the build completes, you will be presented with a message stating where the newly built rpms can be found. It will look similar to this:
|
||||||
website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
|
||||||
|
|
||||||
Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
|
||||||
|
|
||||||
How to Modify the Source Prior to Build
|
|
||||||
***************************************
|
|
||||||
Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong.
|
|
||||||
|
|
||||||
These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch.
|
|
||||||
|
|
||||||
From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system:
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
rpm -ivh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
INFO: Results and/or logs in: /var/lib/mock/fedora-26-x86_64/result
|
||||||
|
|
||||||
|
Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
||||||
|
|
||||||
IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root.
|
Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
||||||
|
|
||||||
Make sure you have git installed:
|
How to Create Your Own Source RPM
|
||||||
|
*********************************
|
||||||
|
In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into a source rpm, which can be used in the previous section to build an rpm.
|
||||||
|
|
||||||
|
Make sure git and rpmdevtools are installed:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
sudo yum install git
|
sudo yum install git rpmdevtools
|
||||||
|
|
||||||
|
|
||||||
Now clone the ZoneMinder git repository:
|
Now clone the ZoneMinder git repository from your home folder:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -170,32 +200,62 @@ Now clone the ZoneMinder git repository:
|
||||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||||
cd ZoneMinder
|
cd ZoneMinder
|
||||||
|
|
||||||
This will create a sub-folder called ZoneMinder, which will contain the latest development.
|
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
|
||||||
|
|
||||||
We want to turn this into a tarball, but first we need to figure out what to name it. Look here:
|
If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
ls ~/rpmbuild/SOURCES
|
cd ~\ZoneMinder
|
||||||
|
git pull origin master
|
||||||
|
|
||||||
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From the root folder of the local ZoneMinder git repository, execute the following:
|
Get the crud submodule tarball:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
git archive --prefix=ZoneMinder-1.28.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.28.1.tar.gz HEAD
|
spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball.
|
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git checkout -b mynewbranch
|
||||||
|
|
||||||
|
Again, depending on what you want to do with those changes, you may want to commit your changes:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git add .
|
||||||
|
git commit
|
||||||
|
|
||||||
|
Once you have made your changes, it is time to turn your work into a new tarball, but first we need to look in the rpm specfile:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
less ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
|
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
|
||||||
|
|
||||||
|
Replace "1.31.1" with the Version shown in the rpm specfile.
|
||||||
|
|
||||||
From the root of the local ZoneMinder git repo, execute the following:
|
From the root of the local ZoneMinder git repo, execute the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
Notice we used the rpm specfile that is part of the latest master branch you just downloaded, rather than the one that may be in your ~/rpmbbuild/SOURCES folder.
|
This step will create a source rpm and it will tell you where it was saved. For example:
|
||||||
|
|
||||||
This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro.
|
::
|
||||||
|
|
||||||
You should now have a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM.
|
|
||||||
|
|
||||||
|
Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm
|
||||||
|
|
||||||
|
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
|
||||||
|
|
|
@ -72,6 +72,7 @@ Source Tab
|
||||||
FFmpeg
|
FFmpeg
|
||||||
^^^^^^
|
^^^^^^
|
||||||
This is the recommended source type for most modern ip cameras.
|
This is the recommended source type for most modern ip cameras.
|
||||||
|
|
||||||
Source Path
|
Source Path
|
||||||
Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this:
|
Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this:
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ How filters actually work
|
||||||
--------------------------
|
--------------------------
|
||||||
It is useful to know how filters actually work behind the scenes in ZoneMinder, in the event you find your filter not functioning as intended:
|
It is useful to know how filters actually work behind the scenes in ZoneMinder, in the event you find your filter not functioning as intended:
|
||||||
|
|
||||||
* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl``
|
* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl`` which retrieves filters from the Filters database table
|
||||||
* zmfilter.pl runs every FILTER_EXECUTE_INTERVAL seconds (default is 20s, can be changed in Options->System)
|
* zmfilter.pl runs every FILTER_EXECUTE_INTERVAL seconds (default is 20s, can be changed in Options->System)
|
||||||
* in each run, it goes through all the filters which are marked as "Run in Background" and if the conditions match performs the specified action
|
* in each run, it goes through all the filters which are marked as "Run in Background" and if the conditions match performs the specified action
|
||||||
* zmfilter.pl also reloads all the filters every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System)
|
* zmfilter.pl also reloads all the filters every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System)
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
@ZM_LOGDIR@/*.log {
|
@ZM_LOGDIR@/*.log {
|
||||||
missingok
|
missingok
|
||||||
notifempty
|
notifempty
|
||||||
|
delaycompress
|
||||||
|
compress
|
||||||
sharedscripts
|
sharedscripts
|
||||||
postrotate
|
postrotate
|
||||||
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
|
/bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
|
||||||
|
|
|
@ -253,13 +253,6 @@ sub profiles
|
||||||
foreach my $profile ( @{ $profiles } ) {
|
foreach my $profile ( @{ $profiles } ) {
|
||||||
|
|
||||||
my $token = $profile->attr()->get_token() ;
|
my $token = $profile->attr()->get_token() ;
|
||||||
print $token . ", " .
|
|
||||||
$profile->get_Name() . ", " .
|
|
||||||
$profile->get_VideoEncoderConfiguration()->get_Encoding() . ", " .
|
|
||||||
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Width() . ", " .
|
|
||||||
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Height() . ", " .
|
|
||||||
$profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() .
|
|
||||||
", ";
|
|
||||||
|
|
||||||
# Specification gives conflicting values for unicast stream types, try both.
|
# Specification gives conflicting values for unicast stream types, try both.
|
||||||
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
||||||
|
@ -278,9 +271,18 @@ sub profiles
|
||||||
die $result if not $result;
|
die $result if not $result;
|
||||||
# print $result . "\n";
|
# print $result . "\n";
|
||||||
|
|
||||||
print $result->get_MediaUri()->get_Uri() .
|
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
||||||
"\n";
|
print join(', ', $token,
|
||||||
}
|
$profile->get_Name(),
|
||||||
|
( $VideoEncoderConfiguration ? (
|
||||||
|
$VideoEncoderConfiguration->get_Encoding(),
|
||||||
|
$VideoEncoderConfiguration->get_Resolution()->get_Width(),
|
||||||
|
$VideoEncoderConfiguration->get_Resolution()->get_Height(),
|
||||||
|
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
|
||||||
|
) : () ),
|
||||||
|
$result->get_MediaUri()->get_Uri() ,
|
||||||
|
). "\n";
|
||||||
|
} # end foreach profile
|
||||||
|
|
||||||
#
|
#
|
||||||
# use message parser without schema validation ???
|
# use message parser without schema validation ???
|
||||||
|
|
|
@ -33,8 +33,11 @@ FOREACH(PERLSCRIPT ${perlscripts})
|
||||||
ENDFOREACH(PERLSCRIPT ${perlscripts})
|
ENDFOREACH(PERLSCRIPT ${perlscripts})
|
||||||
|
|
||||||
# Install the perl scripts
|
# Install the perl scripts
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
if(NOT ZM_NO_X10)
|
if(NOT ZM_NO_X10)
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
endif(NOT ZM_NO_X10)
|
endif(NOT ZM_NO_X10)
|
||||||
|
|
||||||
|
if(WITH_SYSTEMD)
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
endif(WITH_SYSTEMD)
|
||||||
|
|
|
@ -101,8 +101,17 @@ BEGIN {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket
|
.$socket.$sslOptions
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
) or croak( "Can't connect to db" );
|
) or croak( "Can't connect to db" );
|
||||||
|
|
|
@ -0,0 +1,333 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder Maginon Supra IPC Camera Control Protocol Module,
|
||||||
|
# Copyright (C) 2017 Martin Gutenbrunner (martin.gutenbrunner@SPAMsonic.net)
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# This module contains the implementation of the Maginon Supra IPC camera
|
||||||
|
# procotol version.
|
||||||
|
#
|
||||||
|
package ZoneMinder::Control::MaginonIPC;
|
||||||
|
|
||||||
|
use 5.006;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
require ZoneMinder::Base;
|
||||||
|
require ZoneMinder::Control;
|
||||||
|
|
||||||
|
our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# Maginon Supra IPC IP Camera Control Protocol
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
|
use Time::HiRes qw( usleep );
|
||||||
|
|
||||||
|
sub new
|
||||||
|
{
|
||||||
|
my $class = shift;
|
||||||
|
my $id = shift;
|
||||||
|
my $self = ZoneMinder::Control->new( $id );
|
||||||
|
bless( $self, $class );
|
||||||
|
srand( time() );
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
our $AUTOLOAD;
|
||||||
|
|
||||||
|
sub AUTOLOAD
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $class = ref($self) || croak( "$self not object" );
|
||||||
|
my $name = $AUTOLOAD;
|
||||||
|
$name =~ s/.*://;
|
||||||
|
if ( exists($self->{$name}) )
|
||||||
|
{
|
||||||
|
return( $self->{$name} );
|
||||||
|
}
|
||||||
|
Fatal( "Can't access $name member of object of class $class" );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->loadMonitor();
|
||||||
|
|
||||||
|
use LWP::UserAgent;
|
||||||
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
|
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||||
|
|
||||||
|
$self->{state} = 'open';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub close
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
$self->{state} = 'closed';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub printMsg
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $msg = shift;
|
||||||
|
my $msg_len = length($msg);
|
||||||
|
|
||||||
|
Debug( $msg."[".$msg_len."]" );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub sendCmd
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $cmd = shift;
|
||||||
|
|
||||||
|
#my $result = undef;
|
||||||
|
|
||||||
|
printMsg( $cmd, "Tx" );
|
||||||
|
|
||||||
|
my $url = "http://".$self->{Monitor}->{ControlAddress}."$cmd";
|
||||||
|
# Info($url);
|
||||||
|
my $req = HTTP::Request->new( GET=>$url );
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
return( !undef );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveStop
|
||||||
|
{
|
||||||
|
Debug("moveStop");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=1";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUp
|
||||||
|
{
|
||||||
|
Debug("moveConUp");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=0";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDown
|
||||||
|
{
|
||||||
|
Debug("moveConDown");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=2";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConLeft
|
||||||
|
{
|
||||||
|
Debug("moveConLeft");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=4";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConRight
|
||||||
|
{
|
||||||
|
Debug("moveConRight");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=6";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUpRight
|
||||||
|
{
|
||||||
|
Debug("moveConUpRight");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=91";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUpLeft
|
||||||
|
{
|
||||||
|
Debug("moveConUpLeft");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=90";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDownRight
|
||||||
|
{
|
||||||
|
Debug("moveConDownRight");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=93";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDownLeft
|
||||||
|
{
|
||||||
|
Debug("moveConDownLeft");
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $cmd = "/decoder_control.cgi?command=92";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
my $autostop = $self->getParam( $params, 'autostop', 0 );
|
||||||
|
if ( $autostop && $self->{Monitor}->{AutoStopTimeout} )
|
||||||
|
{
|
||||||
|
usleep( $self->{Monitor}->{AutoStopTimeout} );
|
||||||
|
$self->moveStop( $params );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetSet
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset = $self->getParam( $params, 'preset' );
|
||||||
|
Info( "Set Preset $preset" );
|
||||||
|
|
||||||
|
my $cmdNum;
|
||||||
|
if ($preset == 1) {
|
||||||
|
$cmdNum = 30;
|
||||||
|
} elsif ($preset == 2) {
|
||||||
|
$cmdNum = 32;
|
||||||
|
} elsif ($preset == 3) {
|
||||||
|
$cmdNum = 34;
|
||||||
|
} elsif ($preset == 4) {
|
||||||
|
$cmdNum = 36;
|
||||||
|
} else {
|
||||||
|
$cmdNum = 36;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cmd = "/decoder_control.cgi?command=$cmdNum";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetGoto
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset = $self->getParam( $params, 'preset' );
|
||||||
|
Info( "Goto Preset $preset" );
|
||||||
|
|
||||||
|
my $cmdNum;
|
||||||
|
if ($preset == 1) {
|
||||||
|
$cmdNum = 31;
|
||||||
|
} elsif ($preset == 2) {
|
||||||
|
$cmdNum = 33;
|
||||||
|
} elsif ($preset == 3) {
|
||||||
|
$cmdNum = 35;
|
||||||
|
} elsif ($preset == 4) {
|
||||||
|
$cmdNum = 37;
|
||||||
|
} else {
|
||||||
|
$cmdNum = 37;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cmd = "/decoder_control.cgi?command=$cmdNum";
|
||||||
|
$self->sendCmd( $cmd );
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
# Below is stub documentation for your module. You'd better edit it!
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
ZoneMinder::Control::MaginonIPC - Zoneminder PTZ control module for the Maginon Supra-IPC 40 IP Camera
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use ZoneMinder::Control::MaginonIPC;
|
||||||
|
blah blah blah
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This is for Zoneminder PTZ control module for the Maginon Supra-IPC 40 camera. It probably also works with other models.
|
||||||
|
|
||||||
|
=head2 EXPORT
|
||||||
|
|
||||||
|
None by default.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
www.zoneminder.com
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Martin Gutenbrunner, E<lt>martin.gutenbrunner@gmx.atE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
Copyright (C) 2017 by Martin Gutenbrunner
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify
|
||||||
|
it under the same terms as Perl itself, either Perl version 5.8.3 or,
|
||||||
|
at your option, any later version of Perl 5 you may have available.
|
||||||
|
|
||||||
|
|
||||||
|
=cut
|
|
@ -90,8 +90,19 @@ sub zmDbConnect {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
);
|
);
|
||||||
|
|
|
@ -41,7 +41,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
|
||||||
# will save memory.
|
# will save memory.
|
||||||
our %EXPORT_TAGS = (
|
our %EXPORT_TAGS = (
|
||||||
'constants' => [ qw(
|
constants => [ qw(
|
||||||
DEBUG
|
DEBUG
|
||||||
INFO
|
INFO
|
||||||
WARNING
|
WARNING
|
||||||
|
@ -50,7 +50,7 @@ our %EXPORT_TAGS = (
|
||||||
PANIC
|
PANIC
|
||||||
NOLOG
|
NOLOG
|
||||||
) ],
|
) ],
|
||||||
'functions' => [ qw(
|
functions => [ qw(
|
||||||
logInit
|
logInit
|
||||||
logReinit
|
logReinit
|
||||||
logTerm
|
logTerm
|
||||||
|
@ -72,6 +72,7 @@ our %EXPORT_TAGS = (
|
||||||
Panic
|
Panic
|
||||||
) ]
|
) ]
|
||||||
);
|
);
|
||||||
|
|
||||||
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
|
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
|
||||||
|
|
||||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
||||||
|
@ -107,22 +108,22 @@ our %EXPORT_TAGS = (
|
||||||
};
|
};
|
||||||
|
|
||||||
our %codes = (
|
our %codes = (
|
||||||
&DEBUG => "DBG",
|
&DEBUG => 'DBG',
|
||||||
&INFO => "INF",
|
&INFO => 'INF',
|
||||||
&WARNING => "WAR",
|
&WARNING => 'WAR',
|
||||||
&ERROR => "ERR",
|
&ERROR => 'ERR',
|
||||||
&FATAL => "FAT",
|
&FATAL => 'FAT',
|
||||||
&PANIC => "PNC",
|
&PANIC => 'PNC',
|
||||||
&NOLOG => "OFF"
|
&NOLOG => 'OFF'
|
||||||
);
|
);
|
||||||
|
|
||||||
our %priorities = (
|
our %priorities = (
|
||||||
&DEBUG => "debug",
|
&DEBUG => 'debug',
|
||||||
&INFO => "info",
|
&INFO => 'info',
|
||||||
&WARNING => "warning",
|
&WARNING => 'warning',
|
||||||
&ERROR => "err",
|
&ERROR => 'err',
|
||||||
&FATAL => "err",
|
&FATAL => 'err',
|
||||||
&PANIC => "err"
|
&PANIC => 'err'
|
||||||
);
|
);
|
||||||
|
|
||||||
our $logger;
|
our $logger;
|
||||||
|
@ -134,10 +135,10 @@ sub new {
|
||||||
|
|
||||||
$this->{initialised} = undef;
|
$this->{initialised} = undef;
|
||||||
|
|
||||||
#$this->{id} = "zmundef";
|
#$this->{id} = 'zmundef';
|
||||||
( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|;
|
( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|;
|
||||||
$this->{idRoot} = $this->{id};
|
$this->{idRoot} = $this->{id};
|
||||||
$this->{idArgs} = "";
|
$this->{idArgs} = '';
|
||||||
|
|
||||||
$this->{level} = INFO;
|
$this->{level} = INFO;
|
||||||
$this->{termLevel} = NOLOG;
|
$this->{termLevel} = NOLOG;
|
||||||
|
@ -151,7 +152,7 @@ sub new {
|
||||||
|
|
||||||
( $this->{fileName} = $0 ) =~ s|^.*/||;
|
( $this->{fileName} = $0 ) =~ s|^.*/||;
|
||||||
$this->{logPath} = $Config{ZM_PATH_LOGS};
|
$this->{logPath} = $Config{ZM_PATH_LOGS};
|
||||||
$this->{logFile} = $this->{logPath}."/".$this->{id}.".log";
|
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.".log";
|
||||||
|
|
||||||
$this->{trace} = 0;
|
$this->{trace} = 0;
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ sub initialise( @ ) {
|
||||||
$this->{logPath} = $options{logPath} if ( defined($options{logPath}) );
|
$this->{logPath} = $options{logPath} if ( defined($options{logPath}) );
|
||||||
|
|
||||||
my $tempLogFile;
|
my $tempLogFile;
|
||||||
$tempLogFile = $this->{logPath}."/".$this->{id}.".log";
|
$tempLogFile = $this->{logPath}.'/'.$this->{id}.".log";
|
||||||
$tempLogFile = $options{logFile} if ( defined($options{logFile}) );
|
$tempLogFile = $options{logFile} if ( defined($options{logFile}) );
|
||||||
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
|
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
|
||||||
$tempLogFile = $logFile;
|
$tempLogFile = $logFile;
|
||||||
|
@ -231,7 +232,6 @@ sub initialise( @ ) {
|
||||||
|
|
||||||
my $level;
|
my $level;
|
||||||
$tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) );
|
$tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) );
|
||||||
|
|
||||||
$tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) );
|
$tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) );
|
||||||
$tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) );
|
$tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) );
|
||||||
$tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) );
|
$tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) );
|
||||||
|
@ -240,9 +240,9 @@ sub initialise( @ ) {
|
||||||
if ( $Config{ZM_LOG_DEBUG} ) {
|
if ( $Config{ZM_LOG_DEBUG} ) {
|
||||||
foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) {
|
foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) {
|
||||||
if ( $target eq $this->{id}
|
if ( $target eq $this->{id}
|
||||||
|| $target eq "_".$this->{id}
|
|| $target eq '_'.$this->{id}
|
||||||
|| $target eq $this->{idRoot}
|
|| $target eq $this->{idRoot}
|
||||||
|| $target eq "_".$this->{idRoot}
|
|| $target eq '_'.$this->{idRoot}
|
||||||
|| $target eq ""
|
|| $target eq ""
|
||||||
) {
|
) {
|
||||||
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
|
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
|
||||||
|
@ -271,13 +271,13 @@ sub initialise( @ ) {
|
||||||
|
|
||||||
$this->{initialised} = !undef;
|
$this->{initialised} = !undef;
|
||||||
|
|
||||||
Debug( "LogOpts: level=".$codes{$this->{level}}
|
Debug( 'LogOpts: level='.$codes{$this->{level}}
|
||||||
."/".$codes{$this->{effectiveLevel}}
|
.'/'.$codes{$this->{effectiveLevel}}
|
||||||
.", screen=".$codes{$this->{termLevel}}
|
.', screen='.$codes{$this->{termLevel}}
|
||||||
.", database=".$codes{$this->{databaseLevel}}
|
.', database='.$codes{$this->{databaseLevel}}
|
||||||
.", logfile=".$codes{$this->{fileLevel}}
|
.', logfile='.$codes{$this->{fileLevel}}
|
||||||
."->".$this->{logFile}
|
.'->'.$this->{logFile}
|
||||||
.", syslog=".$codes{$this->{syslogLevel}}
|
.', syslog='.$codes{$this->{syslogLevel}}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,24 +293,28 @@ sub terminate {
|
||||||
sub reinitialise {
|
sub reinitialise {
|
||||||
my $this = shift;
|
my $this = shift;
|
||||||
|
|
||||||
|
# So if the logger is initialized, we just return. Since the logger is NORMALLY initialized... the rest of this function never executes.
|
||||||
return unless ( $this->{initialised} );
|
return unless ( $this->{initialised} );
|
||||||
|
|
||||||
# Bit of a nasty hack to reopen connections to log files and the DB
|
# Bit of a nasty hack to reopen connections to log files and the DB
|
||||||
my $syslogLevel = $this->syslogLevel();
|
my $syslogLevel = $this->syslogLevel();
|
||||||
$this->syslogLevel( NOLOG );
|
$this->syslogLevel( NOLOG );
|
||||||
|
$this->syslogLevel( $syslogLevel ) if ( $syslogLevel > NOLOG );
|
||||||
|
|
||||||
my $logfileLevel = $this->fileLevel();
|
my $logfileLevel = $this->fileLevel();
|
||||||
$this->fileLevel( NOLOG );
|
$this->fileLevel( NOLOG );
|
||||||
|
$this->fileLevel( $logfileLevel ) if ( $logfileLevel > NOLOG );
|
||||||
|
|
||||||
my $databaseLevel = $this->databaseLevel();
|
my $databaseLevel = $this->databaseLevel();
|
||||||
$this->databaseLevel( NOLOG );
|
$this->databaseLevel( NOLOG );
|
||||||
|
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG );
|
||||||
|
|
||||||
my $screenLevel = $this->termLevel();
|
my $screenLevel = $this->termLevel();
|
||||||
$this->termLevel( NOLOG );
|
$this->termLevel( NOLOG );
|
||||||
|
$this->termLevel( $screenLevel ) if ( $screenLevel > NOLOG );
|
||||||
$this->syslogLevel( $syslogLevel ) if ( $syslogLevel > NOLOG );
|
|
||||||
$this->fileLevel( $logfileLevel ) if ( $logfileLevel > NOLOG );
|
|
||||||
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG );
|
|
||||||
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prevents undefined logging levels
|
||||||
sub limit {
|
sub limit {
|
||||||
my $this = shift;
|
my $this = shift;
|
||||||
my $level = shift;
|
my $level = shift;
|
||||||
|
@ -322,11 +326,11 @@ sub limit {
|
||||||
sub getTargettedEnv {
|
sub getTargettedEnv {
|
||||||
my $this = shift;
|
my $this = shift;
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
my $envName = $name."_".$this->{id};
|
my $envName = $name.'_'.$this->{id};
|
||||||
my $value;
|
my $value;
|
||||||
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
|
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
|
||||||
if ( !defined($value) && $this->{id} ne $this->{idRoot} ) {
|
if ( !defined($value) && $this->{id} ne $this->{idRoot} ) {
|
||||||
$envName = $name."_".$this->{idRoot};
|
$envName = $name.'_'.$this->{idRoot};
|
||||||
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
|
$value = $ENV{$envName} if ( defined($ENV{$envName}) );
|
||||||
}
|
}
|
||||||
if ( !defined($value) ) {
|
if ( !defined($value) ) {
|
||||||
|
@ -371,12 +375,16 @@ sub level {
|
||||||
my $level = shift;
|
my $level = shift;
|
||||||
if ( defined($level) ) {
|
if ( defined($level) ) {
|
||||||
$this->{level} = $this->limit( $level );
|
$this->{level} = $this->limit( $level );
|
||||||
|
|
||||||
|
# effectiveLevel is the highest logging level used by any of the outputs.
|
||||||
$this->{effectiveLevel} = NOLOG;
|
$this->{effectiveLevel} = NOLOG;
|
||||||
$this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} );
|
$this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} );
|
||||||
$this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} );
|
$this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} );
|
||||||
$this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} );
|
$this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} );
|
||||||
$this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{level} );
|
$this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{effectiveLevel} );
|
||||||
$this->{effectiveLevel} = $this->{level} if ( $this->{effectiveLevel} > $this->{level} );
|
|
||||||
|
# ICON: I am remarking this out because I don't see the point of having an effective level, if we are just going to set it to level.
|
||||||
|
#$this->{effectiveLevel} = $this->{level} if ( $this->{level} > $this->{effectiveLevel} );
|
||||||
}
|
}
|
||||||
return( $this->{level} );
|
return( $this->{level} );
|
||||||
}
|
}
|
||||||
|
@ -396,6 +404,7 @@ sub termLevel {
|
||||||
my $this = shift;
|
my $this = shift;
|
||||||
my $termLevel = shift;
|
my $termLevel = shift;
|
||||||
if ( defined($termLevel) ) {
|
if ( defined($termLevel) ) {
|
||||||
|
# What is the point of this next lint if we are just going to overwrite it with the next line? I propose we move it down one line or remove it altogether
|
||||||
$termLevel = NOLOG if ( !$this->{hasTerm} );
|
$termLevel = NOLOG if ( !$this->{hasTerm} );
|
||||||
$termLevel = $this->limit( $termLevel );
|
$termLevel = $this->limit( $termLevel );
|
||||||
if ( $this->{termLevel} != $termLevel ) {
|
if ( $this->{termLevel} != $termLevel ) {
|
||||||
|
@ -425,8 +434,17 @@ sub databaseLevel {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket
|
.$socket.$sslOptions
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
);
|
);
|
||||||
|
@ -490,7 +508,7 @@ sub syslogLevel {
|
||||||
|
|
||||||
sub openSyslog {
|
sub openSyslog {
|
||||||
my $this = shift;
|
my $this = shift;
|
||||||
openlog( $this->{id}, "pid", "local1" );
|
openlog( $this->{id}, 'pid', "local1" );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub closeSyslog {
|
sub closeSyslog {
|
||||||
|
@ -523,6 +541,7 @@ sub openFile {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->fileLevel( NOLOG );
|
$this->fileLevel( NOLOG );
|
||||||
|
$this->termLevel( INFO );
|
||||||
Error( "Can't open log file '".$this->{logFile}."': $!" );
|
Error( "Can't open log file '".$this->{logFile}."': $!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -544,10 +563,8 @@ sub logPrint {
|
||||||
|
|
||||||
my ($seconds, $microseconds) = gettimeofday();
|
my ($seconds, $microseconds) = gettimeofday();
|
||||||
my $message = sprintf(
|
my $message = sprintf(
|
||||||
"%s.%06d %s[%d].%s [%s]"
|
'%s.%06d %s[%d].%s [%s]'
|
||||||
, strftime( "%x %H:%M:%S"
|
, strftime( '%x %H:%M:%S' ,localtime( $seconds ) )
|
||||||
,localtime( $seconds )
|
|
||||||
)
|
|
||||||
, $microseconds
|
, $microseconds
|
||||||
, $this->{id}
|
, $this->{id}
|
||||||
, $$
|
, $$
|
||||||
|
@ -564,7 +581,7 @@ sub logPrint {
|
||||||
}
|
}
|
||||||
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} );
|
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} );
|
||||||
if ( $level <= $this->{databaseLevel} ) {
|
if ( $level <= $this->{databaseLevel} ) {
|
||||||
my $sql = "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )";
|
my $sql = 'insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )';
|
||||||
$this->{sth} = $this->{dbh}->prepare_cached( $sql );
|
$this->{sth} = $this->{dbh}->prepare_cached( $sql );
|
||||||
if ( !$this->{sth} ) {
|
if ( !$this->{sth} ) {
|
||||||
$this->{databaseLevel} = NOLOG;
|
$this->{databaseLevel} = NOLOG;
|
||||||
|
@ -589,7 +606,7 @@ sub logPrint {
|
||||||
|
|
||||||
sub logInit( ;@ ) {
|
sub logInit( ;@ ) {
|
||||||
my %options = @_ ? @_ : ();
|
my %options = @_ ? @_ : ();
|
||||||
$logger = ZoneMinder::Logger->new() if ( !$logger );
|
$logger = ZoneMinder::Logger->new() if !$logger;
|
||||||
$logger->initialise( %options );
|
$logger->initialise( %options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,14 +663,14 @@ sub logSyslogLevel {
|
||||||
sub Mark {
|
sub Mark {
|
||||||
my $level = shift;
|
my $level = shift;
|
||||||
$level = DEBUG unless( defined($level) );
|
$level = DEBUG unless( defined($level) );
|
||||||
my $tag = "Mark";
|
my $tag = 'Mark';
|
||||||
fetch()->logPrint( $level, $tag );
|
fetch()->logPrint( $level, $tag );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub Dump {
|
sub Dump {
|
||||||
my $var = shift;
|
my $var = shift;
|
||||||
my $label = shift;
|
my $label = shift;
|
||||||
$label = "VAR" unless( defined($label) );
|
$label = 'VAR' unless( defined($label) );
|
||||||
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
|
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,10 +712,10 @@ ZoneMinder::Logger - ZoneMinder Logger module
|
||||||
use ZoneMinder::Logger;
|
use ZoneMinder::Logger;
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
|
||||||
logInit( "myproc", DEBUG );
|
logInit( 'myproc', DEBUG );
|
||||||
|
|
||||||
Debug( "This is what is happening" );
|
Debug( 'This is what is happening' );
|
||||||
Info( "Something interesting is happening" );
|
Info( 'Something interesting is happening' );
|
||||||
Warning( "Something might be going wrong." );
|
Warning( "Something might be going wrong." );
|
||||||
Error( "Something has gone wrong!!" );
|
Error( "Something has gone wrong!!" );
|
||||||
Fatal( "Something has gone badly wrong, gotta stop!!" );
|
Fatal( "Something has gone badly wrong, gotta stop!!" );
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
|
|
@ -21,29 +21,6 @@
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
zmtelemetry.pl - Send usage information to the ZoneMinder development team
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
zmtelemetry.pl
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
This script collects usage information of the local system and sends it to the
|
|
||||||
ZoneMinder development team. This data will be used to determine things like
|
|
||||||
who and where our customers are, how big their systems are, the underlying
|
|
||||||
hardware and operating system, etc. This is being done for the sole purpoase of
|
|
||||||
creating a better product for our target audience. This script is intended to
|
|
||||||
be completely transparent to the end user, and can be disabled from the web
|
|
||||||
console under Options.
|
|
||||||
|
|
||||||
=head1 OPTIONS
|
|
||||||
|
|
||||||
none currently
|
|
||||||
|
|
||||||
=cut
|
|
||||||
use strict;
|
use strict;
|
||||||
use bytes;
|
use bytes;
|
||||||
|
|
||||||
|
@ -67,40 +44,39 @@ use constant CHECK_INTERVAL => (14*24*60*60); # Interval between version checks
|
||||||
# Alternatively, we can put these in the dB and then retrieve using the Config hash.
|
# Alternatively, we can put these in the dB and then retrieve using the Config hash.
|
||||||
use constant ZM_TELEMETRY_SERVER_ENDPOINT => 'https://zmanon:2b2d0b4skps@telemetry.zoneminder.com/zmtelemetry/testing5';
|
use constant ZM_TELEMETRY_SERVER_ENDPOINT => 'https://zmanon:2b2d0b4skps@telemetry.zoneminder.com/zmtelemetry/testing5';
|
||||||
|
|
||||||
if ( $Config{ZM_TELEMETRY_DATA} )
|
if ( $Config{ZM_TELEMETRY_DATA} ) {
|
||||||
{
|
print( 'Update agent starting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
|
||||||
print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
|
|
||||||
|
|
||||||
my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
|
my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
|
||||||
|
|
||||||
while( 1 ) {
|
while( 1 ) {
|
||||||
my $now = time();
|
my $now = time();
|
||||||
if ( ($now-$lastCheck) > CHECK_INTERVAL ) {
|
if ( ($now-$lastCheck) > CHECK_INTERVAL ) {
|
||||||
Info( "Collecting data to send to ZoneMinder Telemetry server." );
|
Info( 'Collecting data to send to ZoneMinder Telemetry server.' );
|
||||||
my $dbh = zmDbConnect();
|
my $dbh = zmDbConnect();
|
||||||
# Build the telemetry hash
|
# Build the telemetry hash
|
||||||
# We should keep *BSD systems in mind when calling system commands
|
# We should keep *BSD systems in mind when calling system commands
|
||||||
my %telemetry;
|
my %telemetry;
|
||||||
$telemetry{uuid} = getUUID($dbh);
|
$telemetry{uuid} = getUUID($dbh);
|
||||||
$telemetry{ip} = getIP();
|
$telemetry{ip} = getIP();
|
||||||
$telemetry{timestamp} = strftime( "%Y-%m-%dT%H:%M:%S%z", localtime() );
|
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
|
||||||
$telemetry{monitor_count} = countQuery($dbh,"Monitors");
|
$telemetry{monitor_count} = countQuery($dbh,'Monitors');
|
||||||
$telemetry{event_count} = countQuery($dbh,"Events");
|
$telemetry{event_count} = countQuery($dbh,'Events');
|
||||||
$telemetry{architecture} = runSysCmd("uname -p");
|
$telemetry{architecture} = runSysCmd('uname -p');
|
||||||
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
|
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
|
||||||
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
||||||
$telemetry{system_memory} = totalmem();
|
$telemetry{system_memory} = totalmem();
|
||||||
$telemetry{processor_count} = cpu_count();
|
$telemetry{processor_count} = cpu_count();
|
||||||
$telemetry{monitors} = getMonitorRef($dbh);
|
$telemetry{monitors} = getMonitorRef($dbh);
|
||||||
|
|
||||||
Info( "Sending data to ZoneMinder Telemetry server." );
|
Info( 'Sending data to ZoneMinder Telemetry server.' );
|
||||||
|
|
||||||
my $result = jsonEncode( \%telemetry );
|
my $result = jsonEncode( \%telemetry );
|
||||||
|
|
||||||
if ( sendData($result) ) {
|
if ( sendData($result) ) {
|
||||||
$lastCheck = $now;
|
$lastCheck = $now;
|
||||||
|
|
||||||
my $sql = "update Config set Value = ? where Name = 'ZM_TELEMETRY_LAST_UPLOAD'";
|
my $sql = q`update Config set Value = ? where Name = 'ZM_TELEMETRY_LAST_UPLOAD'`;
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute( "$lastCheck" ) or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute( "$lastCheck" ) or die( "Can't execute: ".$sth->errstr() );
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
@ -108,7 +84,7 @@ if ( $Config{ZM_TELEMETRY_DATA} )
|
||||||
}
|
}
|
||||||
sleep( 3600 );
|
sleep( 3600 );
|
||||||
}
|
}
|
||||||
print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
|
print( 'Update agent exiting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
###############
|
###############
|
||||||
|
@ -123,13 +99,13 @@ sub runSysCmd {
|
||||||
my $path = qx( which $arguments[0] );
|
my $path = qx( which $arguments[0] );
|
||||||
|
|
||||||
my $status = $? >> 8;
|
my $status = $? >> 8;
|
||||||
my $result = "";
|
my $result = '';
|
||||||
if ( !$path || $status ) {
|
if ( !$path || $status ) {
|
||||||
Warning( "Cannot find the $arguments[0] executable." );
|
Warning( "Cannot find the $arguments[0] executable." );
|
||||||
} else {
|
} else {
|
||||||
chomp($path);
|
chomp($path);
|
||||||
$arguments[0] = $path;
|
$arguments[0] = $path;
|
||||||
my $cmd = join(" ",@arguments);
|
my $cmd = join(' ',@arguments);
|
||||||
$result = qx( $cmd );
|
$result = qx( $cmd );
|
||||||
chomp($result);
|
chomp($result);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +121,7 @@ sub sendData {
|
||||||
my $server_endpoint = ZM_TELEMETRY_SERVER_ENDPOINT;
|
my $server_endpoint = ZM_TELEMETRY_SERVER_ENDPOINT;
|
||||||
|
|
||||||
if ( $Config{ZM_UPDATE_CHECK_PROXY} ) {
|
if ( $Config{ZM_UPDATE_CHECK_PROXY} ) {
|
||||||
$ua->proxy( "https", $Config{ZM_UPDATE_CHECK_PROXY} );
|
$ua->proxy( 'https', $Config{ZM_UPDATE_CHECK_PROXY} );
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug("Posting telemetry data to: $server_endpoint");
|
Debug("Posting telemetry data to: $server_endpoint");
|
||||||
|
@ -162,7 +138,7 @@ sub sendData {
|
||||||
my $resp_msg = $resp->decoded_content;
|
my $resp_msg = $resp->decoded_content;
|
||||||
my $resp_code = $resp->code;
|
my $resp_code = $resp->code;
|
||||||
if ($resp->is_success) {
|
if ($resp->is_success) {
|
||||||
Info("Telemetry data uploaded successfully.");
|
Info('Telemetry data uploaded successfully.');
|
||||||
Debug("Telemetry server upload success response message: $resp_msg");
|
Debug("Telemetry server upload success response message: $resp_msg");
|
||||||
} else {
|
} else {
|
||||||
Warning("Telemetry server returned HTTP POST error code: $resp_code");
|
Warning("Telemetry server returned HTTP POST error code: $resp_code");
|
||||||
|
@ -177,16 +153,16 @@ sub getUUID {
|
||||||
my $uuid= "";
|
my $uuid= "";
|
||||||
|
|
||||||
# Verify the current UUID is valid and not nil
|
# Verify the current UUID is valid and not nil
|
||||||
if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne "00000000-0000-0000-0000-000000000000" )) {
|
if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne '00000000-0000-0000-0000-000000000000' )) {
|
||||||
$uuid = $Config{ZM_TELEMETRY_UUID};
|
$uuid = $Config{ZM_TELEMETRY_UUID};
|
||||||
} else {
|
} else {
|
||||||
my $sql = "SELECT uuid()";
|
my $sql = 'SELECT uuid()';
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||||
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
|
||||||
$sql = "UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'";
|
$sql = q`UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`;
|
||||||
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
$res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() );
|
$res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() );
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
@ -198,9 +174,9 @@ return $uuid;
|
||||||
|
|
||||||
# Retrieves the local server's external IP address
|
# Retrieves the local server's external IP address
|
||||||
sub getIP {
|
sub getIP {
|
||||||
my $ipaddr = "0.0.0.0";
|
my $ipaddr = '0.0.0.0';
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
my $server_endpoint = "https://wiki.zoneminder.com/ip.php";
|
my $server_endpoint = 'https://wiki.zoneminder.com/ip.php';
|
||||||
|
|
||||||
my $req = HTTP::Request->new(GET => $server_endpoint);
|
my $req = HTTP::Request->new(GET => $server_endpoint);
|
||||||
my $resp = $ua->request($req);
|
my $resp = $ua->request($req);
|
||||||
|
@ -232,7 +208,7 @@ return $count
|
||||||
sub getMonitorRef {
|
sub getMonitorRef {
|
||||||
my $dbh = shift;
|
my $dbh = shift;
|
||||||
|
|
||||||
my $sql = "SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors";
|
my $sql = 'SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors';
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||||
my $arrayref = $sth->fetchall_arrayref({});
|
my $arrayref = $sth->fetchall_arrayref({});
|
||||||
|
@ -241,34 +217,34 @@ return $arrayref;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getDistro {
|
sub getDistro {
|
||||||
my $kernel = "";
|
my $kernel = '';
|
||||||
my $distro = "";
|
my $distro = '';
|
||||||
my $version = "";
|
my $version = '';
|
||||||
my @uname = uname();
|
my @uname = uname();
|
||||||
|
|
||||||
if ( $uname[0] =~ /Linux/ ) {
|
if ( $uname[0] =~ /Linux/ ) {
|
||||||
Debug("Linux distro detected.");
|
Debug('Linux distro detected.');
|
||||||
($kernel, $distro, $version) = linuxDistro();
|
($kernel, $distro, $version) = linuxDistro();
|
||||||
} elsif ( $uname[0] =~ /.*BSD/ ) {
|
} elsif ( $uname[0] =~ /.*BSD/ ) {
|
||||||
Debug("BSD distro detected.");
|
Debug('BSD distro detected.');
|
||||||
$kernel = $uname[3];
|
$kernel = $uname[3];
|
||||||
$distro = $uname[0];
|
$distro = $uname[0];
|
||||||
$version = $uname[2];
|
$version = $uname[2];
|
||||||
} elsif ( $uname[0] =~ /Darwin/ ) {
|
} elsif ( $uname[0] =~ /Darwin/ ) {
|
||||||
Debug("Mac OS distro detected.");
|
Debug('Mac OS distro detected.');
|
||||||
$kernel = $uname[3];
|
$kernel = $uname[3];
|
||||||
$distro = runSysCmd("sw_vers -productName");
|
$distro = runSysCmd('sw_vers -productName');
|
||||||
$version = runSysCmd("sw_vers -productVersion");
|
$version = runSysCmd('sw_vers -productVersion');
|
||||||
} elsif ( $uname[0] =~ /SunOS|Solaris/ ) {
|
} elsif ( $uname[0] =~ /SunOS|Solaris/ ) {
|
||||||
Debug("Sun Solaris detected.");
|
Debug('Sun Solaris detected.');
|
||||||
$kernel = $uname[3];
|
$kernel = $uname[3];
|
||||||
$distro = $uname[1];
|
$distro = $uname[1];
|
||||||
$version = $uname[2];
|
$version = $uname[2];
|
||||||
} else {
|
} else {
|
||||||
Warning("ZoneMinder was unable to determine the host system. Please report.");
|
Warning('ZoneMinder was unable to determine the host system. Please report.');
|
||||||
$kernel = "Unknown";
|
$kernel = 'Unknown';
|
||||||
$distro = "Unknown";
|
$distro = 'Unknown';
|
||||||
$version = "Unknown";
|
$version = 'Unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($kernel, $distro, $version);
|
return ($kernel, $distro, $version);
|
||||||
|
@ -277,13 +253,13 @@ return ($kernel, $distro, $version);
|
||||||
sub linuxDistro {
|
sub linuxDistro {
|
||||||
my @uname = uname();
|
my @uname = uname();
|
||||||
my $kernel = $uname[2];
|
my $kernel = $uname[2];
|
||||||
my $distro = "Unknown Linux Distro";
|
my $distro = 'Unknown Linux Distro';
|
||||||
my $version = "Unknown Linux Version";
|
my $version = 'Unknown Linux Version';
|
||||||
my $found = 0;
|
my $found = 0;
|
||||||
|
|
||||||
# os-release is the standard for many new distros based on systemd
|
# os-release is the standard for many new distros based on systemd
|
||||||
if ( -f "/etc/os-release" ) {
|
if ( -f '/etc/os-release' ) {
|
||||||
open(my $RELFILE,"<","/etc/os-release") or die( "Can't Open file: $!\n" );
|
open(my $RELFILE,'<','/etc/os-release') or die( "Can't Open file: $!\n" );
|
||||||
while (<$RELFILE>) {
|
while (<$RELFILE>) {
|
||||||
if ( /^NAME=(")?(.*)(?(1)\1|).*$/ ) {
|
if ( /^NAME=(")?(.*)(?(1)\1|).*$/ ) {
|
||||||
$distro = $2;
|
$distro = $2;
|
||||||
|
@ -296,8 +272,8 @@ sub linuxDistro {
|
||||||
}
|
}
|
||||||
close $RELFILE;
|
close $RELFILE;
|
||||||
# exists on many distros but does not always contain useful information, such as redhat
|
# exists on many distros but does not always contain useful information, such as redhat
|
||||||
} elsif ( -f "/etc/lsb-release" ) {
|
} elsif ( -f '/etc/lsb-release' ) {
|
||||||
open(my $RELFILE,"<","/etc/lsb-release") or die( "Can't Open file: $!\n" );
|
open(my $RELFILE,'<','/etc/lsb-release') or die( "Can't Open file: $!\n" );
|
||||||
while (<$RELFILE>) {
|
while (<$RELFILE>) {
|
||||||
if ( /^DISTRIB_DESCRIPTION=(")?(.*)(?(1)\1|).*$/ ) {
|
if ( /^DISTRIB_DESCRIPTION=(")?(.*)(?(1)\1|).*$/ ) {
|
||||||
$distro = $2;
|
$distro = $2;
|
||||||
|
@ -313,13 +289,13 @@ sub linuxDistro {
|
||||||
|
|
||||||
# If all else fails, search through a list of known release files until we find one
|
# If all else fails, search through a list of known release files until we find one
|
||||||
if ( !$found ) {
|
if ( !$found ) {
|
||||||
my @releasefile = ("/etc/SuSE-release", "/etc/redhat-release", "/etc/redhat_version",
|
my @releasefile = ('/etc/SuSE-release', '/etc/redhat-release', '/etc/redhat_version',
|
||||||
"/etc/fedora-release", "/etc/slackware-release", "/etc/slackware-version",
|
'/etc/fedora-release', '/etc/slackware-release', '/etc/slackware-version',
|
||||||
"/etc/debian_release", "/etc/debian_version", "/etc/mandrake-release",
|
'/etc/debian_release', '/etc/debian_version', '/etc/mandrake-release',
|
||||||
"/etc/yellowdog-release", "/etc/gentoo-release");
|
'/etc/yellowdog-release', '/etc/gentoo-release');
|
||||||
foreach (@releasefile) {
|
foreach (@releasefile) {
|
||||||
if ( -f $_ ) {
|
if ( -f $_ ) {
|
||||||
open(my $RELFILE,"<",$_) or die( "Can't Open file: $!\n" );
|
open(my $RELFILE,'<',$_) or die( "Can't Open file: $!\n" );
|
||||||
while (<$RELFILE>) {
|
while (<$RELFILE>) {
|
||||||
if ( /(.*).* (\d+\.?\d*) .*/ ) {
|
if ( /(.*).* (\d+\.?\d*) .*/ ) {
|
||||||
$distro = $1;
|
$distro = $1;
|
||||||
|
@ -334,10 +310,35 @@ sub linuxDistro {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$found ) {
|
if ( !$found ) {
|
||||||
Warning("ZoneMinder was unable to determine Linux distro. Please report.");
|
Warning('ZoneMinder was unable to determine Linux distro. Please report.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($kernel, $distro, $version);
|
return ($kernel, $distro, $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
zmtelemetry.pl - Send usage information to the ZoneMinder development team
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
zmtelemetry.pl
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This script collects usage information of the local system and sends it to the
|
||||||
|
ZoneMinder development team. This data will be used to determine things like
|
||||||
|
who and where our customers are, how big their systems are, the underlying
|
||||||
|
hardware and operating system, etc. This is being done for the sole purpoase of
|
||||||
|
creating a better product for our target audience. This script is intended to
|
||||||
|
be completely transparent to the end user, and can be disabled from the web
|
||||||
|
console under Options.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
none currently
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder External Trigger Script, $Date: 2008-07-25 10:48:16 +0100 (Fri, 25 Jul 2008) $, $Revision: 2612 $
|
# ZoneMinder External Trigger Script
|
||||||
# Copyright (C) 2001-2008 Philip Coombes
|
# Copyright (C) 2001-2008 Philip Coombes
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
|
@ -21,6 +21,428 @@
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use bytes;
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# User config
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
use constant MAX_CONNECT_DELAY => 10;
|
||||||
|
use constant MONITOR_RELOAD_INTERVAL => 300;
|
||||||
|
use constant SELECT_TIMEOUT => 0.25;
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# Channel/Connection Modules
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
@EXTRA_PERL_LIB@
|
||||||
|
use ZoneMinder;
|
||||||
|
use ZoneMinder::Trigger::Channel::Inet;
|
||||||
|
use ZoneMinder::Trigger::Channel::Unix;
|
||||||
|
use ZoneMinder::Trigger::Channel::Serial;
|
||||||
|
use ZoneMinder::Trigger::Connection;
|
||||||
|
|
||||||
|
my @connections;
|
||||||
|
push( @connections,
|
||||||
|
ZoneMinder::Trigger::Connection->new(
|
||||||
|
name=>'Chan1 TCP on port 6802',
|
||||||
|
channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ),
|
||||||
|
mode=>'rw'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
push( @connections,
|
||||||
|
ZoneMinder::Trigger::Connection->new(
|
||||||
|
name=>'Chan2 Unix Socket at ' . $Config{ZM_PATH_SOCKS}.'/zmtrigger.sock',
|
||||||
|
channel=>ZoneMinder::Trigger::Channel::Unix->new(
|
||||||
|
path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock'
|
||||||
|
),
|
||||||
|
mode=>'rw'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>'Chan3', channel=>ZoneMinder::Trigger::Channel::File->new( path=>'/tmp/zmtrigger.out' ), mode=>'w' ) );
|
||||||
|
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>'Chan4', channel=>ZoneMinder::Trigger::Channel::Serial->new( path=>'/dev/ttyS0' ), mode=>'rw' ) );
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# Don't change anything from here on down
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
use DBI;
|
||||||
|
#use Socket;
|
||||||
|
use autouse 'Data::Dumper'=>qw(Dumper);
|
||||||
|
use POSIX qw( EINTR );
|
||||||
|
use Time::HiRes qw( usleep );
|
||||||
|
|
||||||
|
$| = 1;
|
||||||
|
|
||||||
|
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||||
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||||
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
|
logInit();
|
||||||
|
logSetSignal();
|
||||||
|
|
||||||
|
Info( "Trigger daemon starting\n" );
|
||||||
|
|
||||||
|
my $dbh = zmDbConnect();
|
||||||
|
|
||||||
|
my $base_rin = '';
|
||||||
|
foreach my $connection ( @connections ) {
|
||||||
|
Info( "Opening connection '$connection->{name}'\n" );
|
||||||
|
$connection->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
my @in_select_connections = grep { $_->input() && $_->selectable() } @connections;
|
||||||
|
my @in_poll_connections = grep { $_->input() && !$_->selectable() } @connections;
|
||||||
|
my @out_connections = grep { $_->output() } @connections;
|
||||||
|
|
||||||
|
foreach my $connection ( @in_select_connections ) {
|
||||||
|
vec( $base_rin, $connection->fileno(), 1 ) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my %spawned_connections;
|
||||||
|
my %monitors;
|
||||||
|
my $monitor_reload_time = 0;
|
||||||
|
my $needsReload = 0;
|
||||||
|
loadMonitors();
|
||||||
|
|
||||||
|
$! = undef;
|
||||||
|
my $rin = '';
|
||||||
|
my $win = $rin;
|
||||||
|
my $ein = $win;
|
||||||
|
my $timeout = SELECT_TIMEOUT;
|
||||||
|
my %actions;
|
||||||
|
while( 1 ) {
|
||||||
|
$rin = $base_rin;
|
||||||
|
# Add the file descriptors of any spawned connections
|
||||||
|
foreach my $fileno ( keys(%spawned_connections) ) {
|
||||||
|
vec( $rin, $fileno, 1 ) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
|
||||||
|
if ( $nfound > 0 ) {
|
||||||
|
Debug( "Got input from $nfound connections\n" );
|
||||||
|
foreach my $connection ( @in_select_connections ) {
|
||||||
|
if ( vec( $rout, $connection->fileno(), 1 ) ) {
|
||||||
|
Debug( 'Got input from connection '
|
||||||
|
.$connection->name()
|
||||||
|
.' ('
|
||||||
|
.$connection->fileno()
|
||||||
|
.")\n"
|
||||||
|
);
|
||||||
|
if ( $connection->spawns() ) {
|
||||||
|
my $new_connection = $connection->accept();
|
||||||
|
$spawned_connections{$new_connection->fileno()} = $new_connection;
|
||||||
|
Debug( 'Added new spawned connection ('
|
||||||
|
.$new_connection->fileno()
|
||||||
|
.'), '
|
||||||
|
.int(keys(%spawned_connections))
|
||||||
|
." spawned connections\n"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
my $messages = $connection->getMessages();
|
||||||
|
if ( defined($messages) ) {
|
||||||
|
foreach my $message ( @$messages ) {
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} # end foreach connection
|
||||||
|
foreach my $connection ( values(%spawned_connections) ) {
|
||||||
|
if ( vec( $rout, $connection->fileno(), 1 ) ) {
|
||||||
|
Debug( 'Got input from spawned connection '
|
||||||
|
.$connection->name()
|
||||||
|
.' ('
|
||||||
|
.$connection->fileno()
|
||||||
|
.")\n"
|
||||||
|
);
|
||||||
|
my $messages = $connection->getMessages();
|
||||||
|
if ( defined($messages) ) {
|
||||||
|
foreach my $message ( @$messages ) {
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete( $spawned_connections{$connection->fileno()} );
|
||||||
|
Debug( 'Removed spawned connection ('
|
||||||
|
.$connection->fileno()
|
||||||
|
.'), '
|
||||||
|
.int(keys(%spawned_connections))
|
||||||
|
." spawned connections\n"
|
||||||
|
);
|
||||||
|
$connection->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} # end foreach spawned connection
|
||||||
|
} elsif ( $nfound < 0 ) {
|
||||||
|
if ( $! == EINTR ) {
|
||||||
|
# Do nothing
|
||||||
|
} else {
|
||||||
|
Fatal( "Can't select: $!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check polled connections
|
||||||
|
foreach my $connection ( @in_poll_connections ) {
|
||||||
|
my $messages = $connection->getMessages();
|
||||||
|
if ( defined($messages) ) {
|
||||||
|
foreach my $message ( @$messages ) {
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for alarms that might have happened
|
||||||
|
my @out_messages;
|
||||||
|
foreach my $monitor ( values(%monitors) ) {
|
||||||
|
|
||||||
|
if ( ! zmMemVerify($monitor) ) {
|
||||||
|
# Our attempt to verify the memory handle failed. We should reload the monitors.
|
||||||
|
# Don't need to zmMemInvalidate because the monitor reload will do it.
|
||||||
|
$needsReload = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my ( $state, $last_event ) = zmMemRead( $monitor,
|
||||||
|
[
|
||||||
|
'shared_data:state',
|
||||||
|
'shared_data:last_event'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
|
||||||
|
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
|
||||||
|
if ( $state == STATE_ALARM || $state == STATE_ALERT ) {
|
||||||
|
# In alarm state
|
||||||
|
if ( !defined($monitor->{LastEvent})
|
||||||
|
|| ($last_event != $monitor->{LastEvent})
|
||||||
|
) {
|
||||||
|
# A new event
|
||||||
|
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
|
||||||
|
} else {
|
||||||
|
# The same one as last time, so ignore it
|
||||||
|
# Do nothing
|
||||||
|
}
|
||||||
|
} elsif (
|
||||||
|
($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|
||||||
|
||
|
||||||
|
($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
|
||||||
|
) {
|
||||||
|
# Out of alarm state
|
||||||
|
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
|
||||||
|
} elsif (
|
||||||
|
defined($monitor->{LastEvent})
|
||||||
|
&&
|
||||||
|
($last_event != $monitor->{LastEvent})
|
||||||
|
) {
|
||||||
|
# We've missed a whole event
|
||||||
|
push( @out_messages, $monitor->{Id}.'|on|'.time().'|'.$last_event );
|
||||||
|
push( @out_messages, $monitor->{Id}.'|off|'.time().'|'.$last_event );
|
||||||
|
}
|
||||||
|
$monitor->{LastState} = $state;
|
||||||
|
$monitor->{LastEvent} = $last_event;
|
||||||
|
}
|
||||||
|
foreach my $connection ( @out_connections ) {
|
||||||
|
if ( $connection->canWrite() ) {
|
||||||
|
$connection->putMessages( \@out_messages );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach my $connection ( values(%spawned_connections) ) {
|
||||||
|
if ( $connection->canWrite() ) {
|
||||||
|
$connection->putMessages( \@out_messages );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug( "Checking for timed actions\n" ) if int(keys(%actions));
|
||||||
|
my $now = time();
|
||||||
|
foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) ) {
|
||||||
|
Info( "Found actions expiring at $action_time\n" );
|
||||||
|
foreach my $action ( @{$actions{$action_time}} ) {
|
||||||
|
my $connection = $action->{connection};
|
||||||
|
my $message = $action->{message};
|
||||||
|
Info( "Found action '$message'\n" );
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
delete( $actions{$action_time} );
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow connections to do their own timed actions
|
||||||
|
foreach my $connection ( @connections ) {
|
||||||
|
my $messages = $connection->timedActions();
|
||||||
|
if ( defined($messages) ) {
|
||||||
|
foreach my $message ( @$messages ) {
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach my $connection ( values(%spawned_connections) ) {
|
||||||
|
my $messages = $connection->timedActions();
|
||||||
|
if ( defined($messages) ) {
|
||||||
|
foreach my $message ( @$messages ) {
|
||||||
|
handleMessage( $connection, $message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# If necessary reload monitors
|
||||||
|
if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )) {
|
||||||
|
foreach my $monitor ( values(%monitors) ) {
|
||||||
|
# Free up any used memory handle
|
||||||
|
zmMemInvalidate( $monitor );
|
||||||
|
}
|
||||||
|
loadMonitors();
|
||||||
|
$needsReload = 0;
|
||||||
|
}
|
||||||
|
# zmDbConnect will ping and reconnect if neccessary
|
||||||
|
$dbh = zmDbConnect();
|
||||||
|
} # end while ( 1 )
|
||||||
|
Info( "Trigger daemon exiting\n" );
|
||||||
|
exit;
|
||||||
|
|
||||||
|
sub loadMonitors {
|
||||||
|
Debug( "Loading monitors\n" );
|
||||||
|
$monitor_reload_time = time();
|
||||||
|
|
||||||
|
my %new_monitors = ();
|
||||||
|
|
||||||
|
my $sql = "SELECT * FROM Monitors
|
||||||
|
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
|
||||||
|
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' )
|
||||||
|
;
|
||||||
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
|
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
|
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
|
||||||
|
or Fatal( "Can't execute: ".$sth->errstr() );
|
||||||
|
while( my $monitor = $sth->fetchrow_hashref() ) {
|
||||||
|
# Check shared memory ok
|
||||||
|
if ( !zmMemVerify( $monitor ) ) {
|
||||||
|
zmMemInvalidate( $monitor );
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( defined($monitors{$monitor->{Id}}->{LastState}) ) {
|
||||||
|
$monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState};
|
||||||
|
} else {
|
||||||
|
$monitor->{LastState} = zmGetMonitorState( $monitor );
|
||||||
|
}
|
||||||
|
if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) {
|
||||||
|
$monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent};
|
||||||
|
} else {
|
||||||
|
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
|
||||||
|
}
|
||||||
|
$new_monitors{$monitor->{Id}} = $monitor;
|
||||||
|
} # end foreach monitor
|
||||||
|
%monitors = %new_monitors;
|
||||||
|
} # end sub loadMonitors
|
||||||
|
|
||||||
|
sub handleMessage {
|
||||||
|
my $connection = shift;
|
||||||
|
my $message = shift;
|
||||||
|
|
||||||
|
my ( $id, $action, $score, $cause, $text, $showtext )
|
||||||
|
= split( /\|/, $message );
|
||||||
|
$score = 0 if !defined($score);
|
||||||
|
$cause = '' if !defined($cause);
|
||||||
|
$text = '' if !defined($text);
|
||||||
|
|
||||||
|
my $monitor = $monitors{$id};
|
||||||
|
if ( !$monitor ) {
|
||||||
|
Warning( "Can't find monitor '$id' for message '$message'\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Debug( "Found monitor for id '$id'\n" );
|
||||||
|
|
||||||
|
next if !zmMemVerify( $monitor );
|
||||||
|
|
||||||
|
Debug( "Handling action '$action'\n" );
|
||||||
|
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) {
|
||||||
|
my $state = $1;
|
||||||
|
my $delay = $2;
|
||||||
|
if ( $state eq 'enable' ) {
|
||||||
|
zmMonitorEnable( $monitor );
|
||||||
|
} else {
|
||||||
|
zmMonitorDisable( $monitor );
|
||||||
|
}
|
||||||
|
# Force a reload
|
||||||
|
$monitor_reload_time = 0;
|
||||||
|
Info( "Set monitor to $state\n" );
|
||||||
|
if ( $delay ) {
|
||||||
|
my $action_text = $id."|".( ($state eq 'enable')
|
||||||
|
? 'disable'
|
||||||
|
: 'enable'
|
||||||
|
);
|
||||||
|
handleDelay($delay, $connection, $action_text);
|
||||||
|
}
|
||||||
|
} elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ ) {
|
||||||
|
next if !$monitor->{Enabled};
|
||||||
|
|
||||||
|
my $trigger = $1;
|
||||||
|
my $delay = $2;
|
||||||
|
my $trigger_data;
|
||||||
|
if ( $trigger eq 'on' ) {
|
||||||
|
zmTriggerEventOn( $monitor, $score, $cause, $text );
|
||||||
|
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
||||||
|
Info( "Trigger '$trigger' '$cause'\n" );
|
||||||
|
if ( $delay ) {
|
||||||
|
my $action_text = $id.'|cancel';
|
||||||
|
handleDelay($delay, $connection, $action_text);
|
||||||
|
}
|
||||||
|
} elsif ( $trigger eq 'off' ) {
|
||||||
|
if ( $delay ) {
|
||||||
|
my $action_text = $id.'|off|0|'.$cause.'|'.$text;
|
||||||
|
handleDelay($delay, $connection, $action_text);
|
||||||
|
} else {
|
||||||
|
my $last_event = zmGetLastEvent( $monitor );
|
||||||
|
zmTriggerEventOff( $monitor );
|
||||||
|
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
||||||
|
Info( "Trigger '$trigger'\n" );
|
||||||
|
# Wait til it's finished
|
||||||
|
while( zmInAlarm( $monitor )
|
||||||
|
&& ($last_event == zmGetLastEvent( $monitor ))
|
||||||
|
) {
|
||||||
|
# Tenth of a second
|
||||||
|
usleep( 100000 );
|
||||||
|
}
|
||||||
|
zmTriggerEventCancel( $monitor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elsif( $action eq 'cancel' ) {
|
||||||
|
zmTriggerEventCancel( $monitor );
|
||||||
|
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
||||||
|
Info( "Cancelled event\n" );
|
||||||
|
} elsif( $action eq 'show' ) {
|
||||||
|
zmTriggerShowtext( $monitor, $showtext );
|
||||||
|
Info( "Updated show text to '$showtext'\n" );
|
||||||
|
} else {
|
||||||
|
Error( "Unrecognised action '$action' in message '$message'\n" );
|
||||||
|
}
|
||||||
|
} # end sub handleMessage
|
||||||
|
|
||||||
|
sub handleDelay {
|
||||||
|
my $delay = shift;
|
||||||
|
my $connection = shift;
|
||||||
|
my $action_text = shift;
|
||||||
|
|
||||||
|
my $action_time = time()+$delay;
|
||||||
|
my $action_array = $actions{$action_time};
|
||||||
|
if ( !$action_array ) {
|
||||||
|
$action_array = $actions{$action_time} = [];
|
||||||
|
}
|
||||||
|
push @$action_array, {
|
||||||
|
connection => $connection,
|
||||||
|
message => $action_text
|
||||||
|
};
|
||||||
|
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
|
||||||
|
}
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
zmtrigger.pl - ZoneMinder External Trigger Script
|
zmtrigger.pl - ZoneMinder External Trigger Script
|
||||||
|
@ -104,504 +526,6 @@ likely to be easier.
|
||||||
|
|
||||||
3|on+10|1|motion|text|showtext
|
3|on+10|1|motion|text|showtext
|
||||||
|
|
||||||
Triggers "alarm" on camera #3 for 10 seconds with score=1, cause="motion".
|
Triggers 'alarm' on camera #3 for 10 seconds with score=1, cause='motion'.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
use strict;
|
|
||||||
use bytes;
|
|
||||||
|
|
||||||
# ==========================================================================
|
|
||||||
#
|
|
||||||
# User config
|
|
||||||
#
|
|
||||||
# ==========================================================================
|
|
||||||
|
|
||||||
use constant MAX_CONNECT_DELAY => 10;
|
|
||||||
use constant MONITOR_RELOAD_INTERVAL => 300;
|
|
||||||
use constant SELECT_TIMEOUT => 0.25;
|
|
||||||
|
|
||||||
# ==========================================================================
|
|
||||||
#
|
|
||||||
# Channel/Connection Modules
|
|
||||||
#
|
|
||||||
# ==========================================================================
|
|
||||||
|
|
||||||
@EXTRA_PERL_LIB@
|
|
||||||
use ZoneMinder;
|
|
||||||
use ZoneMinder::Trigger::Channel::Inet;
|
|
||||||
use ZoneMinder::Trigger::Channel::Unix;
|
|
||||||
use ZoneMinder::Trigger::Channel::Serial;
|
|
||||||
use ZoneMinder::Trigger::Connection;
|
|
||||||
|
|
||||||
my @connections;
|
|
||||||
push( @connections,
|
|
||||||
ZoneMinder::Trigger::Connection->new(
|
|
||||||
name=>"Chan1 TCP on port 6802",
|
|
||||||
channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ),
|
|
||||||
mode=>"rw"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
push( @connections,
|
|
||||||
ZoneMinder::Trigger::Connection->new(
|
|
||||||
name=>"Chan2 Unix Socket at " . $Config{ZM_PATH_SOCKS}.'/zmtrigger.sock',
|
|
||||||
channel=>ZoneMinder::Trigger::Channel::Unix->new(
|
|
||||||
path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock'
|
|
||||||
),
|
|
||||||
mode=>"rw"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan3", channel=>ZoneMinder::Trigger::Channel::File->new( path=>'/tmp/zmtrigger.out' ), mode=>"w" ) );
|
|
||||||
#push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan4", channel=>ZoneMinder::Trigger::Channel::Serial->new( path=>'/dev/ttyS0' ), mode=>"rw" ) );
|
|
||||||
|
|
||||||
# ==========================================================================
|
|
||||||
#
|
|
||||||
# Don't change anything from here on down
|
|
||||||
#
|
|
||||||
# ==========================================================================
|
|
||||||
|
|
||||||
use DBI;
|
|
||||||
#use Socket;
|
|
||||||
use autouse 'Data::Dumper'=>qw(Dumper);
|
|
||||||
use POSIX qw( EINTR );
|
|
||||||
use Time::HiRes qw( usleep );
|
|
||||||
|
|
||||||
$| = 1;
|
|
||||||
|
|
||||||
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
|
||||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
|
||||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|
||||||
|
|
||||||
logInit();
|
|
||||||
logSetSignal();
|
|
||||||
|
|
||||||
Info( "Trigger daemon starting\n" );
|
|
||||||
|
|
||||||
my $dbh = zmDbConnect();
|
|
||||||
|
|
||||||
my $base_rin = '';
|
|
||||||
foreach my $connection ( @connections )
|
|
||||||
{
|
|
||||||
Info( "Opening connection '$connection->{name}'\n" );
|
|
||||||
$connection->open();
|
|
||||||
}
|
|
||||||
|
|
||||||
my @in_select_connections = grep { $_->input() && $_->selectable() } @connections;
|
|
||||||
my @in_poll_connections = grep { $_->input() && !$_->selectable() } @connections;
|
|
||||||
my @out_connections = grep { $_->output() } @connections;
|
|
||||||
|
|
||||||
foreach my $connection ( @in_select_connections )
|
|
||||||
{
|
|
||||||
vec( $base_rin, $connection->fileno(), 1 ) = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
my %spawned_connections;
|
|
||||||
my %monitors;
|
|
||||||
my $monitor_reload_time = 0;
|
|
||||||
my $needsReload = 0;
|
|
||||||
loadMonitors();
|
|
||||||
|
|
||||||
|
|
||||||
$! = undef;
|
|
||||||
my $rin = '';
|
|
||||||
my $win = $rin;
|
|
||||||
my $ein = $win;
|
|
||||||
my $timeout = SELECT_TIMEOUT;
|
|
||||||
my %actions;
|
|
||||||
while( 1 )
|
|
||||||
{
|
|
||||||
$rin = $base_rin;
|
|
||||||
# Add the file descriptors of any spawned connections
|
|
||||||
foreach my $fileno ( keys(%spawned_connections) )
|
|
||||||
{
|
|
||||||
vec( $rin, $fileno, 1 ) = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout );
|
|
||||||
if ( $nfound > 0 )
|
|
||||||
{
|
|
||||||
Debug( "Got input from $nfound connections\n" );
|
|
||||||
foreach my $connection ( @in_select_connections )
|
|
||||||
{
|
|
||||||
if ( vec( $rout, $connection->fileno(), 1 ) )
|
|
||||||
{
|
|
||||||
Debug( "Got input from connection "
|
|
||||||
.$connection->name()
|
|
||||||
." ("
|
|
||||||
.$connection->fileno()
|
|
||||||
.")\n"
|
|
||||||
);
|
|
||||||
if ( $connection->spawns() )
|
|
||||||
{
|
|
||||||
my $new_connection = $connection->accept();
|
|
||||||
$spawned_connections{$new_connection->fileno()} = $new_connection;
|
|
||||||
Debug( "Added new spawned connection ("
|
|
||||||
.$new_connection->fileno()
|
|
||||||
."), "
|
|
||||||
.int(keys(%spawned_connections))
|
|
||||||
." spawned connections\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
my $messages = $connection->getMessages();
|
|
||||||
if ( defined($messages) )
|
|
||||||
{
|
|
||||||
foreach my $message ( @$messages )
|
|
||||||
{
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach my $connection ( values(%spawned_connections) )
|
|
||||||
{
|
|
||||||
if ( vec( $rout, $connection->fileno(), 1 ) )
|
|
||||||
{
|
|
||||||
Debug( "Got input from spawned connection "
|
|
||||||
.$connection->name()
|
|
||||||
." ("
|
|
||||||
.$connection->fileno()
|
|
||||||
.")\n"
|
|
||||||
);
|
|
||||||
my $messages = $connection->getMessages();
|
|
||||||
if ( defined($messages) )
|
|
||||||
{
|
|
||||||
foreach my $message ( @$messages )
|
|
||||||
{
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete( $spawned_connections{$connection->fileno()} );
|
|
||||||
Debug( "Removed spawned connection ("
|
|
||||||
.$connection->fileno()
|
|
||||||
."), "
|
|
||||||
.int(keys(%spawned_connections))
|
|
||||||
." spawned connections\n"
|
|
||||||
);
|
|
||||||
$connection->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( $nfound < 0 )
|
|
||||||
{
|
|
||||||
if ( $! == EINTR )
|
|
||||||
{
|
|
||||||
# Do nothing
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Fatal( "Can't select: $!" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check polled connections
|
|
||||||
foreach my $connection ( @in_poll_connections )
|
|
||||||
{
|
|
||||||
my $messages = $connection->getMessages();
|
|
||||||
if ( defined($messages) )
|
|
||||||
{
|
|
||||||
foreach my $message ( @$messages )
|
|
||||||
{
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check for alarms that might have happened
|
|
||||||
my @out_messages;
|
|
||||||
foreach my $monitor ( values(%monitors) )
|
|
||||||
{
|
|
||||||
|
|
||||||
if ( ! zmMemVerify($monitor) ) {
|
|
||||||
# Our attempt to verify the memory handle failed. We should reload the monitors.
|
|
||||||
# Don't need to zmMemInvalidate because the monitor reload will do it.
|
|
||||||
$needsReload = 1;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
my ( $state, $last_event )
|
|
||||||
= zmMemRead( $monitor,
|
|
||||||
[ "shared_data:state",
|
|
||||||
"shared_data:last_event"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
#print( "$monitor->{Id}: S:$state, LE:$last_event\n" );
|
|
||||||
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" );
|
|
||||||
if ( $state == STATE_ALARM
|
|
||||||
|| $state == STATE_ALERT
|
|
||||||
) # In alarm state
|
|
||||||
{
|
|
||||||
if ( !defined($monitor->{LastEvent})
|
|
||||||
|| ($last_event != $monitor->{LastEvent})
|
|
||||||
) # A new event
|
|
||||||
{
|
|
||||||
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
|
|
||||||
}
|
|
||||||
else # The same one as last time, so ignore it
|
|
||||||
{
|
|
||||||
# Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( ($state == STATE_IDLE
|
|
||||||
&& $monitor->{LastState} != STATE_IDLE
|
|
||||||
)
|
|
||||||
|| ($state == STATE_TAPE
|
|
||||||
&& $monitor->{LastState} != STATE_TAPE
|
|
||||||
)
|
|
||||||
) # Out of alarm state
|
|
||||||
{
|
|
||||||
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
|
|
||||||
}
|
|
||||||
elsif ( defined($monitor->{LastEvent})
|
|
||||||
&& ($last_event != $monitor->{LastEvent})
|
|
||||||
) # We've missed a whole event
|
|
||||||
{
|
|
||||||
push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event );
|
|
||||||
push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event );
|
|
||||||
}
|
|
||||||
$monitor->{LastState} = $state;
|
|
||||||
$monitor->{LastEvent} = $last_event;
|
|
||||||
}
|
|
||||||
foreach my $connection ( @out_connections )
|
|
||||||
{
|
|
||||||
if ( $connection->canWrite() )
|
|
||||||
{
|
|
||||||
$connection->putMessages( \@out_messages );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach my $connection ( values(%spawned_connections) )
|
|
||||||
{
|
|
||||||
if ( $connection->canWrite() )
|
|
||||||
{
|
|
||||||
$connection->putMessages( \@out_messages );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug( "Checking for timed actions\n" )
|
|
||||||
if ( int(keys(%actions)) );
|
|
||||||
my $now = time();
|
|
||||||
foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) )
|
|
||||||
{
|
|
||||||
Info( "Found actions expiring at $action_time\n" );
|
|
||||||
foreach my $action ( @{$actions{$action_time}} )
|
|
||||||
{
|
|
||||||
my $connection = $action->{connection};
|
|
||||||
my $message = $action->{message};
|
|
||||||
Info( "Found action '$message'\n" );
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
delete( $actions{$action_time} );
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allow connections to do their own timed actions
|
|
||||||
foreach my $connection ( @connections )
|
|
||||||
{
|
|
||||||
my $messages = $connection->timedActions();
|
|
||||||
if ( defined($messages) )
|
|
||||||
{
|
|
||||||
foreach my $message ( @$messages )
|
|
||||||
{
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach my $connection ( values(%spawned_connections) )
|
|
||||||
{
|
|
||||||
my $messages = $connection->timedActions();
|
|
||||||
if ( defined($messages) )
|
|
||||||
{
|
|
||||||
foreach my $message ( @$messages )
|
|
||||||
{
|
|
||||||
handleMessage( $connection, $message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# If necessary reload monitors
|
|
||||||
if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ))
|
|
||||||
{
|
|
||||||
foreach my $monitor ( values(%monitors) )
|
|
||||||
{
|
|
||||||
# Free up any used memory handle
|
|
||||||
zmMemInvalidate( $monitor );
|
|
||||||
}
|
|
||||||
loadMonitors();
|
|
||||||
$needsReload = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Info( "Trigger daemon exiting\n" );
|
|
||||||
exit;
|
|
||||||
|
|
||||||
sub loadMonitors
|
|
||||||
{
|
|
||||||
Debug( "Loading monitors\n" );
|
|
||||||
$monitor_reload_time = time();
|
|
||||||
|
|
||||||
my %new_monitors = ();
|
|
||||||
|
|
||||||
my $sql = "SELECT * FROM Monitors
|
|
||||||
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )".
|
|
||||||
( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' )
|
|
||||||
;
|
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
|
||||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
|
||||||
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
|
|
||||||
or Fatal( "Can't execute: ".$sth->errstr() );
|
|
||||||
while( my $monitor = $sth->fetchrow_hashref() )
|
|
||||||
{
|
|
||||||
# Check shared memory ok
|
|
||||||
if ( !zmMemVerify( $monitor ) ) {
|
|
||||||
zmMemInvalidate( $monitor );
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( defined($monitors{$monitor->{Id}}->{LastState}) )
|
|
||||||
{
|
|
||||||
$monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$monitor->{LastState} = zmGetMonitorState( $monitor );
|
|
||||||
}
|
|
||||||
if ( defined($monitors{$monitor->{Id}}->{LastEvent}) )
|
|
||||||
{
|
|
||||||
$monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
|
|
||||||
}
|
|
||||||
$new_monitors{$monitor->{Id}} = $monitor;
|
|
||||||
}
|
|
||||||
%monitors = %new_monitors;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub handleMessage
|
|
||||||
{
|
|
||||||
my $connection = shift;
|
|
||||||
my $message = shift;
|
|
||||||
|
|
||||||
my ( $id, $action, $score, $cause, $text, $showtext )
|
|
||||||
= split( /\|/, $message );
|
|
||||||
$score = 0 if ( !defined($score) );
|
|
||||||
$cause = "" if ( !defined($cause) );
|
|
||||||
$text = "" if ( !defined($text) );
|
|
||||||
|
|
||||||
my $monitor = $monitors{$id};
|
|
||||||
if ( !$monitor )
|
|
||||||
{
|
|
||||||
Warning( "Can't find monitor '$id' for message '$message'\n" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Debug( "Found monitor for id '$id'\n" );
|
|
||||||
|
|
||||||
next if ( !zmMemVerify( $monitor ) );
|
|
||||||
|
|
||||||
Debug( "Handling action '$action'\n" );
|
|
||||||
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ )
|
|
||||||
{
|
|
||||||
my $state = $1;
|
|
||||||
my $delay = $2;
|
|
||||||
if ( $state eq "enable" )
|
|
||||||
{
|
|
||||||
zmMonitorEnable( $monitor );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zmMonitorDisable( $monitor );
|
|
||||||
}
|
|
||||||
# Force a reload
|
|
||||||
$monitor_reload_time = 0;
|
|
||||||
Info( "Set monitor to $state\n" );
|
|
||||||
if ( $delay )
|
|
||||||
{
|
|
||||||
my $action_text = $id."|".( ($state eq "enable")
|
|
||||||
? "disable"
|
|
||||||
: "enable"
|
|
||||||
);
|
|
||||||
handleDelay($delay, $connection, $action_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ )
|
|
||||||
{
|
|
||||||
next if ( !$monitor->{Enabled} );
|
|
||||||
|
|
||||||
my $trigger = $1;
|
|
||||||
my $delay = $2;
|
|
||||||
my $trigger_data;
|
|
||||||
if ( $trigger eq "on" )
|
|
||||||
{
|
|
||||||
zmTriggerEventOn( $monitor, $score, $cause, $text );
|
|
||||||
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
|
||||||
Info( "Trigger '$trigger' '$cause'\n" );
|
|
||||||
if ( $delay )
|
|
||||||
{
|
|
||||||
my $action_text = $id."|cancel";
|
|
||||||
handleDelay($delay, $connection, $action_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif ( $trigger eq "off" )
|
|
||||||
{
|
|
||||||
if ( $delay )
|
|
||||||
{
|
|
||||||
my $action_text = $id."|off|0|".$cause."|".$text;
|
|
||||||
handleDelay($delay, $connection, $action_text);
|
|
||||||
} else {
|
|
||||||
my $last_event = zmGetLastEvent( $monitor );
|
|
||||||
zmTriggerEventOff( $monitor );
|
|
||||||
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
|
||||||
Info( "Trigger '$trigger'\n" );
|
|
||||||
# Wait til it's finished
|
|
||||||
while( zmInAlarm( $monitor )
|
|
||||||
&& ($last_event == zmGetLastEvent( $monitor ))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
# Tenth of a second
|
|
||||||
usleep( 100000 );
|
|
||||||
}
|
|
||||||
zmTriggerEventCancel( $monitor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif( $action eq "cancel" )
|
|
||||||
{
|
|
||||||
zmTriggerEventCancel( $monitor );
|
|
||||||
zmTriggerShowtext( $monitor, $showtext ) if defined($showtext);
|
|
||||||
Info( "Cancelled event\n" );
|
|
||||||
}
|
|
||||||
elsif( $action eq "show" )
|
|
||||||
{
|
|
||||||
zmTriggerShowtext( $monitor, $showtext );
|
|
||||||
Info( "Updated show text to '$showtext'\n" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error( "Unrecognised action '$action' in message '$message'\n" );
|
|
||||||
}
|
|
||||||
} # end sub handleMessage
|
|
||||||
|
|
||||||
sub handleDelay
|
|
||||||
{
|
|
||||||
my $delay = shift;
|
|
||||||
my $connection = shift;
|
|
||||||
my $action_text = shift;
|
|
||||||
|
|
||||||
my $action_time = time()+$delay;
|
|
||||||
my $action_array = $actions{$action_time};
|
|
||||||
if ( !$action_array )
|
|
||||||
{
|
|
||||||
$action_array = $actions{$action_time} = [];
|
|
||||||
}
|
|
||||||
push( @$action_array, { connection=>$connection,
|
|
||||||
message=>$action_text
|
|
||||||
}
|
|
||||||
);
|
|
||||||
Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" );
|
|
||||||
}
|
|
||||||
1;
|
|
||||||
__END__
|
|
||||||
|
|
|
@ -27,7 +27,9 @@ zmvideo.pl - ZoneMinder Video Creation Script
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
zmvideo.pl [ -e <event_id>,--event=<event_id> | --filter=<filter name> ] [--format <format>]
|
zmvideo.pl [ -e <event_id>,--event=<event_id> | --filter_name=<filter name> | --filter_id=<filter id> ]
|
||||||
|
[--concat=filename]
|
||||||
|
[--format <format>]
|
||||||
[--rate=<rate>]
|
[--rate=<rate>]
|
||||||
[--scale=<scale>]
|
[--scale=<scale>]
|
||||||
[--fps=<fps>]
|
[--fps=<fps>]
|
||||||
|
@ -94,15 +96,6 @@ my $size = '';
|
||||||
my $overwrite = 0;
|
my $overwrite = 0;
|
||||||
my $version = 0;
|
my $version = 0;
|
||||||
|
|
||||||
my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} );
|
|
||||||
for ( my $i = 0; $i < @formats; $i++ )
|
|
||||||
{
|
|
||||||
if ( $i =~ /^(.+)\*$/ )
|
|
||||||
{
|
|
||||||
$format = $formats[$i] = $1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'concat|c:s' =>\$concat_name,
|
'concat|c:s' =>\$concat_name,
|
||||||
'event|e=i' =>\$event_id,
|
'event|e=i' =>\$event_id,
|
||||||
|
@ -122,42 +115,42 @@ if ( $version ) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !( $filter_id or $filter_name or $event_id ) || $event_id < 0 )
|
if ( !( $filter_id or $filter_name or $event_id ) || ($event_id and ( $event_id < 0 ) ) ) {
|
||||||
{
|
|
||||||
print( STDERR "Please give a valid event id or filter name\n" );
|
print( STDERR "Please give a valid event id or filter name\n" );
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $Config{ZM_OPT_FFMPEG} )
|
if ( ! $Config{ZM_OPT_FFMPEG} ) {
|
||||||
{
|
|
||||||
print( STDERR "Mpeg encoding is not currently enabled\n" );
|
print( STDERR "Mpeg encoding is not currently enabled\n" );
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$rate && !$fps )
|
my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} );
|
||||||
{
|
for ( my $i = 0; $i < @formats; $i++ ) {
|
||||||
|
if ( $i =~ /^(.+)\*$/ ) {
|
||||||
|
$format = $formats[$i] = $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$rate && !$fps ) {
|
||||||
$rate = 1;
|
$rate = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$scale && !$size )
|
if ( !$scale && !$size ) {
|
||||||
{
|
|
||||||
$scale = 1;
|
$scale = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $rate && ($rate < 0.25 || $rate > 100) )
|
if ( $rate && ($rate < 0.25 || $rate > 100) ) {
|
||||||
{
|
|
||||||
print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" );
|
print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" );
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $scale && ($scale < 0.25 || $scale > 4) )
|
if ( $scale && ($scale < 0.25 || $scale > 4) ) {
|
||||||
{
|
|
||||||
print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" );
|
print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" );
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $fps && ($fps > 30) )
|
if ( $fps && ($fps > 30) ) {
|
||||||
{
|
|
||||||
print( STDERR "FPS is out of range, <= 30\n" );
|
print( STDERR "FPS is out of range, <= 30\n" );
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
@ -192,28 +185,30 @@ if ( $event_id ) {
|
||||||
Fatal("Filter $filter_name $filter_id not found.");
|
Fatal("Filter $filter_name $filter_id not found.");
|
||||||
}
|
}
|
||||||
@event_ids = map { $_->{Id} } $Filter->Execute();
|
@event_ids = map { $_->{Id} } $Filter->Execute();
|
||||||
Fatal( "No events found for $filter_name") if ! @event_ids;
|
if ( ! @event_ids ) {
|
||||||
|
Fatal( "No events found for $filter_name")
|
||||||
|
} else {
|
||||||
|
Debug(@event_ids . " events found for $filter_name");
|
||||||
|
}
|
||||||
$concat_name = $filter_name if $concat_name eq '';
|
$concat_name = $filter_name if $concat_name eq '';
|
||||||
}
|
}
|
||||||
|
|
||||||
my $sql = " SELECT max(F.Delta)-min(F.Delta) as FullLength,
|
my $sql = " SELECT (SELECT max(Delta) FROM Frames WHERE EventId=Events.Id)-(SELECT min(Delta) FROM Frames WHERE EventId=Events.Id) as FullLength,
|
||||||
E.*,
|
Events.*,
|
||||||
unix_timestamp(E.StartTime) as Time,
|
unix_timestamp(Events.StartTime) as Time,
|
||||||
M.Name as MonitorName,
|
M.Name as MonitorName,
|
||||||
M.Width as MonitorWidth,
|
M.Width as MonitorWidth,
|
||||||
M.Height as MonitorHeight,
|
M.Height as MonitorHeight,
|
||||||
M.Palette
|
M.Palette
|
||||||
FROM Frames as F
|
FROM Events
|
||||||
INNER JOIN Events as E on F.EventId = E.Id
|
INNER JOIN Monitors as M on Events.MonitorId = M.Id
|
||||||
INNER JOIN Monitors as M on E.MonitorId = M.Id
|
WHERE Events.Id = ?
|
||||||
WHERE EventId = ?
|
";
|
||||||
GROUP BY F.EventId"
|
|
||||||
;
|
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
|
Debug($sql);
|
||||||
|
|
||||||
my @video_files;
|
my @video_files;
|
||||||
foreach my $event_id ( @event_ids ) {
|
foreach my $event_id ( @event_ids ) {
|
||||||
|
|
||||||
my $res = $sth->execute( $event_id )
|
my $res = $sth->execute( $event_id )
|
||||||
or Fatal( "Can't execute: ".$sth->errstr() );
|
or Fatal( "Can't execute: ".$sth->errstr() );
|
||||||
my $event = $sth->fetchrow_hashref();
|
my $event = $sth->fetchrow_hashref();
|
||||||
|
@ -223,6 +218,8 @@ foreach my $event_id ( @event_ids ) {
|
||||||
if ( $video_file ) {
|
if ( $video_file ) {
|
||||||
push @video_files, $video_file;
|
push @video_files, $video_file;
|
||||||
print( STDOUT $video_file."\n" );
|
print( STDOUT $video_file."\n" );
|
||||||
|
} else {
|
||||||
|
Warning("No video file generated for event $event_id");
|
||||||
}
|
}
|
||||||
} # end foreach event_id
|
} # end foreach event_id
|
||||||
|
|
||||||
|
@ -240,8 +237,9 @@ if ( $concat_name ) {
|
||||||
}
|
}
|
||||||
close $fd;
|
close $fd;
|
||||||
my $command = $Config{ZM_PATH_FFMPEG}
|
my $command = $Config{ZM_PATH_FFMPEG}
|
||||||
. " -f concat -i $concat_list_file -c copy "
|
. " -f concat -safe 0 -i $concat_list_file -c copy "
|
||||||
." '$video_file' > ffmpeg.log 2>&1"
|
.$Config{ZM_FFMPEG_OUTPUT_OPTIONS}
|
||||||
|
." '$video_file' > $Config{ZM_PATH_LOGS}/ffmpeg_${concat_name}.log 2>&1"
|
||||||
;
|
;
|
||||||
Debug( $command."\n" );
|
Debug( $command."\n" );
|
||||||
my $output = qx($command);
|
my $output = qx($command);
|
||||||
|
@ -249,11 +247,12 @@ if ( $concat_name ) {
|
||||||
my $status = $? >> 8;
|
my $status = $? >> 8;
|
||||||
|
|
||||||
unlink $concat_list_file;
|
unlink $concat_list_file;
|
||||||
if ( $status )
|
if ( $status ) {
|
||||||
{
|
Error( "Unable to generate video, check $Config{ZM_PATH_LOGS}/ffmpeg_${concat_name}.log for details");
|
||||||
Error( "Unable to generate video, check /ffmpeg.log for details");
|
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
print( STDOUT $video_file."\n" );
|
print( STDOUT $video_file."\n" );
|
||||||
}
|
}
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
|
|
||||||
|
__END__
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
||||||
|
|
||||||
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
||||||
set(ZM_BIN_SRC_FILES zm_box.cpp 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_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp)
|
set(ZM_BIN_SRC_FILES zm_box.cpp 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_eventstream.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_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp)
|
||||||
|
|
||||||
# A fix for cmake recompiling the source files for every target.
|
# A fix for cmake recompiling the source files for every target.
|
||||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||||
|
|
|
@ -150,6 +150,12 @@ void process_configfile( char* configFile) {
|
||||||
staticConfig.DB_USER = std::string(val_ptr);
|
staticConfig.DB_USER = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||||
staticConfig.DB_PASS = std::string(val_ptr);
|
staticConfig.DB_PASS = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CA_CERT" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CA_CERT = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_KEY" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CLIENT_KEY = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_CERT" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CLIENT_CERT = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||||
|
|
|
@ -67,6 +67,9 @@ struct StaticConfig
|
||||||
std::string DB_NAME;
|
std::string DB_NAME;
|
||||||
std::string DB_USER;
|
std::string DB_USER;
|
||||||
std::string DB_PASS;
|
std::string DB_PASS;
|
||||||
|
std::string DB_SSL_CA_CERT;
|
||||||
|
std::string DB_SSL_CLIENT_KEY;
|
||||||
|
std::string DB_SSL_CLIENT_CERT;
|
||||||
std::string PATH_WEB;
|
std::string PATH_WEB;
|
||||||
std::string SERVER_NAME;
|
std::string SERVER_NAME;
|
||||||
unsigned int SERVER_ID;
|
unsigned int SERVER_ID;
|
||||||
|
|
|
@ -37,6 +37,8 @@ void zmDbConnect()
|
||||||
my_bool reconnect = 1;
|
my_bool reconnect = 1;
|
||||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||||
|
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||||
|
mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL );
|
||||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
||||||
if ( colonIndex == std::string::npos )
|
if ( colonIndex == std::string::npos )
|
||||||
{
|
{
|
||||||
|
|
783
src/zm_event.cpp
783
src/zm_event.cpp
|
@ -31,21 +31,10 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_db.h"
|
#include "zm_db.h"
|
||||||
#include "zm_time.h"
|
#include "zm_time.h"
|
||||||
#include "zm_mpeg.h"
|
|
||||||
#include "zm_signal.h"
|
#include "zm_signal.h"
|
||||||
#include "zm_event.h"
|
#include "zm_event.h"
|
||||||
#include "zm_monitor.h"
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
// sendfile tricks
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#include "zm_sendfile.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_SYS_SENDFILE_H
|
|
||||||
#include <sys/sendfile.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//#define USE_PREPARED_SQL 1
|
//#define USE_PREPARED_SQL 1
|
||||||
|
|
||||||
bool Event::initialised = false;
|
bool Event::initialised = false;
|
||||||
|
@ -583,775 +572,3 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
|
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
|
||||||
|
|
||||||
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time );
|
|
||||||
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
|
||||||
|
|
||||||
if ( mysql_errno( &dbconn ) ) {
|
|
||||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_event_id = atoi( dbrow[0] );
|
|
||||||
|
|
||||||
mysql_free_result( result );
|
|
||||||
|
|
||||||
loadEventData( init_event_id );
|
|
||||||
|
|
||||||
if ( event_time ) {
|
|
||||||
curr_stream_time = event_time;
|
|
||||||
curr_frame_id = 1;
|
|
||||||
if ( event_time >= event_data->start_time ) {
|
|
||||||
for (unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
|
||||||
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
|
||||||
if ( event_data->frames[i].timestamp >= event_time ) {
|
|
||||||
curr_frame_id = i+1;
|
|
||||||
Debug( 3, "Set cst:%.2f", curr_stream_time );
|
|
||||||
Debug( 3, "Set cfid:%d", curr_frame_id );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Debug( 3, "Skipping %ld frames", event_data->frame_count );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) {
|
|
||||||
loadEventData( init_event_id );
|
|
||||||
|
|
||||||
if ( init_frame_id ) {
|
|
||||||
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
|
||||||
curr_frame_id = init_frame_id;
|
|
||||||
} else {
|
|
||||||
curr_stream_time = event_data->start_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventStream::loadEventData( int event_id ) {
|
|
||||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
|
||||||
|
|
||||||
snprintf( sql, sizeof(sql), "SELECT MonitorId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo FROM Events WHERE Id = %d", event_id );
|
|
||||||
|
|
||||||
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 ) ) {
|
|
||||||
Fatal( "Unable to load event %d, not found in DB", event_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
|
||||||
|
|
||||||
if ( mysql_errno( &dbconn ) ) {
|
|
||||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
delete event_data;
|
|
||||||
event_data = new EventData;
|
|
||||||
event_data->event_id = event_id;
|
|
||||||
event_data->monitor_id = atoi( dbrow[0] );
|
|
||||||
event_data->start_time = atoi(dbrow[2]);
|
|
||||||
if ( config.use_deep_storage ) {
|
|
||||||
struct tm *event_time = localtime( &event_data->start_time );
|
|
||||||
if ( staticConfig.DIR_EVENTS.c_str()[0] == '/' )
|
|
||||||
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
|
|
||||||
else
|
|
||||||
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
|
|
||||||
} else {
|
|
||||||
if ( staticConfig.DIR_EVENTS.c_str()[0] == '/' )
|
|
||||||
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_data->event_id );
|
|
||||||
else
|
|
||||||
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_data->event_id );
|
|
||||||
}
|
|
||||||
event_data->frame_count = dbrow[1] == NULL ? 0 : atoi(dbrow[1]);
|
|
||||||
event_data->duration = atof(dbrow[3]);
|
|
||||||
strncpy( event_data->video_file, dbrow[4], sizeof( event_data->video_file )-1 );
|
|
||||||
|
|
||||||
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
|
||||||
|
|
||||||
mysql_free_result( result );
|
|
||||||
|
|
||||||
snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id );
|
|
||||||
if ( mysql_query( &dbconn, sql ) ) {
|
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
result = mysql_store_result( &dbconn );
|
|
||||||
if ( !result ) {
|
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
event_data->n_frames = mysql_num_rows( result );
|
|
||||||
|
|
||||||
event_data->frames = new FrameData[event_data->frame_count];
|
|
||||||
int id, last_id = 0;
|
|
||||||
time_t timestamp, last_timestamp = event_data->start_time;
|
|
||||||
double delta, last_delta = 0.0;
|
|
||||||
while ( ( dbrow = mysql_fetch_row( result ) ) ) {
|
|
||||||
id = atoi(dbrow[0]);
|
|
||||||
timestamp = atoi(dbrow[1]);
|
|
||||||
delta = atof(dbrow[2]);
|
|
||||||
int id_diff = id - last_id;
|
|
||||||
double frame_delta = (delta-last_delta)/id_diff;
|
|
||||||
if ( id_diff > 1 ) {
|
|
||||||
for ( int i = last_id+1; i < id; i++ ) {
|
|
||||||
event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta));
|
|
||||||
event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time);
|
|
||||||
event_data->frames[i-1].delta = frame_delta;
|
|
||||||
event_data->frames[i-1].in_db = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event_data->frames[id-1].timestamp = timestamp;
|
|
||||||
event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time);
|
|
||||||
event_data->frames[id-1].delta = id>1?frame_delta:0.0;
|
|
||||||
event_data->frames[id-1].in_db = true;
|
|
||||||
last_id = id;
|
|
||||||
last_delta = delta;
|
|
||||||
last_timestamp = timestamp;
|
|
||||||
}
|
|
||||||
if ( mysql_errno( &dbconn ) ) {
|
|
||||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//for ( int i = 0; i < 250; i++ )
|
|
||||||
//{
|
|
||||||
//Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
|
|
||||||
//}
|
|
||||||
|
|
||||||
mysql_free_result( result );
|
|
||||||
|
|
||||||
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
|
|
||||||
if ( replay_rate > 0 )
|
|
||||||
curr_stream_time = event_data->frames[0].timestamp;
|
|
||||||
else
|
|
||||||
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
|
|
||||||
}
|
|
||||||
Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration );
|
|
||||||
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventStream::processCommand( const CmdMsg *msg ) {
|
|
||||||
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
|
||||||
// Check for incoming command
|
|
||||||
switch( (MsgCommand)msg->msg_data[0] ) {
|
|
||||||
case CMD_PAUSE :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got PAUSE command" );
|
|
||||||
|
|
||||||
// Set paused flag
|
|
||||||
paused = true;
|
|
||||||
replay_rate = ZM_RATE_BASE;
|
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_PLAY :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got PLAY command" );
|
|
||||||
if ( paused )
|
|
||||||
{
|
|
||||||
// Clear paused flag
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in single event mode and at the last frame, replay the current event
|
|
||||||
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
|
|
||||||
Debug(1, "Was in single_mode, and last frame, so jumping to 1st frame");
|
|
||||||
curr_frame_id = 1;
|
|
||||||
} else {
|
|
||||||
Debug(1, "mode is %s, current frame is %d, frame count is %d", (mode == MODE_SINGLE ? "single" : "not single" ), curr_frame_id, event_data->frame_count );
|
|
||||||
}
|
|
||||||
|
|
||||||
replay_rate = ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_VARPLAY :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got VARPLAY command" );
|
|
||||||
if ( paused )
|
|
||||||
{
|
|
||||||
// Clear paused flag
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_STOP :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got STOP command" );
|
|
||||||
|
|
||||||
// Clear paused flag
|
|
||||||
paused = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_FASTFWD :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got FAST FWD command" );
|
|
||||||
if ( paused ) {
|
|
||||||
// Clear paused flag
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
// Set play rate
|
|
||||||
switch ( replay_rate ) {
|
|
||||||
case 2 * ZM_RATE_BASE :
|
|
||||||
replay_rate = 5 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case 5 * ZM_RATE_BASE :
|
|
||||||
replay_rate = 10 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case 10 * ZM_RATE_BASE :
|
|
||||||
replay_rate = 25 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case 25 * ZM_RATE_BASE :
|
|
||||||
case 50 * ZM_RATE_BASE :
|
|
||||||
replay_rate = 50 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
replay_rate = 2 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_SLOWFWD :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got SLOW FWD command" );
|
|
||||||
// Set paused flag
|
|
||||||
paused = true;
|
|
||||||
// Set play rate
|
|
||||||
replay_rate = ZM_RATE_BASE;
|
|
||||||
// Set step
|
|
||||||
step = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_SLOWREV :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got SLOW REV command" );
|
|
||||||
// Set paused flag
|
|
||||||
paused = true;
|
|
||||||
// Set play rate
|
|
||||||
replay_rate = ZM_RATE_BASE;
|
|
||||||
// Set step
|
|
||||||
step = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_FASTREV :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got FAST REV command" );
|
|
||||||
if ( paused ) {
|
|
||||||
// Clear paused flag
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
// Set play rate
|
|
||||||
switch ( replay_rate ) {
|
|
||||||
case -2 * ZM_RATE_BASE :
|
|
||||||
replay_rate = -5 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case -5 * ZM_RATE_BASE :
|
|
||||||
replay_rate = -10 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case -10 * ZM_RATE_BASE :
|
|
||||||
replay_rate = -25 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
case -25 * ZM_RATE_BASE :
|
|
||||||
case -50 * ZM_RATE_BASE :
|
|
||||||
replay_rate = -50 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
replay_rate = -2 * ZM_RATE_BASE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_ZOOMIN :
|
|
||||||
{
|
|
||||||
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
|
||||||
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
|
||||||
Debug( 1, "Got ZOOM IN command, to %d,%d", x, y );
|
|
||||||
switch ( zoom ) {
|
|
||||||
case 100:
|
|
||||||
zoom = 150;
|
|
||||||
break;
|
|
||||||
case 150:
|
|
||||||
zoom = 200;
|
|
||||||
break;
|
|
||||||
case 200:
|
|
||||||
zoom = 300;
|
|
||||||
break;
|
|
||||||
case 300:
|
|
||||||
zoom = 400;
|
|
||||||
break;
|
|
||||||
case 400:
|
|
||||||
default :
|
|
||||||
zoom = 500;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
send_frame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_ZOOMOUT :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got ZOOM OUT command" );
|
|
||||||
switch ( zoom ) {
|
|
||||||
case 500:
|
|
||||||
zoom = 400;
|
|
||||||
break;
|
|
||||||
case 400:
|
|
||||||
zoom = 300;
|
|
||||||
break;
|
|
||||||
case 300:
|
|
||||||
zoom = 200;
|
|
||||||
break;
|
|
||||||
case 200:
|
|
||||||
zoom = 150;
|
|
||||||
break;
|
|
||||||
case 150:
|
|
||||||
default :
|
|
||||||
zoom = 100;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
send_frame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_PAN :
|
|
||||||
{
|
|
||||||
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
|
||||||
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
|
||||||
Debug( 1, "Got PAN command, to %d,%d", x, y );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_SCALE :
|
|
||||||
{
|
|
||||||
scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
|
||||||
Debug( 1, "Got SCALE command, to %d", scale );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_PREV :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got PREV command" );
|
|
||||||
if ( replay_rate >= 0 )
|
|
||||||
curr_frame_id = 0;
|
|
||||||
else
|
|
||||||
curr_frame_id = event_data->frame_count+1;
|
|
||||||
paused = false;
|
|
||||||
forceEventChange = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_NEXT :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got NEXT command" );
|
|
||||||
if ( replay_rate >= 0 )
|
|
||||||
curr_frame_id = event_data->frame_count+1;
|
|
||||||
else
|
|
||||||
curr_frame_id = 0;
|
|
||||||
paused = false;
|
|
||||||
forceEventChange = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_SEEK :
|
|
||||||
{
|
|
||||||
int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
|
||||||
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration);
|
|
||||||
Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id );
|
|
||||||
send_frame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_QUERY :
|
|
||||||
{
|
|
||||||
Debug( 1, "Got QUERY command, sending STATUS" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CMD_QUIT :
|
|
||||||
{
|
|
||||||
Info ("User initiated exit - CMD_QUIT");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
// Do nothing, for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct {
|
|
||||||
int event;
|
|
||||||
int progress;
|
|
||||||
int rate;
|
|
||||||
int zoom;
|
|
||||||
bool paused;
|
|
||||||
} status_data;
|
|
||||||
|
|
||||||
status_data.event = event_data->event_id;
|
|
||||||
status_data.progress = (int)event_data->frames[curr_frame_id-1].offset;
|
|
||||||
status_data.rate = replay_rate;
|
|
||||||
status_data.zoom = zoom;
|
|
||||||
status_data.paused = paused;
|
|
||||||
Debug( 2, "Event:%d, Paused:%d, progress:%d Rate:%d, Zoom:%d",
|
|
||||||
status_data.event,
|
|
||||||
status_data.paused,
|
|
||||||
status_data.progress,
|
|
||||||
status_data.rate,
|
|
||||||
status_data.zoom
|
|
||||||
);
|
|
||||||
|
|
||||||
DataMsg status_msg;
|
|
||||||
status_msg.msg_type = MSG_DATA_EVENT;
|
|
||||||
memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) );
|
|
||||||
if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) {
|
|
||||||
//if ( errno != EAGAIN )
|
|
||||||
{
|
|
||||||
Error( "Can't sendto on sd %d: %s", sd, strerror(errno) );
|
|
||||||
exit( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// quit after sending a status, if this was a quit request
|
|
||||||
if ((MsgCommand)msg->msg_data[0]==CMD_QUIT)
|
|
||||||
exit(0);
|
|
||||||
|
|
||||||
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventStream::checkEventLoaded() {
|
|
||||||
bool reload_event = false;
|
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
|
||||||
|
|
||||||
if ( curr_frame_id <= 0 ) {
|
|
||||||
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id );
|
|
||||||
reload_event = true;
|
|
||||||
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
|
|
||||||
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id );
|
|
||||||
reload_event = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( reload_event ) {
|
|
||||||
if ( forceEventChange || mode != MODE_SINGLE ) {
|
|
||||||
//Info( "SQL:%s", sql );
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
|
||||||
|
|
||||||
if ( mysql_errno( &dbconn ) ) {
|
|
||||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( dbrow ) {
|
|
||||||
int event_id = atoi(dbrow[0]);
|
|
||||||
Debug( 1, "Loading new event %d", event_id );
|
|
||||||
|
|
||||||
loadEventData( event_id );
|
|
||||||
|
|
||||||
Debug( 2, "Current frame id = %d", curr_frame_id );
|
|
||||||
if ( replay_rate < 0 )
|
|
||||||
curr_frame_id = event_data->frame_count;
|
|
||||||
else
|
|
||||||
curr_frame_id = 1;
|
|
||||||
Debug( 2, "New frame id = %d", curr_frame_id );
|
|
||||||
} else {
|
|
||||||
if ( curr_frame_id <= 0 )
|
|
||||||
curr_frame_id = 1;
|
|
||||||
else
|
|
||||||
curr_frame_id = event_data->frame_count;
|
|
||||||
paused = true;
|
|
||||||
}
|
|
||||||
mysql_free_result( result );
|
|
||||||
forceEventChange = false;
|
|
||||||
} else {
|
|
||||||
if ( curr_frame_id <= 0 )
|
|
||||||
curr_frame_id = 1;
|
|
||||||
else
|
|
||||||
curr_frame_id = event_data->frame_count;
|
|
||||||
paused = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventStream::sendFrame( int delta_us ) {
|
|
||||||
Debug( 2, "Sending frame %d", curr_frame_id );
|
|
||||||
|
|
||||||
static char filepath[PATH_MAX];
|
|
||||||
static struct stat filestat;
|
|
||||||
FILE *fdj = NULL;
|
|
||||||
|
|
||||||
// This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that.
|
|
||||||
if ( monitor->GetOptSaveJPEGs() & 1 ) {
|
|
||||||
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
|
||||||
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
|
||||||
snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id );
|
|
||||||
if ( stat( filepath, &filestat ) < 0 ) {
|
|
||||||
Debug(1, "%s not found, dalling back to capture");
|
|
||||||
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
|
||||||
if ( type == STREAM_MPEG ) {
|
|
||||||
Image image( filepath );
|
|
||||||
|
|
||||||
Image *send_image = prepareImage( &image );
|
|
||||||
|
|
||||||
if ( !vid_stream ) {
|
|
||||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() );
|
|
||||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
|
||||||
vid_stream->OpenStream();
|
|
||||||
}
|
|
||||||
/* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 );
|
|
||||||
} else
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
|
||||||
{
|
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
|
||||||
|
|
||||||
int img_buffer_size = 0;
|
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
|
||||||
|
|
||||||
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
|
|
||||||
|
|
||||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
|
||||||
|
|
||||||
if ( type != STREAM_JPEG )
|
|
||||||
send_raw = false;
|
|
||||||
|
|
||||||
if ( send_raw ) {
|
|
||||||
fdj = fopen( filepath, "rb" );
|
|
||||||
if ( !fdj ) {
|
|
||||||
Error( "Can't open %s: %s", filepath, strerror(errno) );
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
#if HAVE_SENDFILE
|
|
||||||
if( fstat(fileno(fdj),&filestat) < 0 ) {
|
|
||||||
Error( "Failed getting information about file %s: %s", filepath, strerror(errno) );
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
Image image( filepath );
|
|
||||||
|
|
||||||
Image *send_image = prepareImage( &image );
|
|
||||||
|
|
||||||
switch( type ) {
|
|
||||||
case STREAM_JPEG :
|
|
||||||
send_image->EncodeJpeg( img_buffer, &img_buffer_size );
|
|
||||||
break;
|
|
||||||
case STREAM_ZIP :
|
|
||||||
#if HAVE_ZLIB_H
|
|
||||||
unsigned long zip_buffer_size;
|
|
||||||
send_image->Zip( img_buffer, &zip_buffer_size );
|
|
||||||
img_buffer_size = zip_buffer_size;
|
|
||||||
break;
|
|
||||||
#else
|
|
||||||
Error("zlib is required for zipped images. Falling back to raw image");
|
|
||||||
type = STREAM_RAW;
|
|
||||||
#endif // HAVE_ZLIB_H
|
|
||||||
case STREAM_RAW :
|
|
||||||
img_buffer = (uint8_t*)(send_image->Buffer());
|
|
||||||
img_buffer_size = send_image->Size();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Fatal( "Unexpected frame type %d", type );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( type ) {
|
|
||||||
case STREAM_JPEG :
|
|
||||||
fprintf( stdout, "Content-Type: image/jpeg\r\n" );
|
|
||||||
break;
|
|
||||||
case STREAM_RAW :
|
|
||||||
fprintf( stdout, "Content-Type: image/x-rgb\r\n" );
|
|
||||||
break;
|
|
||||||
case STREAM_ZIP :
|
|
||||||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n" );
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
Fatal( "Unexpected frame type %d", type );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(send_raw) {
|
|
||||||
#if HAVE_SENDFILE
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size );
|
|
||||||
if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) {
|
|
||||||
/* sendfile() failed, use standard way instead */
|
|
||||||
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
|
||||||
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
|
||||||
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
|
||||||
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
|
||||||
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
fclose(fdj); /* Close the file handle */
|
|
||||||
} else {
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
|
||||||
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
|
||||||
Error( "Unable to send stream frame: %s", strerror(errno) );
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf( stdout, "\r\n\r\n" );
|
|
||||||
fflush( stdout );
|
|
||||||
}
|
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventStream::runStream() {
|
|
||||||
Event::Initialise();
|
|
||||||
|
|
||||||
openComms();
|
|
||||||
|
|
||||||
checkInitialised();
|
|
||||||
|
|
||||||
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
|
||||||
|
|
||||||
if ( type == STREAM_JPEG )
|
|
||||||
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" );
|
|
||||||
|
|
||||||
if ( !event_data ) {
|
|
||||||
sendTextFrame( "No event data found" );
|
|
||||||
exit( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
while( !zm_terminate ) {
|
|
||||||
gettimeofday( &now, NULL );
|
|
||||||
|
|
||||||
unsigned int delta_us = 0;
|
|
||||||
send_frame = false;
|
|
||||||
|
|
||||||
// commands may set send_frame to true
|
|
||||||
while(checkCommandQueue());
|
|
||||||
|
|
||||||
if ( step != 0 )
|
|
||||||
curr_frame_id += step;
|
|
||||||
|
|
||||||
checkEventLoaded();
|
|
||||||
|
|
||||||
// Get current frame data
|
|
||||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
|
||||||
|
|
||||||
//Info( "cst:%.2f", curr_stream_time );
|
|
||||||
//Info( "cfid:%d", curr_frame_id );
|
|
||||||
//Info( "fdt:%d", frame_data->timestamp );
|
|
||||||
if ( ! paused ) {
|
|
||||||
bool in_event = true;
|
|
||||||
double time_to_event = 0;
|
|
||||||
if ( replay_rate > 0 ) {
|
|
||||||
time_to_event = event_data->frames[0].timestamp - curr_stream_time;
|
|
||||||
if ( time_to_event > 0 )
|
|
||||||
in_event = false;
|
|
||||||
} else if ( replay_rate < 0 ) {
|
|
||||||
time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp;
|
|
||||||
if ( time_to_event > 0 )
|
|
||||||
in_event = false;
|
|
||||||
}
|
|
||||||
if ( ! in_event ) {
|
|
||||||
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
|
||||||
if ( actual_delta_time > 1 ) {
|
|
||||||
static char frame_text[64];
|
|
||||||
snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event );
|
|
||||||
if ( !sendTextFrame( frame_text ) )
|
|
||||||
zm_terminate = true;
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
usleep( STREAM_PAUSE_WAIT );
|
|
||||||
//curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
|
|
||||||
curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
|
||||||
//}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Figure out if we should send this frame
|
|
||||||
// If we are streaming and this frame is due to be sent
|
|
||||||
if ( ((curr_frame_id-1)%frame_mod) == 0 ) {
|
|
||||||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
|
||||||
// if effective > base we should speed up frame delivery
|
|
||||||
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
|
||||||
// but must not exceed maxfps
|
|
||||||
delta_us = max(delta_us, 1000000 / maxfps);
|
|
||||||
send_frame = true;
|
|
||||||
}
|
|
||||||
} else if ( step != 0 ) {
|
|
||||||
// We are paused and are just stepping forward or backward one frame
|
|
||||||
step = 0;
|
|
||||||
send_frame = true;
|
|
||||||
} else if ( ! send_frame ) {
|
|
||||||
// We are paused, and doing nothing
|
|
||||||
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
|
||||||
if ( actual_delta_time > MAX_STREAM_DELAY ) {
|
|
||||||
// Send keepalive
|
|
||||||
Debug( 2, "Sending keepalive frame" );
|
|
||||||
send_frame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( send_frame )
|
|
||||||
if ( !sendFrame( delta_us ) )
|
|
||||||
zm_terminate = true;
|
|
||||||
|
|
||||||
curr_stream_time = frame_data->timestamp;
|
|
||||||
|
|
||||||
if ( !paused ) {
|
|
||||||
curr_frame_id += replay_rate>0?1:-1;
|
|
||||||
if ( send_frame && type != STREAM_MPEG ) {
|
|
||||||
Debug( 3, "dUs: %d", delta_us );
|
|
||||||
if ( delta_us )
|
|
||||||
usleep( delta_us );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) );
|
|
||||||
}
|
|
||||||
} // end while ! zm_terminate
|
|
||||||
#if HAVE_LIBAVCODEC
|
|
||||||
if ( type == STREAM_MPEG )
|
|
||||||
delete vid_stream;
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
|
||||||
|
|
||||||
closeComms();
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,817 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Event Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/msg.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
|
#include "zm.h"
|
||||||
|
#include "zm_db.h"
|
||||||
|
#include "zm_time.h"
|
||||||
|
#include "zm_mpeg.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
#include "zm_event.h"
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
|
// sendfile tricks
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "zm_sendfile.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
|
||||||
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
|
|
||||||
|
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time );
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
|
|
||||||
|
if ( mysql_errno( &dbconn ) ) {
|
||||||
|
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_event_id = atoi( dbrow[0] );
|
||||||
|
|
||||||
|
mysql_free_result( result );
|
||||||
|
|
||||||
|
loadEventData( init_event_id );
|
||||||
|
|
||||||
|
if ( event_time ) {
|
||||||
|
curr_stream_time = event_time;
|
||||||
|
curr_frame_id = 1;
|
||||||
|
if ( event_time >= event_data->start_time ) {
|
||||||
|
for (unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
||||||
|
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
||||||
|
if ( event_data->frames[i].timestamp >= event_time ) {
|
||||||
|
curr_frame_id = i+1;
|
||||||
|
Debug( 3, "Set cst:%.2f", curr_stream_time );
|
||||||
|
Debug( 3, "Set cfid:%d", curr_frame_id );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug( 3, "Skipping %ld frames", event_data->frame_count );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) {
|
||||||
|
loadEventData( init_event_id );
|
||||||
|
|
||||||
|
if ( init_frame_id ) {
|
||||||
|
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
||||||
|
curr_frame_id = init_frame_id;
|
||||||
|
} else {
|
||||||
|
curr_stream_time = event_data->start_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventStream::loadEventData( int event_id ) {
|
||||||
|
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||||
|
|
||||||
|
snprintf( sql, sizeof(sql), "SELECT MonitorId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo FROM Events WHERE Id = %d", event_id );
|
||||||
|
|
||||||
|
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 ) ) {
|
||||||
|
Fatal( "Unable to load event %d, not found in DB", event_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
|
|
||||||
|
if ( mysql_errno( &dbconn ) ) {
|
||||||
|
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
delete event_data;
|
||||||
|
event_data = new EventData;
|
||||||
|
event_data->event_id = event_id;
|
||||||
|
event_data->monitor_id = atoi( dbrow[0] );
|
||||||
|
event_data->start_time = atoi(dbrow[2]);
|
||||||
|
if ( config.use_deep_storage ) {
|
||||||
|
struct tm *event_time = localtime( &event_data->start_time );
|
||||||
|
if ( staticConfig.DIR_EVENTS.c_str()[0] == '/' )
|
||||||
|
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
|
||||||
|
else
|
||||||
|
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
|
||||||
|
} else {
|
||||||
|
if ( staticConfig.DIR_EVENTS.c_str()[0] == '/' )
|
||||||
|
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_data->event_id );
|
||||||
|
else
|
||||||
|
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str(), event_data->monitor_id, event_data->event_id );
|
||||||
|
}
|
||||||
|
event_data->frame_count = dbrow[1] == NULL ? 0 : atoi(dbrow[1]);
|
||||||
|
event_data->duration = atof(dbrow[3]);
|
||||||
|
strncpy( event_data->video_file, dbrow[4], sizeof( event_data->video_file )-1 );
|
||||||
|
|
||||||
|
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
||||||
|
|
||||||
|
mysql_free_result( result );
|
||||||
|
|
||||||
|
snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id );
|
||||||
|
if ( mysql_query( &dbconn, sql ) ) {
|
||||||
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mysql_store_result( &dbconn );
|
||||||
|
if ( !result ) {
|
||||||
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
event_data->n_frames = mysql_num_rows( result );
|
||||||
|
|
||||||
|
event_data->frames = new FrameData[event_data->frame_count];
|
||||||
|
int id, last_id = 0;
|
||||||
|
time_t timestamp, last_timestamp = event_data->start_time;
|
||||||
|
double delta, last_delta = 0.0;
|
||||||
|
while ( ( dbrow = mysql_fetch_row( result ) ) ) {
|
||||||
|
id = atoi(dbrow[0]);
|
||||||
|
timestamp = atoi(dbrow[1]);
|
||||||
|
delta = atof(dbrow[2]);
|
||||||
|
int id_diff = id - last_id;
|
||||||
|
double frame_delta = (delta-last_delta)/id_diff;
|
||||||
|
if ( id_diff > 1 ) {
|
||||||
|
for ( int i = last_id+1; i < id; i++ ) {
|
||||||
|
event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta));
|
||||||
|
event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time);
|
||||||
|
event_data->frames[i-1].delta = frame_delta;
|
||||||
|
event_data->frames[i-1].in_db = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event_data->frames[id-1].timestamp = timestamp;
|
||||||
|
event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time);
|
||||||
|
event_data->frames[id-1].delta = id>1?frame_delta:0.0;
|
||||||
|
event_data->frames[id-1].in_db = true;
|
||||||
|
last_id = id;
|
||||||
|
last_delta = delta;
|
||||||
|
last_timestamp = timestamp;
|
||||||
|
}
|
||||||
|
if ( mysql_errno( &dbconn ) ) {
|
||||||
|
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//for ( int i = 0; i < 250; i++ )
|
||||||
|
//{
|
||||||
|
//Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
|
||||||
|
//}
|
||||||
|
|
||||||
|
mysql_free_result( result );
|
||||||
|
|
||||||
|
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
|
||||||
|
if ( replay_rate > 0 )
|
||||||
|
curr_stream_time = event_data->frames[0].timestamp;
|
||||||
|
else
|
||||||
|
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
|
||||||
|
}
|
||||||
|
Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration );
|
||||||
|
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventStream::processCommand( const CmdMsg *msg ) {
|
||||||
|
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
||||||
|
// Check for incoming command
|
||||||
|
switch( (MsgCommand)msg->msg_data[0] ) {
|
||||||
|
case CMD_PAUSE :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got PAUSE command" );
|
||||||
|
|
||||||
|
// Set paused flag
|
||||||
|
paused = true;
|
||||||
|
replay_rate = ZM_RATE_BASE;
|
||||||
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_PLAY :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got PLAY command" );
|
||||||
|
if ( paused )
|
||||||
|
{
|
||||||
|
// Clear paused flag
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in single event mode and at the last frame, replay the current event
|
||||||
|
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
|
||||||
|
curr_frame_id = 1;
|
||||||
|
Debug(1, "Was in single_mode, and last frame, so jumping to 1st frame");
|
||||||
|
curr_frame_id = 1;
|
||||||
|
} else {
|
||||||
|
Debug(1, "mode is %s, current frame is %d, frame count is %d", (mode == MODE_SINGLE ? "single" : "not single" ), curr_frame_id, event_data->frame_count );
|
||||||
|
}
|
||||||
|
|
||||||
|
replay_rate = ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_VARPLAY :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got VARPLAY command" );
|
||||||
|
if ( paused )
|
||||||
|
{
|
||||||
|
// Clear paused flag
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_STOP :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got STOP command" );
|
||||||
|
|
||||||
|
// Clear paused flag
|
||||||
|
paused = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_FASTFWD :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got FAST FWD command" );
|
||||||
|
if ( paused ) {
|
||||||
|
// Clear paused flag
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
// Set play rate
|
||||||
|
switch ( replay_rate ) {
|
||||||
|
case 2 * ZM_RATE_BASE :
|
||||||
|
replay_rate = 5 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case 5 * ZM_RATE_BASE :
|
||||||
|
replay_rate = 10 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case 10 * ZM_RATE_BASE :
|
||||||
|
replay_rate = 25 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case 25 * ZM_RATE_BASE :
|
||||||
|
case 50 * ZM_RATE_BASE :
|
||||||
|
replay_rate = 50 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
replay_rate = 2 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SLOWFWD :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got SLOW FWD command" );
|
||||||
|
// Set paused flag
|
||||||
|
paused = true;
|
||||||
|
// Set play rate
|
||||||
|
replay_rate = ZM_RATE_BASE;
|
||||||
|
// Set step
|
||||||
|
step = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SLOWREV :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got SLOW REV command" );
|
||||||
|
// Set paused flag
|
||||||
|
paused = true;
|
||||||
|
// Set play rate
|
||||||
|
replay_rate = ZM_RATE_BASE;
|
||||||
|
// Set step
|
||||||
|
step = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_FASTREV :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got FAST REV command" );
|
||||||
|
if ( paused ) {
|
||||||
|
// Clear paused flag
|
||||||
|
paused = false;
|
||||||
|
}
|
||||||
|
// Set play rate
|
||||||
|
switch ( replay_rate ) {
|
||||||
|
case -2 * ZM_RATE_BASE :
|
||||||
|
replay_rate = -5 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case -5 * ZM_RATE_BASE :
|
||||||
|
replay_rate = -10 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case -10 * ZM_RATE_BASE :
|
||||||
|
replay_rate = -25 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
case -25 * ZM_RATE_BASE :
|
||||||
|
case -50 * ZM_RATE_BASE :
|
||||||
|
replay_rate = -50 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
replay_rate = -2 * ZM_RATE_BASE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_ZOOMIN :
|
||||||
|
{
|
||||||
|
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
||||||
|
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
||||||
|
Debug( 1, "Got ZOOM IN command, to %d,%d", x, y );
|
||||||
|
switch ( zoom ) {
|
||||||
|
case 100:
|
||||||
|
zoom = 150;
|
||||||
|
break;
|
||||||
|
case 150:
|
||||||
|
zoom = 200;
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
zoom = 300;
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
zoom = 400;
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
default :
|
||||||
|
zoom = 500;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_frame = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_ZOOMOUT :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got ZOOM OUT command" );
|
||||||
|
switch ( zoom ) {
|
||||||
|
case 500:
|
||||||
|
zoom = 400;
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
zoom = 300;
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
zoom = 200;
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
zoom = 150;
|
||||||
|
break;
|
||||||
|
case 150:
|
||||||
|
default :
|
||||||
|
zoom = 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
send_frame = true;
|
||||||
|
}
|
||||||
|
case CMD_PAN :
|
||||||
|
{
|
||||||
|
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
||||||
|
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
||||||
|
Debug( 1, "Got PAN command, to %d,%d", x, y );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SCALE :
|
||||||
|
{
|
||||||
|
scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
||||||
|
Debug( 1, "Got SCALE command, to %d", scale );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_PREV :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got PREV command" );
|
||||||
|
if ( replay_rate >= 0 )
|
||||||
|
curr_frame_id = 0;
|
||||||
|
else
|
||||||
|
curr_frame_id = event_data->frame_count+1;
|
||||||
|
paused = false;
|
||||||
|
forceEventChange = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_NEXT :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got NEXT command" );
|
||||||
|
if ( replay_rate >= 0 )
|
||||||
|
curr_frame_id = event_data->frame_count+1;
|
||||||
|
else
|
||||||
|
curr_frame_id = 0;
|
||||||
|
paused = false;
|
||||||
|
forceEventChange = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_SEEK :
|
||||||
|
{
|
||||||
|
int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
||||||
|
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration);
|
||||||
|
Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id );
|
||||||
|
send_frame = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_QUERY :
|
||||||
|
{
|
||||||
|
Debug( 1, "Got QUERY command, sending STATUS" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CMD_QUIT :
|
||||||
|
{
|
||||||
|
Info ("User initiated exit - CMD_QUIT");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
// Do nothing, for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct {
|
||||||
|
int event;
|
||||||
|
int progress;
|
||||||
|
int rate;
|
||||||
|
int zoom;
|
||||||
|
bool paused;
|
||||||
|
} status_data;
|
||||||
|
|
||||||
|
status_data.event = event_data->event_id;
|
||||||
|
status_data.progress = (int)event_data->frames[curr_frame_id-1].offset;
|
||||||
|
status_data.rate = replay_rate;
|
||||||
|
status_data.zoom = zoom;
|
||||||
|
status_data.paused = paused;
|
||||||
|
Debug( 2, "Event:%d, Paused:%d, progress:%d Rate:%d, Zoom:%d",
|
||||||
|
status_data.event,
|
||||||
|
status_data.paused,
|
||||||
|
status_data.progress,
|
||||||
|
status_data.rate,
|
||||||
|
status_data.zoom
|
||||||
|
);
|
||||||
|
|
||||||
|
DataMsg status_msg;
|
||||||
|
status_msg.msg_type = MSG_DATA_EVENT;
|
||||||
|
memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) );
|
||||||
|
if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) {
|
||||||
|
//if ( errno != EAGAIN )
|
||||||
|
{
|
||||||
|
Error( "Can't sendto on sd %d: %s", sd, strerror(errno) );
|
||||||
|
exit( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// quit after sending a status, if this was a quit request
|
||||||
|
if ((MsgCommand)msg->msg_data[0]==CMD_QUIT)
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventStream::checkEventLoaded() {
|
||||||
|
bool reload_event = false;
|
||||||
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
|
|
||||||
|
if ( curr_frame_id <= 0 ) {
|
||||||
|
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id );
|
||||||
|
reload_event = true;
|
||||||
|
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
|
||||||
|
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id );
|
||||||
|
reload_event = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( reload_event ) {
|
||||||
|
if ( forceEventChange || mode != MODE_SINGLE ) {
|
||||||
|
//Info( "SQL:%s", sql );
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
|
|
||||||
|
if ( mysql_errno( &dbconn ) ) {
|
||||||
|
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||||
|
exit( mysql_errno( &dbconn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dbrow ) {
|
||||||
|
int event_id = atoi(dbrow[0]);
|
||||||
|
Debug( 1, "Loading new event %d", event_id );
|
||||||
|
|
||||||
|
loadEventData( event_id );
|
||||||
|
|
||||||
|
Debug( 2, "Current frame id = %d", curr_frame_id );
|
||||||
|
if ( replay_rate < 0 )
|
||||||
|
curr_frame_id = event_data->frame_count;
|
||||||
|
else
|
||||||
|
curr_frame_id = 1;
|
||||||
|
Debug( 2, "New frame id = %d", curr_frame_id );
|
||||||
|
} else {
|
||||||
|
if ( curr_frame_id <= 0 )
|
||||||
|
curr_frame_id = 1;
|
||||||
|
else
|
||||||
|
curr_frame_id = event_data->frame_count;
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
mysql_free_result( result );
|
||||||
|
forceEventChange = false;
|
||||||
|
} else {
|
||||||
|
if ( curr_frame_id <= 0 )
|
||||||
|
curr_frame_id = 1;
|
||||||
|
else
|
||||||
|
curr_frame_id = event_data->frame_count;
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventStream::sendFrame( int delta_us ) {
|
||||||
|
Debug( 2, "Sending frame %d", curr_frame_id );
|
||||||
|
|
||||||
|
static char filepath[PATH_MAX];
|
||||||
|
static struct stat filestat;
|
||||||
|
FILE *fdj = NULL;
|
||||||
|
|
||||||
|
// This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that.
|
||||||
|
if ( monitor->GetOptSaveJPEGs() & 1 ) {
|
||||||
|
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
||||||
|
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
||||||
|
snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id );
|
||||||
|
if ( stat( filepath, &filestat ) < 0 ) {
|
||||||
|
Debug(1, "%s not found, dalling back to capture");
|
||||||
|
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_LIBAVCODEC
|
||||||
|
if ( type == STREAM_MPEG ) {
|
||||||
|
Image image( filepath );
|
||||||
|
|
||||||
|
Image *send_image = prepareImage( &image );
|
||||||
|
|
||||||
|
if ( !vid_stream ) {
|
||||||
|
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() );
|
||||||
|
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||||
|
vid_stream->OpenStream();
|
||||||
|
}
|
||||||
|
/* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 );
|
||||||
|
} else
|
||||||
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
{
|
||||||
|
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
|
||||||
|
int img_buffer_size = 0;
|
||||||
|
uint8_t *img_buffer = temp_img_buffer;
|
||||||
|
|
||||||
|
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
|
||||||
|
|
||||||
|
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||||
|
|
||||||
|
if ( type != STREAM_JPEG )
|
||||||
|
send_raw = false;
|
||||||
|
|
||||||
|
if ( send_raw ) {
|
||||||
|
fdj = fopen( filepath, "rb" );
|
||||||
|
if ( !fdj ) {
|
||||||
|
Error( "Can't open %s: %s", filepath, strerror(errno) );
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
#if HAVE_SENDFILE
|
||||||
|
if( fstat(fileno(fdj),&filestat) < 0 ) {
|
||||||
|
Error( "Failed getting information about file %s: %s", filepath, strerror(errno) );
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
Image image( filepath );
|
||||||
|
|
||||||
|
Image *send_image = prepareImage( &image );
|
||||||
|
|
||||||
|
switch( type ) {
|
||||||
|
case STREAM_JPEG :
|
||||||
|
send_image->EncodeJpeg( img_buffer, &img_buffer_size );
|
||||||
|
break;
|
||||||
|
case STREAM_ZIP :
|
||||||
|
#if HAVE_ZLIB_H
|
||||||
|
unsigned long zip_buffer_size;
|
||||||
|
send_image->Zip( img_buffer, &zip_buffer_size );
|
||||||
|
img_buffer_size = zip_buffer_size;
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
Error("zlib is required for zipped images. Falling back to raw image");
|
||||||
|
type = STREAM_RAW;
|
||||||
|
#endif // HAVE_ZLIB_H
|
||||||
|
case STREAM_RAW :
|
||||||
|
img_buffer = (uint8_t*)(send_image->Buffer());
|
||||||
|
img_buffer_size = send_image->Size();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Fatal( "Unexpected frame type %d", type );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( type ) {
|
||||||
|
case STREAM_JPEG :
|
||||||
|
fprintf( stdout, "Content-Type: image/jpeg\r\n" );
|
||||||
|
break;
|
||||||
|
case STREAM_RAW :
|
||||||
|
fprintf( stdout, "Content-Type: image/x-rgb\r\n" );
|
||||||
|
break;
|
||||||
|
case STREAM_ZIP :
|
||||||
|
fprintf( stdout, "Content-Type: image/x-rgbz\r\n" );
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
Fatal( "Unexpected frame type %d", type );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(send_raw) {
|
||||||
|
#if HAVE_SENDFILE
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size );
|
||||||
|
if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) {
|
||||||
|
/* sendfile() failed, use standard way instead */
|
||||||
|
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fclose(fdj); /* Close the file handle */
|
||||||
|
} else {
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error( "Unable to send stream frame: %s", strerror(errno) );
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
|
fflush( stdout );
|
||||||
|
}
|
||||||
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventStream::runStream() {
|
||||||
|
Event::Initialise();
|
||||||
|
|
||||||
|
openComms();
|
||||||
|
|
||||||
|
checkInitialised();
|
||||||
|
|
||||||
|
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
||||||
|
|
||||||
|
if ( type == STREAM_JPEG )
|
||||||
|
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" );
|
||||||
|
|
||||||
|
if ( !event_data ) {
|
||||||
|
sendTextFrame( "No event data found" );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
while( !zm_terminate ) {
|
||||||
|
gettimeofday( &now, NULL );
|
||||||
|
|
||||||
|
unsigned int delta_us = 0;
|
||||||
|
send_frame = false;
|
||||||
|
|
||||||
|
// commands may set send_frame to true
|
||||||
|
while(checkCommandQueue());
|
||||||
|
|
||||||
|
if ( step != 0 )
|
||||||
|
curr_frame_id += step;
|
||||||
|
|
||||||
|
checkEventLoaded();
|
||||||
|
|
||||||
|
// Get current frame data
|
||||||
|
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
|
|
||||||
|
//Info( "cst:%.2f", curr_stream_time );
|
||||||
|
//Info( "cfid:%d", curr_frame_id );
|
||||||
|
//Info( "fdt:%d", frame_data->timestamp );
|
||||||
|
if ( !paused ) {
|
||||||
|
bool in_event = true;
|
||||||
|
double time_to_event = 0;
|
||||||
|
if ( replay_rate > 0 ) {
|
||||||
|
time_to_event = event_data->frames[0].timestamp - curr_stream_time;
|
||||||
|
if ( time_to_event > 0 )
|
||||||
|
in_event = false;
|
||||||
|
} else if ( replay_rate < 0 ) {
|
||||||
|
time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp;
|
||||||
|
if ( time_to_event > 0 )
|
||||||
|
in_event = false;
|
||||||
|
}
|
||||||
|
if ( !in_event ) {
|
||||||
|
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
||||||
|
if ( actual_delta_time > 1 ) {
|
||||||
|
static char frame_text[64];
|
||||||
|
snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event );
|
||||||
|
if ( !sendTextFrame( frame_text ) )
|
||||||
|
zm_terminate = true;
|
||||||
|
}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
usleep( STREAM_PAUSE_WAIT );
|
||||||
|
//curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
|
||||||
|
curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
||||||
|
//}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out if we should send this frame
|
||||||
|
|
||||||
|
// If we are streaming and this frame is due to be sent
|
||||||
|
if ( ((curr_frame_id-1)%frame_mod) == 0 ) {
|
||||||
|
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||||
|
// if effective > base we should speed up frame delivery
|
||||||
|
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
||||||
|
// but must not exceed maxfps
|
||||||
|
delta_us = max(delta_us, 1000000 / maxfps);
|
||||||
|
send_frame = true;
|
||||||
|
}
|
||||||
|
} else if ( step != 0 ) {
|
||||||
|
// We are paused and are just stepping forward or backward one frame
|
||||||
|
step = 0;
|
||||||
|
send_frame = true;
|
||||||
|
} else if ( !send_frame ) {
|
||||||
|
// We are paused, and doing nothing
|
||||||
|
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
||||||
|
if ( actual_delta_time > MAX_STREAM_DELAY ) {
|
||||||
|
// Send keepalive
|
||||||
|
Debug( 2, "Sending keepalive frame" );
|
||||||
|
send_frame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( send_frame )
|
||||||
|
if ( !sendFrame( delta_us ) )
|
||||||
|
zm_terminate = true;
|
||||||
|
|
||||||
|
curr_stream_time = frame_data->timestamp;
|
||||||
|
|
||||||
|
if ( !paused ) {
|
||||||
|
curr_frame_id += replay_rate>0?1:-1;
|
||||||
|
if ( send_frame && type != STREAM_MPEG ) {
|
||||||
|
Debug( 3, "dUs: %d", delta_us );
|
||||||
|
if ( delta_us )
|
||||||
|
usleep( delta_us );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) );
|
||||||
|
}
|
||||||
|
} // end while ! zm_terminate
|
||||||
|
#if HAVE_LIBAVCODEC
|
||||||
|
if ( type == STREAM_MPEG )
|
||||||
|
delete vid_stream;
|
||||||
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
||||||
|
closeComms();
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Core Interfaces, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ZM_EVENTSTREAM_H
|
||||||
|
#define ZM_EVENTSTREAM_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "zm_image.h"
|
||||||
|
#include "zm_stream.h"
|
||||||
|
#include "zm_video.h"
|
||||||
|
|
||||||
|
class EventStream : public StreamBase {
|
||||||
|
public:
|
||||||
|
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct FrameData {
|
||||||
|
//unsigned long id;
|
||||||
|
time_t timestamp;
|
||||||
|
time_t offset;
|
||||||
|
double delta;
|
||||||
|
bool in_db;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EventData {
|
||||||
|
unsigned long event_id;
|
||||||
|
unsigned long monitor_id;
|
||||||
|
unsigned long frame_count;
|
||||||
|
time_t start_time;
|
||||||
|
double duration;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int n_frames;
|
||||||
|
FrameData *frames;
|
||||||
|
char video_file[PATH_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||||
|
|
||||||
|
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StreamMode mode;
|
||||||
|
bool forceEventChange;
|
||||||
|
|
||||||
|
int curr_frame_id;
|
||||||
|
double curr_stream_time;
|
||||||
|
|
||||||
|
EventData *event_data;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool loadEventData( int event_id );
|
||||||
|
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id );
|
||||||
|
bool loadInitialEventData( int monitor_id, time_t event_time );
|
||||||
|
|
||||||
|
void checkEventLoaded();
|
||||||
|
void processCommand( const CmdMsg *msg );
|
||||||
|
bool sendFrame( int delta_us );
|
||||||
|
|
||||||
|
public:
|
||||||
|
EventStream() {
|
||||||
|
mode = DEFAULT_MODE;
|
||||||
|
|
||||||
|
forceEventChange = false;
|
||||||
|
|
||||||
|
curr_frame_id = 0;
|
||||||
|
curr_stream_time = 0.0;
|
||||||
|
|
||||||
|
event_data = 0;
|
||||||
|
}
|
||||||
|
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
|
||||||
|
loadInitialEventData( init_event_id, init_frame_id );
|
||||||
|
loadMonitor( event_data->monitor_id );
|
||||||
|
}
|
||||||
|
void setStreamStart( int monitor_id, time_t event_time ) {
|
||||||
|
loadInitialEventData( monitor_id, event_time );
|
||||||
|
loadMonitor( monitor_id );
|
||||||
|
}
|
||||||
|
void setStreamMode( StreamMode p_mode ) {
|
||||||
|
mode = p_mode;
|
||||||
|
}
|
||||||
|
void runStream();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ZM_EVENTSTREAM_H
|
|
@ -32,8 +32,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
/* Workaround for GNU/kFreeBSD */
|
/* Workaround for GNU/kFreeBSD and FreeBSD */
|
||||||
#if defined(__FreeBSD_kernel__)
|
#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
|
||||||
#ifndef ENODATA
|
#ifndef ENODATA
|
||||||
#define ENODATA ENOATTR
|
#define ENODATA ENOATTR
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -195,7 +195,7 @@ void Logger::initialise( const std::string &id, const Options &options ) {
|
||||||
level( tempLevel );
|
level( tempLevel );
|
||||||
|
|
||||||
mFlush = false;
|
mFlush = false;
|
||||||
if (envPtr = getenv( "LOG_FLUSH")) {
|
if ( (envPtr = getenv("LOG_FLUSH")) ) {
|
||||||
mFlush = atoi( envPtr );
|
mFlush = atoi( envPtr );
|
||||||
} else if ( config.log_debug ) {
|
} else if ( config.log_debug ) {
|
||||||
mFlush = true;
|
mFlush = true;
|
||||||
|
@ -341,6 +341,8 @@ Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
|
||||||
my_bool reconnect = 1;
|
my_bool reconnect = 1;
|
||||||
if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) )
|
if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) );
|
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) );
|
||||||
|
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||||
|
mysql_ssl_set( &mDbConnection, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL );
|
||||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
||||||
if ( colonIndex == std::string::npos ) {
|
if ( colonIndex == std::string::npos ) {
|
||||||
if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
|
if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
|
||||||
|
|
|
@ -24,6 +24,10 @@ extern "C" {
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif // __FreeBSD__
|
||||||
|
|
||||||
class ZMPacket {
|
class ZMPacket {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,13 @@ RETSIGTYPE zm_die_handler(int signal)
|
||||||
ucontext_t *uc = (ucontext_t *) context;
|
ucontext_t *uc = (ucontext_t *) context;
|
||||||
cr2 = info->si_addr;
|
cr2 = info->si_addr;
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
#ifdef __FreeBSD_kernel__
|
#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
|
||||||
ip = (void *)(uc->uc_mcontext.mc_rip);
|
ip = (void *)(uc->uc_mcontext.mc_rip);
|
||||||
#else
|
#else
|
||||||
ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]);
|
ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]);
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
|
||||||
ip = (void *)(uc->uc_mcontext.mc_eip);
|
ip = (void *)(uc->uc_mcontext.mc_eip);
|
||||||
#else
|
#else
|
||||||
ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]);
|
ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]);
|
||||||
|
|
|
@ -30,8 +30,7 @@ class Monitor;
|
||||||
|
|
||||||
#define TV_2_FLOAT( tv ) ( double((tv).tv_sec) + (double((tv).tv_usec) / 1000000.0) )
|
#define TV_2_FLOAT( tv ) ( double((tv).tv_sec) + (double((tv).tv_usec) / 1000000.0) )
|
||||||
|
|
||||||
class StreamBase
|
class StreamBase {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||||
|
|
||||||
|
@ -110,8 +109,7 @@ protected:
|
||||||
virtual void processCommand( const CmdMsg *msg )=0;
|
virtual void processCommand( const CmdMsg *msg )=0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamBase()
|
StreamBase() {
|
||||||
{
|
|
||||||
monitor = 0;
|
monitor = 0;
|
||||||
|
|
||||||
type = DEFAULT_TYPE;
|
type = DEFAULT_TYPE;
|
||||||
|
@ -145,32 +143,27 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~StreamBase();
|
virtual ~StreamBase();
|
||||||
|
|
||||||
void setStreamType( StreamType p_type )
|
void setStreamType( StreamType p_type ) {
|
||||||
{
|
|
||||||
type = p_type;
|
type = p_type;
|
||||||
}
|
}
|
||||||
void setStreamFormat( const char *p_format )
|
void setStreamFormat( const char *p_format ) {
|
||||||
{
|
|
||||||
format = p_format;
|
format = p_format;
|
||||||
}
|
}
|
||||||
void setStreamScale( int p_scale )
|
void setStreamScale( int p_scale ) {
|
||||||
{
|
|
||||||
scale = p_scale;
|
scale = p_scale;
|
||||||
|
if ( ! scale )
|
||||||
|
scale = DEFAULT_SCALE;
|
||||||
}
|
}
|
||||||
void setStreamReplayRate( int p_rate )
|
void setStreamReplayRate( int p_rate ) {
|
||||||
{
|
|
||||||
replay_rate = p_rate;
|
replay_rate = p_rate;
|
||||||
}
|
}
|
||||||
void setStreamMaxFPS( double p_maxfps )
|
void setStreamMaxFPS( double p_maxfps ) {
|
||||||
{
|
|
||||||
maxfps = p_maxfps;
|
maxfps = p_maxfps;
|
||||||
}
|
}
|
||||||
void setStreamBitrate( int p_bitrate )
|
void setStreamBitrate( int p_bitrate ) {
|
||||||
{
|
|
||||||
bitrate = p_bitrate;
|
bitrate = p_bitrate;
|
||||||
}
|
}
|
||||||
void setStreamQueue( int p_connkey )
|
void setStreamQueue( int p_connkey ) {
|
||||||
{
|
|
||||||
connkey = p_connkey;
|
connkey = p_connkey;
|
||||||
}
|
}
|
||||||
virtual void openComms();
|
virtual void openComms();
|
||||||
|
|
125
src/zm_video.cpp
125
src/zm_video.cpp
|
@ -1,3 +1,4 @@
|
||||||
|
// Copyright (C) 2001-2017 ZoneMinder LLC
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
// as published by the Free Software Foundation; either version 2
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
@ -18,9 +19,25 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_rgb.h"
|
#include "zm_rgb.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
VideoWriter::VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) :
|
VideoWriter::VideoWriter(
|
||||||
container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_height), colours(p_colours), subpixelorder(p_subpixelorder), frame_count(0) {
|
const char* p_container,
|
||||||
|
const char* p_codec,
|
||||||
|
const char* p_path,
|
||||||
|
const unsigned int p_width,
|
||||||
|
const unsigned int p_height,
|
||||||
|
const unsigned int p_colours,
|
||||||
|
const unsigned int p_subpixelorder) :
|
||||||
|
container(p_container),
|
||||||
|
codec(p_codec),
|
||||||
|
path(p_path),
|
||||||
|
width(p_width),
|
||||||
|
height(p_height),
|
||||||
|
colours(p_colours),
|
||||||
|
subpixelorder(p_subpixelorder),
|
||||||
|
frame_count(0) {
|
||||||
Debug(7, "Video object created");
|
Debug(7, "Video object created");
|
||||||
|
|
||||||
/* Parameter checking */
|
/* Parameter checking */
|
||||||
|
@ -30,12 +47,10 @@ container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_h
|
||||||
if ( !width || !height ) {
|
if ( !width || !height ) {
|
||||||
Error("Invalid width or height");
|
Error("Invalid width or height");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoWriter::~VideoWriter() {
|
VideoWriter::~VideoWriter() {
|
||||||
Debug(7, "Video object destroyed");
|
Debug(7, "Video object destroyed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoWriter::Reset(const char* new_path) {
|
int VideoWriter::Reset(const char* new_path) {
|
||||||
|
@ -54,8 +69,24 @@ int VideoWriter::Reset(const char* new_path) {
|
||||||
|
|
||||||
|
|
||||||
#if ZM_HAVE_VIDEOWRITER_X264MP4
|
#if ZM_HAVE_VIDEOWRITER_X264MP4
|
||||||
X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector<EncoderParameter_t>* p_user_params) : VideoWriter("mp4", "h264", p_path, p_width, p_height, p_colours, p_subpixelorder), bOpen(false), bGotH264AVCInfo(false), bFirstFrame(true) {
|
X264MP4Writer::X264MP4Writer(
|
||||||
|
const char* p_path,
|
||||||
|
const unsigned int p_width,
|
||||||
|
const unsigned int p_height,
|
||||||
|
const unsigned int p_colours,
|
||||||
|
const unsigned int p_subpixelorder,
|
||||||
|
const std::vector<EncoderParameter_t>* p_user_params) :
|
||||||
|
VideoWriter(
|
||||||
|
"mp4",
|
||||||
|
"h264",
|
||||||
|
p_path,
|
||||||
|
p_width,
|
||||||
|
p_height,
|
||||||
|
p_colours,
|
||||||
|
p_subpixelorder),
|
||||||
|
bOpen(false),
|
||||||
|
bGotH264AVCInfo(false),
|
||||||
|
bFirstFrame(true) {
|
||||||
/* Initialize ffmpeg if it hasn't been initialized yet */
|
/* Initialize ffmpeg if it hasn't been initialized yet */
|
||||||
FFMPEGInit();
|
FFMPEGInit();
|
||||||
|
|
||||||
|
@ -90,22 +121,22 @@ X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, con
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate x264 input picture */
|
/* Allocate x264 input picture */
|
||||||
x264_picture_alloc(&x264picin, X264_CSP_I420, x264params.i_width, x264params.i_height);
|
x264_picture_alloc(
|
||||||
|
&x264picin,
|
||||||
|
X264_CSP_I420,
|
||||||
|
x264params.i_width,
|
||||||
|
x264params.i_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
X264MP4Writer::~X264MP4Writer() {
|
X264MP4Writer::~X264MP4Writer() {
|
||||||
|
|
||||||
/* Free x264 input picture */
|
/* Free x264 input picture */
|
||||||
x264_picture_clean(&x264picin);
|
x264_picture_clean(&x264picin);
|
||||||
|
|
||||||
if ( bOpen )
|
if ( bOpen )
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int X264MP4Writer::Open() {
|
int X264MP4Writer::Open() {
|
||||||
|
|
||||||
/* Open the encoder */
|
/* Open the encoder */
|
||||||
x264enc = x264_encoder_open(&x264params);
|
x264enc = x264_encoder_open(&x264params);
|
||||||
if ( x264enc == NULL ) {
|
if ( x264enc == NULL ) {
|
||||||
|
@ -113,7 +144,8 @@ int X264MP4Writer::Open() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug(4,"x264 maximum delayed frames: %d",x264_encoder_maximum_delayed_frames(x264enc));
|
// Debug(4,"x264 maximum delayed frames: %d",
|
||||||
|
// x264_encoder_maximum_delayed_frames(x264enc));
|
||||||
|
|
||||||
x264_nal_t* nals;
|
x264_nal_t* nals;
|
||||||
int i_nals;
|
int i_nals;
|
||||||
|
@ -123,7 +155,7 @@ int X264MP4Writer::Open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search SPS NAL for AVC information */
|
/* Search SPS NAL for AVC information */
|
||||||
for(int i=0;i<i_nals;i++) {
|
for ( unsigned int i = 0; i < i_nals; i++ ) {
|
||||||
if ( nals[i].i_type == NAL_SPS ) {
|
if ( nals[i].i_type == NAL_SPS ) {
|
||||||
x264_profleindication = nals[i].p_payload[5];
|
x264_profleindication = nals[i].p_payload[5];
|
||||||
x264_profilecompat = nals[i].p_payload[6];
|
x264_profilecompat = nals[i].p_payload[6];
|
||||||
|
@ -156,7 +188,16 @@ int X264MP4Writer::Open() {
|
||||||
MP4SetVideoProfileLevel(mp4h, 0x7f);
|
MP4SetVideoProfileLevel(mp4h, 0x7f);
|
||||||
|
|
||||||
/* Add H264 video track */
|
/* Add H264 video track */
|
||||||
mp4vtid = MP4AddH264VideoTrack(mp4h,1000,MP4_INVALID_DURATION,width,height,x264_profleindication,x264_profilecompat,x264_levelindication,3);
|
mp4vtid = MP4AddH264VideoTrack(
|
||||||
|
mp4h,
|
||||||
|
1000,
|
||||||
|
MP4_INVALID_DURATION,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x264_profleindication,
|
||||||
|
x264_profilecompat,
|
||||||
|
x264_levelindication,
|
||||||
|
3);
|
||||||
if ( mp4vtid == MP4_INVALID_TRACK_ID ) {
|
if ( mp4vtid == MP4_INVALID_TRACK_ID ) {
|
||||||
Error("Failed adding H264 video track");
|
Error("Failed adding H264 video track");
|
||||||
return -12;
|
return -12;
|
||||||
|
@ -168,7 +209,6 @@ int X264MP4Writer::Open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int X264MP4Writer::Close() {
|
int X264MP4Writer::Close() {
|
||||||
|
|
||||||
/* Flush all pending frames */
|
/* Flush all pending frames */
|
||||||
for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
|
for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
|
||||||
x264encodeloop(true);
|
x264encodeloop(true);
|
||||||
|
@ -194,7 +234,6 @@ int X264MP4Writer::Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int X264MP4Writer::Reset(const char* new_path) {
|
int X264MP4Writer::Reset(const char* new_path) {
|
||||||
|
|
||||||
/* Close the encoder and file */
|
/* Close the encoder and file */
|
||||||
if ( bOpen )
|
if ( bOpen )
|
||||||
Close();
|
Close();
|
||||||
|
@ -217,8 +256,10 @@ int X264MP4Writer::Reset(const char* new_path) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) {
|
int X264MP4Writer::Encode(
|
||||||
|
const uint8_t* data,
|
||||||
|
const size_t data_size,
|
||||||
|
const unsigned int frame_time) {
|
||||||
/* Parameter checking */
|
/* Parameter checking */
|
||||||
if ( data == NULL ) {
|
if ( data == NULL ) {
|
||||||
Error("NULL buffer");
|
Error("NULL buffer");
|
||||||
|
@ -226,7 +267,7 @@ int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const uns
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( data_size != zm_imgsize ) {
|
if ( data_size != zm_imgsize ) {
|
||||||
Error("The data buffer size does not match the expected size. Expected: %d Current: %d", zm_imgsize, data_size);
|
Error("The data buffer size (%d) != expected (%d)", data_size, zm_imgsize);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +295,6 @@ int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const uns
|
||||||
}
|
}
|
||||||
|
|
||||||
int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) {
|
int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) {
|
||||||
|
|
||||||
if ( img->Width() != width ) {
|
if ( img->Width() != width ) {
|
||||||
Error("Source image width differs. Source: %d Output: %d", img->Width(), width);
|
Error("Source image width differs. Source: %d Output: %d", img->Width(), width);
|
||||||
return -12;
|
return -12;
|
||||||
|
@ -333,7 +373,10 @@ int X264MP4Writer::x264config() {
|
||||||
/* Process user parameters (excluding preset, tune and profile) */
|
/* Process user parameters (excluding preset, tune and profile) */
|
||||||
for ( unsigned int i = 0; i < user_params.size(); i++ ) {
|
for ( unsigned int i = 0; i < user_params.size(); i++ ) {
|
||||||
/* Skip preset, tune and profile */
|
/* Skip preset, tune and profile */
|
||||||
if( (strcmp(user_params[i].pname, "preset") == 0) || (strcmp(user_params[i].pname, "tune") == 0) || (strcmp(user_params[i].pname, "profile") == 0) ) {
|
if (
|
||||||
|
(strcmp(user_params[i].pname, "preset") == 0) ||
|
||||||
|
(strcmp(user_params[i].pname, "tune") == 0) ||
|
||||||
|
(strcmp(user_params[i].pname, "profile") == 0) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,11 +386,14 @@ int X264MP4Writer::x264config() {
|
||||||
/* Error checking */
|
/* Error checking */
|
||||||
if ( x264ret != 0 ) {
|
if ( x264ret != 0 ) {
|
||||||
if ( x264ret == X264_PARAM_BAD_NAME ) {
|
if ( x264ret == X264_PARAM_BAD_NAME ) {
|
||||||
Error("Failed processing x264 user parameter %s=%s : Bad name", user_params[i].pname, user_params[i].pvalue);
|
Error("Failed processing x264 user parameter %s=%s : Bad name",
|
||||||
|
user_params[i].pname, user_params[i].pvalue);
|
||||||
} else if ( x264ret == X264_PARAM_BAD_VALUE ) {
|
} else if ( x264ret == X264_PARAM_BAD_VALUE ) {
|
||||||
Error("Failed processing x264 user parameter %s=%s : Bad value", user_params[i].pname, user_params[i].pvalue);
|
Error("Failed processing x264 user parameter %s=%s : Bad value",
|
||||||
|
user_params[i].pname, user_params[i].pvalue);
|
||||||
} else {
|
} else {
|
||||||
Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", user_params[i].pname, user_params[i].pvalue, x264ret);
|
Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)",
|
||||||
|
user_params[i].pname, user_params[i].pvalue, x264ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,7 +402,6 @@ int X264MP4Writer::x264config() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void X264MP4Writer::x264encodeloop(bool bFlush) {
|
void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||||
|
|
||||||
x264_nal_t* nals;
|
x264_nal_t* nals;
|
||||||
int i_nals;
|
int i_nals;
|
||||||
int frame_size;
|
int frame_size;
|
||||||
|
@ -368,16 +413,18 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( frame_size > 0 || bFlush ) {
|
if ( frame_size > 0 || bFlush ) {
|
||||||
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
|
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",
|
||||||
|
frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
|
||||||
|
|
||||||
/* Handle the previous frame */
|
/* Handle the previous frame */
|
||||||
if ( !bFirstFrame ) {
|
if ( !bFirstFrame ) {
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
|
|
||||||
/* Process the NALs for the previous frame */
|
/* Process the NALs for the previous frame */
|
||||||
for ( unsigned int i = 0; i < prevnals.size(); i++ ) {
|
for ( unsigned int i = 0; i < prevnals.size(); i++ ) {
|
||||||
Debug(9,"Processing NAL: Type %d Size %d",prevnals[i].i_type,prevnals[i].i_payload);
|
Debug(9, "Processing NAL: Type %d Size %d",
|
||||||
|
prevnals[i].i_type,
|
||||||
|
prevnals[i].i_payload);
|
||||||
|
|
||||||
switch ( prevnals[i].i_type ) {
|
switch ( prevnals[i].i_type ) {
|
||||||
case NAL_PPS:
|
case NAL_PPS:
|
||||||
|
@ -400,7 +447,14 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||||
|
|
||||||
/* Write the sample */
|
/* Write the sample */
|
||||||
if ( !buffer.empty() ) {
|
if ( !buffer.empty() ) {
|
||||||
if(!MP4WriteSample(mp4h, mp4vtid, buffer.extract(buffer.size()), buffer.size(), duration, offset, prevKeyframe)) {
|
if ( !MP4WriteSample(
|
||||||
|
mp4h,
|
||||||
|
mp4vtid,
|
||||||
|
buffer.extract(buffer.size()),
|
||||||
|
buffer.size(),
|
||||||
|
duration,
|
||||||
|
offset,
|
||||||
|
prevKeyframe) ) {
|
||||||
Error("Failed writing sample");
|
Error("Failed writing sample");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,14 +462,12 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
prevnals.clear();
|
prevnals.clear();
|
||||||
prevpayload.clear();
|
prevpayload.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Got a frame. Copy this new frame into the previous frame */
|
/* Got a frame. Copy this new frame into the previous frame */
|
||||||
if ( frame_size > 0 ) {
|
if ( frame_size > 0 ) {
|
||||||
/* Copy the NALs and the payloads */
|
/* Copy the NALs and the payloads */
|
||||||
for(int i=0;i<i_nals;i++) {
|
for ( unsigned int i = 0; i < i_nals; i++ ) {
|
||||||
|
|
||||||
prevnals.push_back(nals[i]);
|
prevnals.push_back(nals[i]);
|
||||||
prevpayload.append(nals[i].p_payload, nals[i].i_payload);
|
prevpayload.append(nals[i].p_payload, nals[i].i_payload);
|
||||||
}
|
}
|
||||||
|
@ -438,14 +490,18 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( frame_size == 0 ) {
|
} else if ( frame_size == 0 ) {
|
||||||
Debug(7,"x264 encode returned zero. Delayed frames: %d",x264_encoder_delayed_frames(x264enc));
|
Debug(7, "x264 encode returned zero. Delayed frames: %d",
|
||||||
|
x264_encoder_delayed_frames(x264enc));
|
||||||
} else {
|
} else {
|
||||||
Error("x264 encode failed: %d", frame_size);
|
Error("x264 encode failed: %d", frame_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // ZM_VIDEOWRITER_X264MP4
|
#endif // ZM_VIDEOWRITER_X264MP4
|
||||||
|
|
||||||
int ParseEncoderParameters(const char* str, std::vector<EncoderParameter_t>* vec) {
|
int ParseEncoderParameters(
|
||||||
|
const char* str,
|
||||||
|
std::vector<EncoderParameter_t>* vec
|
||||||
|
) {
|
||||||
if ( vec == NULL ) {
|
if ( vec == NULL ) {
|
||||||
Error("NULL Encoder parameters vector pointer");
|
Error("NULL Encoder parameters vector pointer");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -489,7 +545,6 @@ int ParseEncoderParameters(const char* str, std::vector<EncoderParameter_t>* vec
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( valueoffset > (sizeof(param.pname) - 1 ) ) {
|
if ( valueoffset > (sizeof(param.pname) - 1 ) ) {
|
||||||
Warning("Failed parsing encoder parameters line %d: Name too long", lineno);
|
Warning("Failed parsing encoder parameters line %d: Name too long", lineno);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -189,6 +189,8 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
audio_output_codec = NULL;
|
audio_output_codec = NULL;
|
||||||
audio_input_context = NULL;
|
audio_input_context = NULL;
|
||||||
audio_output_stream = NULL;
|
audio_output_stream = NULL;
|
||||||
|
input_frame = NULL;
|
||||||
|
output_frame = NULL;
|
||||||
#ifdef HAVE_LIBAVRESAMPLE
|
#ifdef HAVE_LIBAVRESAMPLE
|
||||||
resample_context = NULL;
|
resample_context = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
@ -391,6 +393,15 @@ Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts
|
||||||
|
|
||||||
/* free the stream */
|
/* free the stream */
|
||||||
avformat_free_context(oc);
|
avformat_free_context(oc);
|
||||||
|
|
||||||
|
if ( input_frame ) {
|
||||||
|
av_frame_free( &input_frame );
|
||||||
|
input_frame = NULL;
|
||||||
|
}
|
||||||
|
if ( output_frame ) {
|
||||||
|
av_frame_free( &output_frame );
|
||||||
|
output_frame = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoStore::setup_resampler() {
|
bool VideoStore::setup_resampler() {
|
||||||
|
@ -687,13 +698,7 @@ int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) {
|
||||||
opkt.data = ipkt->data;
|
opkt.data = ipkt->data;
|
||||||
opkt.size = ipkt->size;
|
opkt.size = ipkt->size;
|
||||||
|
|
||||||
// Some camera have audio on stream 0 and video on stream 1. So when we remove the audio, video stream has to go on 0
|
opkt.stream_index = video_output_stream->index;
|
||||||
if ( ipkt->stream_index > 0 and ! audio_output_stream ) {
|
|
||||||
Debug(1,"Setting stream index to 0 instead of %d", ipkt->stream_index );
|
|
||||||
opkt.stream_index = 0;
|
|
||||||
} else {
|
|
||||||
opkt.stream_index = ipkt->stream_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVPacket safepkt;
|
AVPacket safepkt;
|
||||||
memcpy( &safepkt, &opkt, sizeof(AVPacket) );
|
memcpy( &safepkt, &opkt, sizeof(AVPacket) );
|
||||||
|
@ -718,6 +723,10 @@ Debug(1, "writing video packet pts(%d) dts(%d) duration(%d)", opkt.pts, opkt.dts
|
||||||
// There's nothing we can really do if the frame is rejected, just drop it and get on with the next
|
// There's nothing we can really do if the frame is rejected, just drop it and get on with the next
|
||||||
Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret));
|
Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret));
|
||||||
dumpPacket(&safepkt);
|
dumpPacket(&safepkt);
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
zm_dump_codecpar( video_input_stream->codecpar );
|
||||||
|
zm_dump_codecpar( video_output_stream->codecpar );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,8 +923,7 @@ int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) {
|
||||||
|
|
||||||
// pkt.pos: byte position in stream, -1 if unknown
|
// pkt.pos: byte position in stream, -1 if unknown
|
||||||
opkt.pos = -1;
|
opkt.pos = -1;
|
||||||
opkt.stream_index = ipkt->stream_index;
|
opkt.stream_index = audio_output_stream->index;//ipkt->stream_index;
|
||||||
Debug(2, "Stream index is %d", opkt.stream_index );
|
|
||||||
|
|
||||||
AVPacket safepkt;
|
AVPacket safepkt;
|
||||||
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
||||||
|
|
151
src/zms.cpp
151
src/zms.cpp
|
@ -26,32 +26,26 @@
|
||||||
#include "zm_signal.h"
|
#include "zm_signal.h"
|
||||||
#include "zm_monitor.h"
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
bool ValidateAccess( User *user, int mon_id )
|
bool ValidateAccess( User *user, int mon_id ) {
|
||||||
{
|
|
||||||
bool allowed = true;
|
bool allowed = true;
|
||||||
|
|
||||||
if ( mon_id > 0 )
|
if ( mon_id > 0 ) {
|
||||||
{
|
|
||||||
if ( user->getStream() < User::PERM_VIEW )
|
if ( user->getStream() < User::PERM_VIEW )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
if ( !user->canAccess( mon_id ) )
|
if ( !user->canAccess( mon_id ) )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( user->getEvents() < User::PERM_VIEW )
|
if ( user->getEvents() < User::PERM_VIEW )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
}
|
}
|
||||||
if ( !allowed )
|
if ( !allowed ) {
|
||||||
{
|
|
||||||
Error( "Error, insufficient privileges for requested action" );
|
Error( "Error, insufficient privileges for requested action" );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
return( allowed );
|
return( allowed );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, const char *argv[] )
|
int main( int argc, const char *argv[] ) {
|
||||||
{
|
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
@ -82,8 +76,7 @@ int main( int argc, const char *argv[] )
|
||||||
else //argv[0] will not always contain the full path, but rather just the script name
|
else //argv[0] will not always contain the full path, but rather just the script name
|
||||||
basename = argv[0];
|
basename = argv[0];
|
||||||
const char *nph_prefix = "nph-";
|
const char *nph_prefix = "nph-";
|
||||||
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
|
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) {
|
||||||
{
|
|
||||||
nph = true;
|
nph = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +90,7 @@ int main( int argc, const char *argv[] )
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
const char *query = getenv( "QUERY_STRING" );
|
const char *query = getenv( "QUERY_STRING" );
|
||||||
if ( query )
|
if ( query ) {
|
||||||
{
|
|
||||||
Debug( 1, "Query: %s", query );
|
Debug( 1, "Query: %s", query );
|
||||||
|
|
||||||
char temp_query[1024];
|
char temp_query[1024];
|
||||||
|
@ -106,84 +98,68 @@ int main( int argc, const char *argv[] )
|
||||||
char *q_ptr = temp_query;
|
char *q_ptr = temp_query;
|
||||||
char *parms[16]; // Shouldn't be more than this
|
char *parms[16]; // Shouldn't be more than this
|
||||||
int parm_no = 0;
|
int parm_no = 0;
|
||||||
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
|
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) {
|
||||||
{
|
|
||||||
parm_no++;
|
parm_no++;
|
||||||
q_ptr = NULL;
|
q_ptr = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int p = 0; p < parm_no; p++ )
|
for ( int p = 0; p < parm_no; p++ ) {
|
||||||
{
|
|
||||||
char *name = strtok( parms[p], "=" );
|
char *name = strtok( parms[p], "=" );
|
||||||
char *value = strtok( NULL, "=" );
|
char *value = strtok( NULL, "=" );
|
||||||
if ( !value )
|
if ( !value )
|
||||||
value = (char *)"";
|
value = (char *)"";
|
||||||
if ( !strcmp( name, "source" ) )
|
if ( !strcmp( name, "source" ) ) {
|
||||||
{
|
|
||||||
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
|
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
|
||||||
}
|
} else if ( !strcmp( name, "mode" ) ) {
|
||||||
else if ( !strcmp( name, "mode" ) )
|
|
||||||
{
|
|
||||||
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
||||||
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
||||||
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
||||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
||||||
}
|
} else if ( !strcmp( name, "format" ) ) {
|
||||||
else if ( !strcmp( name, "format" ) )
|
|
||||||
strncpy( format, value, sizeof(format) );
|
strncpy( format, value, sizeof(format) );
|
||||||
else if ( !strcmp( name, "monitor" ) )
|
} else if ( !strcmp( name, "monitor" ) ) {
|
||||||
monitor_id = atoi( value );
|
monitor_id = atoi( value );
|
||||||
else if ( !strcmp( name, "time" ) )
|
} else if ( !strcmp( name, "time" ) ) {
|
||||||
event_time = atoi( value );
|
event_time = atoi( value );
|
||||||
else if ( !strcmp( name, "event" ) )
|
} else if ( !strcmp( name, "event" ) ) {
|
||||||
event_id = strtoull( value, (char **)NULL, 10 );
|
event_id = strtoull( value, (char **)NULL, 10 );
|
||||||
else if ( !strcmp( name, "frame" ) )
|
} else if ( !strcmp( name, "frame" ) ) {
|
||||||
frame_id = strtoull( value, (char **)NULL, 10 );
|
frame_id = strtoull( value, (char **)NULL, 10 );
|
||||||
else if ( !strcmp( name, "scale" ) )
|
} else if ( !strcmp( name, "scale" ) ) {
|
||||||
scale = atoi( value );
|
scale = atoi( value );
|
||||||
else if ( !strcmp( name, "rate" ) )
|
} else if ( !strcmp( name, "rate" ) ) {
|
||||||
rate = atoi( value );
|
rate = atoi( value );
|
||||||
else if ( !strcmp( name, "maxfps" ) )
|
} else if ( !strcmp( name, "maxfps" ) ) {
|
||||||
maxfps = atof( value );
|
maxfps = atof( value );
|
||||||
else if ( !strcmp( name, "bitrate" ) )
|
} else if ( !strcmp( name, "bitrate" ) ) {
|
||||||
bitrate = atoi( value );
|
bitrate = atoi( value );
|
||||||
else if ( !strcmp( name, "ttl" ) )
|
} else if ( !strcmp( name, "ttl" ) ) {
|
||||||
ttl = atoi(value);
|
ttl = atoi(value);
|
||||||
else if ( !strcmp( name, "replay" ) )
|
} else if ( !strcmp( name, "replay" ) ) {
|
||||||
{
|
|
||||||
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
|
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
|
||||||
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
|
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
|
||||||
}
|
} else if ( !strcmp( name, "connkey" ) ) {
|
||||||
else if ( !strcmp( name, "connkey" ) )
|
|
||||||
connkey = atoi(value);
|
connkey = atoi(value);
|
||||||
else if ( !strcmp( name, "buffer" ) )
|
} else if ( !strcmp( name, "buffer" ) ) {
|
||||||
playback_buffer = atoi(value);
|
playback_buffer = atoi(value);
|
||||||
else if ( config.opt_use_auth )
|
} else if ( config.opt_use_auth ) {
|
||||||
{
|
if ( strcmp( config.auth_relay, "none" ) == 0 ) {
|
||||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
if ( !strcmp( name, "user" ) ) {
|
||||||
{
|
|
||||||
if ( !strcmp( name, "user" ) )
|
|
||||||
{
|
|
||||||
username = value;
|
username = value;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||||
{
|
{
|
||||||
if ( !strcmp( name, "auth" ) )
|
if ( !strcmp( name, "auth" ) ) {
|
||||||
{
|
|
||||||
strncpy( auth, value, sizeof(auth) );
|
strncpy( auth, value, sizeof(auth) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||||
{
|
{
|
||||||
if ( !strcmp( name, "user" ) )
|
if ( !strcmp( name, "user" ) ) {
|
||||||
{
|
|
||||||
username = UriDecode( value );
|
username = UriDecode( value );
|
||||||
}
|
}
|
||||||
if ( !strcmp( name, "pass" ) )
|
if ( !strcmp( name, "pass" ) ) {
|
||||||
{
|
|
||||||
password = UriDecode( value );
|
password = UriDecode( value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,36 +168,28 @@ int main( int argc, const char *argv[] )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( config.opt_use_auth )
|
if ( config.opt_use_auth ) {
|
||||||
{
|
|
||||||
User *user = 0;
|
User *user = 0;
|
||||||
|
|
||||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
if ( strcmp( config.auth_relay, "none" ) == 0 ) {
|
||||||
{
|
if ( username.length() ) {
|
||||||
if ( username.length() )
|
|
||||||
{
|
|
||||||
user = zmLoadUser( username.c_str() );
|
user = zmLoadUser( username.c_str() );
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||||
{
|
{
|
||||||
if ( *auth )
|
if ( *auth ) {
|
||||||
{
|
|
||||||
user = zmLoadAuthUser( auth, config.auth_hash_ips );
|
user = zmLoadAuthUser( auth, config.auth_hash_ips );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||||
{
|
{
|
||||||
if ( username.length() && password.length() )
|
if ( username.length() && password.length() ) {
|
||||||
{
|
|
||||||
user = zmLoadUser( username.c_str(), password.c_str() );
|
user = zmLoadUser( username.c_str(), password.c_str() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( !user )
|
if ( !user ) {
|
||||||
{
|
|
||||||
Error( "Unable to authenticate user" );
|
Error( "Unable to authenticate user" );
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
|
@ -231,8 +199,7 @@ int main( int argc, const char *argv[] )
|
||||||
}
|
}
|
||||||
|
|
||||||
setbuf( stdout, 0 );
|
setbuf( stdout, 0 );
|
||||||
if ( nph )
|
if ( nph ) {
|
||||||
{
|
|
||||||
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
|
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
|
||||||
}
|
}
|
||||||
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
|
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
|
||||||
|
@ -252,8 +219,7 @@ int main( int argc, const char *argv[] )
|
||||||
//fprintf( stdout, "Content-Length: 0\r\n");
|
//fprintf( stdout, "Content-Length: 0\r\n");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
if ( source == ZMS_MONITOR )
|
if ( source == ZMS_MONITOR ) {
|
||||||
{
|
|
||||||
MonitorStream stream;
|
MonitorStream stream;
|
||||||
stream.setStreamScale( scale );
|
stream.setStreamScale( scale );
|
||||||
stream.setStreamReplayRate( rate );
|
stream.setStreamReplayRate( rate );
|
||||||
|
@ -269,24 +235,15 @@ int main( int argc, const char *argv[] )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mode == ZMS_JPEG )
|
if ( mode == ZMS_JPEG ) {
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_JPEG );
|
stream.setStreamType( MonitorStream::STREAM_JPEG );
|
||||||
}
|
} else if ( mode == ZMS_RAW ) {
|
||||||
else if ( mode == ZMS_RAW )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_RAW );
|
stream.setStreamType( MonitorStream::STREAM_RAW );
|
||||||
}
|
} else if ( mode == ZMS_ZIP ) {
|
||||||
else if ( mode == ZMS_ZIP )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_ZIP );
|
stream.setStreamType( MonitorStream::STREAM_ZIP );
|
||||||
}
|
} else if ( mode == ZMS_SINGLE ) {
|
||||||
else if ( mode == ZMS_SINGLE )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_SINGLE );
|
stream.setStreamType( MonitorStream::STREAM_SINGLE );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
stream.setStreamFormat( format );
|
stream.setStreamFormat( format );
|
||||||
stream.setStreamBitrate( bitrate );
|
stream.setStreamBitrate( bitrate );
|
||||||
|
@ -300,29 +257,21 @@ int main( int argc, const char *argv[] )
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
}
|
}
|
||||||
stream.runStream();
|
stream.runStream();
|
||||||
}
|
} else if ( source == ZMS_EVENT ) {
|
||||||
else if ( source == ZMS_EVENT )
|
|
||||||
{
|
|
||||||
EventStream stream;
|
EventStream stream;
|
||||||
stream.setStreamScale( scale );
|
stream.setStreamScale( scale );
|
||||||
stream.setStreamReplayRate( rate );
|
stream.setStreamReplayRate( rate );
|
||||||
stream.setStreamMaxFPS( maxfps );
|
stream.setStreamMaxFPS( maxfps );
|
||||||
stream.setStreamMode( replay );
|
stream.setStreamMode( replay );
|
||||||
stream.setStreamQueue( connkey );
|
stream.setStreamQueue( connkey );
|
||||||
if ( monitor_id && event_time )
|
if ( monitor_id && event_time ) {
|
||||||
{
|
|
||||||
stream.setStreamStart( monitor_id, event_time );
|
stream.setStreamStart( monitor_id, event_time );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
stream.setStreamStart( event_id, frame_id );
|
stream.setStreamStart( event_id, frame_id );
|
||||||
}
|
}
|
||||||
if ( mode == ZMS_JPEG )
|
if ( mode == ZMS_JPEG ) {
|
||||||
{
|
|
||||||
stream.setStreamType( EventStream::STREAM_JPEG );
|
stream.setStreamType( EventStream::STREAM_JPEG );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
stream.setStreamFormat( format );
|
stream.setStreamFormat( format );
|
||||||
stream.setStreamBitrate( bitrate );
|
stream.setStreamBitrate( bitrate );
|
||||||
|
|
|
@ -259,23 +259,19 @@ fi
|
||||||
|
|
||||||
if [ $TYPE == "binary" ]; then
|
if [ $TYPE == "binary" ]; then
|
||||||
if [ "$INTERACTIVE" != "no" ]; then
|
if [ "$INTERACTIVE" != "no" ]; then
|
||||||
read -p "Not doing dput since it's a binary release. Do you want to install it? (Y/N)"
|
read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)"
|
||||||
if [[ $REPLY == [yY] ]]; then
|
if [[ $REPLY == [yY] ]]; then
|
||||||
sudo dpkg -i $DIRECTORY*.deb
|
sudo dpkg -i $DIRECTORY*.deb
|
||||||
else
|
|
||||||
echo $REPLY;
|
|
||||||
fi;
|
fi;
|
||||||
if [ "$DISTRO" == "jessie" ]; then
|
|
||||||
read -p "Do you want to upload this binary to zmrepo? (y/N)"
|
read -p "Do you want to upload this binary to zmrepo? (y/N)"
|
||||||
if [[ $REPLY == [yY] ]]; then
|
if [[ $REPLY == [yY] ]]; then
|
||||||
if [ "$RELEASE" != "" ]; then
|
if [ "$RELEASE" != "" ]; then
|
||||||
scp "zoneminder_${VERSION}-${DISTRO}*" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/"
|
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/"
|
||||||
else
|
else
|
||||||
if [ "$BRANCH" == "" ]; then
|
if [ "$BRANCH" == "" ]; then
|
||||||
scp "zoneminder_${VERSION}-${DISTRO}*" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"
|
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"
|
||||||
else
|
else
|
||||||
scp "$DIRECTORY-${DISTRO}*" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
|
scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/"
|
||||||
fi;
|
|
||||||
fi;
|
fi;
|
||||||
fi;
|
fi;
|
||||||
fi;
|
fi;
|
||||||
|
@ -295,12 +291,11 @@ else
|
||||||
|
|
||||||
dput="Y";
|
dput="Y";
|
||||||
if [ "$INTERACTIVE" != "no" ]; then
|
if [ "$INTERACTIVE" != "no" ]; then
|
||||||
echo "Ready to dput $SC to $PPA ? Y/N...";
|
read -p "Ready to dput $SC to $PPA ? Y/N...";
|
||||||
read dput
|
if [[ "$REPLY" == [yY] ]]; then
|
||||||
fi
|
|
||||||
if [ "$dput" == [Yy] ]; then
|
|
||||||
dput $PPA $SC
|
dput $PPA $SC
|
||||||
fi;
|
fi;
|
||||||
fi;
|
fi;
|
||||||
|
fi;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then
|
||||||
|
|
||||||
# Don't keep packages older than 5 days
|
# Don't keep packages older than 5 days
|
||||||
find ./zmrepo/$targetfolder/ -maxdepth 1 -type f -mtime +5 -delete
|
find ./zmrepo/$targetfolder/ -maxdepth 1 -type f -mtime +5 -delete
|
||||||
rsync -vzh --ignore-errors build/* zmrepo/$targetfolder/
|
rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/
|
||||||
fusermount -zu zmrepo
|
fusermount -zu zmrepo
|
||||||
else
|
else
|
||||||
echo
|
echo
|
||||||
|
|
|
@ -95,6 +95,7 @@ switch ( $_REQUEST['task'] )
|
||||||
foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) {
|
foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) {
|
||||||
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
|
$log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] );
|
||||||
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
|
$log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : '';
|
||||||
|
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message'] );
|
||||||
$logs[] = $log;
|
$logs[] = $log;
|
||||||
}
|
}
|
||||||
$options = array();
|
$options = array();
|
||||||
|
|
|
@ -101,49 +101,66 @@ CakeLog::config('debug', array(
|
||||||
'engine' => 'File',
|
'engine' => 'File',
|
||||||
'types' => array('notice', 'info', 'debug'),
|
'types' => array('notice', 'info', 'debug'),
|
||||||
'file' => 'cake_debug',
|
'file' => 'cake_debug',
|
||||||
|
'path' => '@ZM_LOGDIR@/'
|
||||||
));
|
));
|
||||||
CakeLog::config('error', array(
|
CakeLog::config('error', array(
|
||||||
'engine' => 'File',
|
'engine' => 'File',
|
||||||
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
|
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
|
||||||
'file' => 'cake_error',
|
'file' => 'cake_error',
|
||||||
|
'path' => '@ZM_LOGDIR@/'
|
||||||
));
|
));
|
||||||
CakeLog::config('custom_path', array(
|
CakeLog::config('custom_path', array(
|
||||||
'engine' => 'File',
|
'engine' => 'File',
|
||||||
'path' => '@ZM_LOGDIR@'
|
'path' => '@ZM_LOGDIR@/'
|
||||||
));
|
));
|
||||||
|
|
||||||
Configure::write('ZM_CONFIG', '@ZM_CONFIG@');
|
Configure::write('ZM_CONFIG', '@ZM_CONFIG@');
|
||||||
|
Configure::write('ZM_CONFIG_SUBDIR', '@ZM_CONFIG_SUBDIR@');
|
||||||
Configure::write('ZM_VERSION', '@VERSION@');
|
Configure::write('ZM_VERSION', '@VERSION@');
|
||||||
Configure::write('ZM_API_VERSION', '@API_VERSION@');
|
Configure::write('ZM_API_VERSION', '@API_VERSION@');
|
||||||
|
|
||||||
loadConfigFile();
|
# Process name, value pairs from the main config file first
|
||||||
|
$configvals = process_configfile(Configure::read('ZM_CONFIG'));
|
||||||
|
|
||||||
function loadConfigFile() {
|
# Search for user created config files. If one or more are found then
|
||||||
$configFile = Configure::read('ZM_CONFIG');
|
# update our config value array with those values
|
||||||
$localConfigFile = basename($configFile);
|
$configSubFolder = Configure::read('ZM_CONFIG_SUBDIR');
|
||||||
if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 )
|
if ( is_dir($configSubFolder) ) {
|
||||||
{
|
if ( is_readable($configSubFolder) ) {
|
||||||
if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
|
foreach ( glob("$configSubFolder/*.conf") as $filename ) {
|
||||||
print( "Warning, overriding installed $localConfigFile file with local copy\n" );
|
$configvals = array_replace($configvals, process_configfile($filename) );
|
||||||
else
|
}
|
||||||
error_log( "Warning, overriding installed $localConfigFile file with local copy" );
|
} else {
|
||||||
$configFile = $localConfigFile;
|
error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Now that our array our finalized, define each key => value
|
||||||
|
# pair in the array as a constant
|
||||||
|
foreach( $configvals as $key => $value) {
|
||||||
|
define( $key, $value );
|
||||||
|
Configure::write( $key, $value );
|
||||||
|
}
|
||||||
|
|
||||||
|
function process_configfile($configFile) {
|
||||||
|
if ( is_readable( $configFile ) ) {
|
||||||
|
$configvals = array();
|
||||||
|
|
||||||
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
|
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
|
||||||
while ( !feof($cfg) )
|
while ( !feof($cfg) ) {
|
||||||
{
|
|
||||||
$str = fgets( $cfg, 256 );
|
$str = fgets( $cfg, 256 );
|
||||||
if ( preg_match( '/^\s*$/', $str ))
|
if ( preg_match( '/^\s*$/', $str ))
|
||||||
continue;
|
continue;
|
||||||
elseif ( preg_match( '/^\s*#/', $str ))
|
elseif ( preg_match( '/^\s*#/', $str ))
|
||||||
continue;
|
continue;
|
||||||
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) {
|
elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
|
||||||
Configure::write( $matches[1], $matches[2] );
|
$configvals[$matches[1]] = $matches[2];
|
||||||
define( $matches[1], $matches[2] );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fclose( $cfg );
|
fclose( $cfg );
|
||||||
|
return( $configvals );
|
||||||
|
} else {
|
||||||
|
error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." );
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,9 @@ class DATABASE_CONFIG {
|
||||||
'login' => ZM_DB_USER,
|
'login' => ZM_DB_USER,
|
||||||
'password' => ZM_DB_PASS,
|
'password' => ZM_DB_PASS,
|
||||||
'database' => ZM_DB_NAME,
|
'database' => ZM_DB_NAME,
|
||||||
|
'ssl_ca' => ZM_DB_SSL_CA_CERT,
|
||||||
|
'ssl_key' => ZM_DB_SSL_CLIENT_KEY,
|
||||||
|
'ssl_cert' => ZM_DB_SSL_CLIENT_CERT,
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'encoding' => 'utf8',
|
'encoding' => 'utf8',
|
||||||
);
|
);
|
||||||
|
|
|
@ -176,9 +176,9 @@ if ( !empty($action) ) {
|
||||||
} else {
|
} else {
|
||||||
Error("No new Id despite new name");
|
Error("No new Id despite new name");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
$refreshParent = '/index.php?view=filter&Id='.$_REQUEST['Id'];
|
$refreshParent = '/index.php?view=filter&Id='.$_REQUEST['Id'];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // end if canedit events
|
} // end if canedit events
|
||||||
} // end if action == filter
|
} // end if action == filter
|
||||||
} // end if canview events
|
} // end if canview events
|
||||||
|
|
|
@ -42,7 +42,12 @@ function dbConnect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS );
|
$dbOptions = array(
|
||||||
|
PDO::MYSQL_ATTR_SSL_CA => ZM_DB_SSL_CA_CERT,
|
||||||
|
PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY,
|
||||||
|
PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT,
|
||||||
|
);
|
||||||
|
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions );
|
||||||
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||||
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
} catch(PDOException $ex ) {
|
} catch(PDOException $ex ) {
|
||||||
|
|
|
@ -2138,8 +2138,7 @@ function getSkinFile( $file ) {
|
||||||
return( $skinFile );
|
return( $skinFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSkinIncludes( $file, $includeBase=false, $asOverride=false )
|
function getSkinIncludes( $file, $includeBase=false, $asOverride=false ) {
|
||||||
{
|
|
||||||
global $skinBase;
|
global $skinBase;
|
||||||
$skinFile = false;
|
$skinFile = false;
|
||||||
foreach ( $skinBase as $skin ) {
|
foreach ( $skinBase as $skin ) {
|
||||||
|
|
|
@ -21,26 +21,23 @@
|
||||||
error_reporting( E_ALL );
|
error_reporting( E_ALL );
|
||||||
|
|
||||||
$debug = false;
|
$debug = false;
|
||||||
if ( $debug )
|
if ( $debug ) {
|
||||||
{
|
|
||||||
// Use these for debugging, though not both at once!
|
// Use these for debugging, though not both at once!
|
||||||
phpinfo( INFO_VARIABLES );
|
phpinfo( INFO_VARIABLES );
|
||||||
//error_reporting( E_ALL );
|
//error_reporting( E_ALL );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use new style autoglobals where possible
|
// Use new style autoglobals where possible
|
||||||
if ( version_compare( phpversion(), "4.1.0", "<") )
|
if ( version_compare( phpversion(), '4.1.0', '<') ) {
|
||||||
{
|
|
||||||
$_SESSION = &$HTTP_SESSION_VARS;
|
$_SESSION = &$HTTP_SESSION_VARS;
|
||||||
$_SERVER = &$HTTP_SERVER_VARS;
|
$_SERVER = &$HTTP_SERVER_VARS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Useful debugging lines for mobile devices
|
// Useful debugging lines for mobile devices
|
||||||
if ( false )
|
if ( false ) {
|
||||||
{
|
|
||||||
ob_start();
|
ob_start();
|
||||||
phpinfo( INFO_VARIABLES );
|
phpinfo( INFO_VARIABLES );
|
||||||
$fp = fopen( "/tmp/env.html", "w" );
|
$fp = fopen( '/tmp/env.html', 'w' );
|
||||||
fwrite( $fp, ob_get_contents() );
|
fwrite( $fp, ob_get_contents() );
|
||||||
fclose( $fp );
|
fclose( $fp );
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
|
@ -53,21 +50,18 @@ require_once( 'includes/Storage.php' );
|
||||||
require_once( 'includes/Event.php' );
|
require_once( 'includes/Event.php' );
|
||||||
require_once( 'includes/Monitor.php' );
|
require_once( 'includes/Monitor.php' );
|
||||||
|
|
||||||
if ( isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' )
|
if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) {
|
||||||
{
|
|
||||||
$protocol = 'https';
|
$protocol = 'https';
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$protocol = 'http';
|
$protocol = 'http';
|
||||||
}
|
}
|
||||||
define( "ZM_BASE_PROTOCOL", $protocol );
|
define( 'ZM_BASE_PROTOCOL', $protocol );
|
||||||
|
|
||||||
// Absolute URL's are unnecessary and break compatibility with reverse proxies
|
// Absolute URL's are unnecessary and break compatibility with reverse proxies
|
||||||
// define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] );
|
// define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] );
|
||||||
|
|
||||||
// Use relative URL's instead
|
// Use relative URL's instead
|
||||||
define( "ZM_BASE_URL", "" );
|
define( 'ZM_BASE_URL', '' );
|
||||||
|
|
||||||
// Check time zone is set
|
// Check time zone is set
|
||||||
if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) {
|
if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) {
|
||||||
|
@ -82,7 +76,7 @@ elseif ( isset($_COOKIE['zmSkin']) )
|
||||||
elseif ( defined('ZM_SKIN_DEFAULT') )
|
elseif ( defined('ZM_SKIN_DEFAULT') )
|
||||||
$skin = ZM_SKIN_DEFAULT;
|
$skin = ZM_SKIN_DEFAULT;
|
||||||
else
|
else
|
||||||
$skin = "classic";
|
$skin = 'classic';
|
||||||
|
|
||||||
$skins = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) );
|
$skins = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) );
|
||||||
if ( ! in_array( $skin, $skins ) ) {
|
if ( ! in_array( $skin, $skins ) ) {
|
||||||
|
@ -97,7 +91,7 @@ elseif ( isset($_COOKIE['zmCSS']) )
|
||||||
elseif (defined('ZM_CSS_DEFAULT'))
|
elseif (defined('ZM_CSS_DEFAULT'))
|
||||||
$css = ZM_CSS_DEFAULT;
|
$css = ZM_CSS_DEFAULT;
|
||||||
else
|
else
|
||||||
$css = "classic";
|
$css = 'classic';
|
||||||
|
|
||||||
$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) );
|
$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) );
|
||||||
if ( ! in_array( $css, $css_skins ) ) {
|
if ( ! in_array( $css, $css_skins ) ) {
|
||||||
|
@ -105,9 +99,9 @@ if ( ! in_array( $css, $css_skins ) ) {
|
||||||
$css = $css_skins[0];
|
$css = $css_skins[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
define( "ZM_BASE_PATH", dirname( $_SERVER['REQUEST_URI'] ) );
|
define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) );
|
||||||
define( "ZM_SKIN_NAME", $skin );
|
define( 'ZM_SKIN_NAME', $skin );
|
||||||
define( "ZM_SKIN_PATH", "skins/$skin" );
|
define( 'ZM_SKIN_PATH', "skins/$skin" );
|
||||||
|
|
||||||
$skinBase = array(); // To allow for inheritance of skins
|
$skinBase = array(); // To allow for inheritance of skins
|
||||||
if ( !file_exists( ZM_SKIN_PATH ) )
|
if ( !file_exists( ZM_SKIN_PATH ) )
|
||||||
|
@ -117,26 +111,25 @@ $skinBase[] = $skin;
|
||||||
$currentCookieParams = session_get_cookie_params();
|
$currentCookieParams = session_get_cookie_params();
|
||||||
Logger::Debug('Setting cookie parameters to lifetime('.$currentCookieParams['lifetime'].') path('.$currentCookieParams['path'].') domain ('.$currentCookieParams['domain'].') secure('.$currentCookieParams['secure'].') httpOnly(1)');
|
Logger::Debug('Setting cookie parameters to lifetime('.$currentCookieParams['lifetime'].') path('.$currentCookieParams['path'].') domain ('.$currentCookieParams['domain'].') secure('.$currentCookieParams['secure'].') httpOnly(1)');
|
||||||
session_set_cookie_params(
|
session_set_cookie_params(
|
||||||
$currentCookieParams["lifetime"],
|
$currentCookieParams['lifetime'],
|
||||||
$currentCookieParams["path"],
|
$currentCookieParams['path'],
|
||||||
$currentCookieParams["domain"],
|
$currentCookieParams['domain'],
|
||||||
$currentCookieParams["secure"],
|
$currentCookieParams['secure'],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
ini_set( "session.name", "ZMSESSID" );
|
ini_set( 'session.name', 'ZMSESSID' );
|
||||||
|
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin )
|
if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) {
|
||||||
{
|
|
||||||
$_SESSION['skin'] = $skin;
|
$_SESSION['skin'] = $skin;
|
||||||
setcookie( "zmSkin", $skin, time()+3600*24*30*12*10 );
|
setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) {
|
if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) {
|
||||||
$_SESSION['css'] = $css;
|
$_SESSION['css'] = $css;
|
||||||
setcookie( "zmCSS", $css, time()+3600*24*30*12*10 );
|
setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ZM_OPT_USE_AUTH )
|
if ( ZM_OPT_USE_AUTH )
|
||||||
|
@ -149,14 +142,12 @@ else
|
||||||
|
|
||||||
require_once( 'includes/lang.php' );
|
require_once( 'includes/lang.php' );
|
||||||
require_once( 'includes/functions.php' );
|
require_once( 'includes/functions.php' );
|
||||||
require_once( 'includes/csrf/csrf-magic.php' );
|
|
||||||
|
|
||||||
# Add Cross domain access headers
|
# Add Cross domain access headers
|
||||||
CORSHeaders();
|
CORSHeaders();
|
||||||
|
|
||||||
// Check for valid content dirs
|
// Check for valid content dirs
|
||||||
if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) )
|
if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) ) {
|
||||||
{
|
|
||||||
Error( "Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user");
|
Error( "Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +168,8 @@ isset($view) || $view = NULL;
|
||||||
isset($request) || $request = NULL;
|
isset($request) || $request = NULL;
|
||||||
isset($action) || $action = NULL;
|
isset($action) || $action = NULL;
|
||||||
|
|
||||||
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' ) {
|
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' ) {
|
||||||
|
require_once( 'includes/csrf/csrf-magic.php' );
|
||||||
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
||||||
csrf_check();
|
csrf_check();
|
||||||
}
|
}
|
||||||
|
@ -193,22 +185,16 @@ if ( ZM_OPT_USE_AUTH && ! isset($user) && $view != 'login' ) {
|
||||||
# Any file/page that uses the session must re-open it.
|
# Any file/page that uses the session must re-open it.
|
||||||
session_write_close();
|
session_write_close();
|
||||||
|
|
||||||
if ( isset( $_REQUEST['request'] ) )
|
if ( isset( $_REQUEST['request'] ) ) {
|
||||||
{
|
foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) {
|
||||||
foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile )
|
|
||||||
{
|
|
||||||
if ( !file_exists( $includeFile ) )
|
if ( !file_exists( $includeFile ) )
|
||||||
Fatal( "Request '$request' does not exist" );
|
Fatal( "Request '$request' does not exist" );
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) {
|
||||||
{
|
foreach ( $includeFiles as $includeFile ) {
|
||||||
if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) )
|
|
||||||
{
|
|
||||||
foreach ( $includeFiles as $includeFile )
|
|
||||||
{
|
|
||||||
if ( !file_exists( $includeFile ) )
|
if ( !file_exists( $includeFile ) )
|
||||||
Fatal( "View '$view' does not exist" );
|
Fatal( "View '$view' does not exist" );
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
|
@ -216,8 +202,7 @@ else
|
||||||
// If the view overrides $view to 'error', and the user is not logged in, then the
|
// If the view overrides $view to 'error', and the user is not logged in, then the
|
||||||
// issue is probably resolvable by logging in, so provide the opportunity to do so.
|
// issue is probably resolvable by logging in, so provide the opportunity to do so.
|
||||||
// The login view should handle redirecting to the correct location afterward.
|
// The login view should handle redirecting to the correct location afterward.
|
||||||
if ( $view == 'error' && !isset($user) )
|
if ( $view == 'error' && !isset($user) ) {
|
||||||
{
|
|
||||||
$view = 'login';
|
$view = 'login';
|
||||||
foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile )
|
foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile )
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
|
@ -225,8 +210,7 @@ else
|
||||||
}
|
}
|
||||||
// If the view is missing or the view still returned error with the user logged in,
|
// If the view is missing or the view still returned error with the user logged in,
|
||||||
// then it is not recoverable.
|
// then it is not recoverable.
|
||||||
if ( !$includeFiles || $view == 'error' )
|
if ( !$includeFiles || $view == 'error' ) {
|
||||||
{
|
|
||||||
foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile )
|
foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile )
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,8 +79,8 @@
|
||||||
border: 1px solid #006699;
|
border: 1px solid #006699;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
width: 96px;
|
width: 100px;
|
||||||
height: 96px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#monitors .imageFeed img {
|
#monitors .imageFeed img {
|
||||||
border: 2px solid #ffffff;
|
border: 2px solid #ffffff;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#monitors .imageFeed img.idle {
|
#monitors .imageFeed img.idle {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
.ptzControls input.ptzTextBtn {
|
.ptzControls input.ptzTextBtn {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
width: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel {
|
.ptzControls .controlsPanel {
|
||||||
|
@ -79,8 +78,8 @@
|
||||||
border: 1px solid #006699;
|
border: 1px solid #006699;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
width: 96px;
|
width: 100px;
|
||||||
height: 96px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#monitors .imageFeed img {
|
#monitors .imageFeed img {
|
||||||
border: 2px solid #999999;
|
border: 2px solid #999999;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#monitors .imageFeed img.idle {
|
#monitors .imageFeed img.idle {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
.ptzControls input.ptzTextBtn {
|
.ptzControls input.ptzTextBtn {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
width: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel {
|
.ptzControls .controlsPanel {
|
||||||
|
@ -79,8 +78,8 @@
|
||||||
border: 1px solid #006699;
|
border: 1px solid #006699;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
width: 96px;
|
width: 100px;
|
||||||
height: 96px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .arrowBtn {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#monitors .imageFeed img {
|
#monitors .imageFeed img {
|
||||||
border: 2px solid #ffffff;
|
border: 2px solid #ffffff;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#monitors .imageFeed img.idle {
|
#monitors .imageFeed img.idle {
|
||||||
|
|
|
@ -40,67 +40,54 @@ function checkSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
function newWindow( url, name, width, height )
|
function newWindow( url, name, width, height ) {
|
||||||
{
|
|
||||||
var windowId = window.open( url, name, popupOptions+",width="+width+",height="+height );
|
var windowId = window.open( url, name, popupOptions+",width="+width+",height="+height );
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPopupSize( tag, width, height )
|
function getPopupSize( tag, width, height ) {
|
||||||
{
|
|
||||||
var popupSize = Object.clone( popupSizes[tag] );
|
var popupSize = Object.clone( popupSizes[tag] );
|
||||||
if ( !popupSize )
|
if ( !popupSize ) {
|
||||||
{
|
|
||||||
Error( "Can't find window size for tag '"+tag+"'" );
|
Error( "Can't find window size for tag '"+tag+"'" );
|
||||||
return( { 'width': 0, 'height': 0 } );
|
return( { 'width': 0, 'height': 0 } );
|
||||||
}
|
}
|
||||||
if ( popupSize.width && popupSize.height )
|
if ( popupSize.width && popupSize.height ) {
|
||||||
{
|
|
||||||
if ( width || height )
|
if ( width || height )
|
||||||
Warning( "Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'" );
|
Warning( "Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'" );
|
||||||
return( popupSize );
|
return( popupSize );
|
||||||
}
|
}
|
||||||
if ( popupSize.addWidth )
|
if ( popupSize.addWidth ) {
|
||||||
{
|
|
||||||
popupSize.width = popupSize.addWidth;
|
popupSize.width = popupSize.addWidth;
|
||||||
if ( !width )
|
if ( !width )
|
||||||
Error( "Got addWidth but no passed width when getting popup size for tag '"+tag+"'" );
|
Error( "Got addWidth but no passed width when getting popup size for tag '"+tag+"'" );
|
||||||
else
|
else
|
||||||
popupSize.width += parseInt(width);
|
popupSize.width += parseInt(width);
|
||||||
}
|
} else if ( width ) {
|
||||||
else if ( width )
|
|
||||||
{
|
|
||||||
popupSize.width = width;
|
popupSize.width = width;
|
||||||
Error( "Got passed width but no addWidth when getting popup size for tag '"+tag+"'" );
|
Error( "Got passed width but no addWidth when getting popup size for tag '"+tag+"'" );
|
||||||
}
|
}
|
||||||
if ( popupSize.minWidth && popupSize.width < popupSize.minWidth )
|
if ( popupSize.minWidth && popupSize.width < popupSize.minWidth ) {
|
||||||
{
|
|
||||||
Warning( "Adjusting to minimum width when getting popup size for tag '"+tag+"'" );
|
Warning( "Adjusting to minimum width when getting popup size for tag '"+tag+"'" );
|
||||||
popupSize.width = popupSize.minWidth;
|
popupSize.width = popupSize.minWidth;
|
||||||
}
|
}
|
||||||
if ( popupSize.addHeight )
|
if ( popupSize.addHeight ) {
|
||||||
{
|
|
||||||
popupSize.height = popupSize.addHeight;
|
popupSize.height = popupSize.addHeight;
|
||||||
if ( !height )
|
if ( !height )
|
||||||
Error( "Got addHeight but no passed height when getting popup size for tag '"+tag+"'" );
|
Error( "Got addHeight but no passed height when getting popup size for tag '"+tag+"'" );
|
||||||
else
|
else
|
||||||
popupSize.height += parseInt(height);
|
popupSize.height += parseInt(height);
|
||||||
}
|
} else if ( height ) {
|
||||||
else if ( height )
|
|
||||||
{
|
|
||||||
popupSize.height = height;
|
popupSize.height = height;
|
||||||
Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" );
|
Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" );
|
||||||
}
|
}
|
||||||
if ( popupSize.minHeight && popupSize.height < popupSize.minHeight )
|
if ( popupSize.minHeight && ( popupSize.height < popupSize.minHeight ) ) {
|
||||||
{
|
Warning( "Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height );
|
||||||
Warning( "Adjusting to minimum height when getting popup size for tag '"+tag+"'" );
|
|
||||||
popupSize.height = popupSize.minHeight;
|
popupSize.height = popupSize.minHeight;
|
||||||
}
|
}
|
||||||
Debug( popupSize );
|
Debug( popupSize );
|
||||||
return( popupSize );
|
return( popupSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
function zmWindow()
|
function zmWindow() {
|
||||||
{
|
|
||||||
var zmWin = window.open( 'http://www.zoneminder.com', 'ZoneMinder' );
|
var zmWin = window.open( 'http://www.zoneminder.com', 'ZoneMinder' );
|
||||||
if ( ! zmWin ) {
|
if ( ! zmWin ) {
|
||||||
// if popup blocking is enabled, the popup won't be defined.
|
// if popup blocking is enabled, the popup won't be defined.
|
||||||
|
@ -110,8 +97,7 @@ function zmWindow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPopup( url, name, tag, width, height )
|
function createPopup( url, name, tag, width, height ) {
|
||||||
{
|
|
||||||
var popupSize = getPopupSize( tag, width, height );
|
var popupSize = getPopupSize( tag, width, height );
|
||||||
var popupDimensions = "";
|
var popupDimensions = "";
|
||||||
if ( popupSize.width > 0 )
|
if ( popupSize.width > 0 )
|
||||||
|
@ -127,8 +113,7 @@ function createPopup( url, name, tag, width, height )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEventPopup( eventId, eventFilter, width, height )
|
function createEventPopup( eventId, eventFilter, width, height ) {
|
||||||
{
|
|
||||||
var url = '?view=event&eid='+eventId;
|
var url = '?view=event&eid='+eventId;
|
||||||
if ( eventFilter )
|
if ( eventFilter )
|
||||||
url += eventFilter;
|
url += eventFilter;
|
||||||
|
@ -143,8 +128,7 @@ function createEventPopup( eventId, eventFilter, width, height )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFramesPopup( eventId, width, height )
|
function createFramesPopup( eventId, width, height ) {
|
||||||
{
|
|
||||||
var url = '?view=frames&eid='+eventId;
|
var url = '?view=frames&eid='+eventId;
|
||||||
var name = 'zmFrames';
|
var name = 'zmFrames';
|
||||||
var popupSize = getPopupSize( 'frames', width, height );
|
var popupSize = getPopupSize( 'frames', width, height );
|
||||||
|
@ -157,8 +141,7 @@ function createFramesPopup( eventId, width, height )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFramePopup( eventId, frameId, width, height )
|
function createFramePopup( eventId, frameId, width, height ) {
|
||||||
{
|
|
||||||
var url = '?view=frame&eid='+eventId+'&fid='+frameId;
|
var url = '?view=frame&eid='+eventId+'&fid='+frameId;
|
||||||
var name = 'zmFrame';
|
var name = 'zmFrame';
|
||||||
var popupSize = getPopupSize( 'frame', width, height );
|
var popupSize = getPopupSize( 'frame', width, height );
|
||||||
|
@ -171,50 +154,41 @@ function createFramePopup( eventId, frameId, width, height )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function windowToFront()
|
function windowToFront() {
|
||||||
{
|
|
||||||
top.window.focus();
|
top.window.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeWindow()
|
function closeWindow() {
|
||||||
{
|
|
||||||
top.window.close();
|
top.window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshWindow()
|
function refreshWindow() {
|
||||||
{
|
|
||||||
window.location.reload( true );
|
window.location.reload( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshParentWindow()
|
function refreshParentWindow() {
|
||||||
{
|
|
||||||
if ( window.opener )
|
if ( window.opener )
|
||||||
window.opener.location.reload( true );
|
window.opener.location.reload( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
//Shows a message if there is an error in the streamObj or the stream doesn't exist. Returns true if error, false otherwise.
|
//Shows a message if there is an error in the streamObj or the stream doesn't exist. Returns true if error, false otherwise.
|
||||||
function checkStreamForErrors( funcName, streamObj )
|
function checkStreamForErrors( funcName, streamObj ) {
|
||||||
{
|
if ( !streamObj ) {
|
||||||
if ( !streamObj )
|
|
||||||
{
|
|
||||||
Error( funcName+": stream object was null" );
|
Error( funcName+": stream object was null" );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ( streamObj.result == "Error" )
|
if ( streamObj.result == "Error" ) {
|
||||||
{
|
|
||||||
Error( funcName+" stream error: "+streamObj.message );
|
Error( funcName+" stream error: "+streamObj.message );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function secsToTime( seconds )
|
function secsToTime( seconds ) {
|
||||||
{
|
|
||||||
var timeString = "--";
|
var timeString = "--";
|
||||||
if ( seconds < 60 )
|
if ( seconds < 60 ) {
|
||||||
timeString = seconds.toString();
|
timeString = seconds.toString();
|
||||||
else if ( seconds < 60*60 )
|
} else if ( seconds < 60*60 ) {
|
||||||
{
|
|
||||||
var timeMins = parseInt(seconds/60);
|
var timeMins = parseInt(seconds/60);
|
||||||
var timeSecs = seconds%60;
|
var timeSecs = seconds%60;
|
||||||
if ( timeSecs < 10 )
|
if ( timeSecs < 10 )
|
||||||
|
@ -222,9 +196,7 @@ function secsToTime( seconds )
|
||||||
else
|
else
|
||||||
timeSecs = timeSecs.toString().substr( 0, 5 );
|
timeSecs = timeSecs.toString().substr( 0, 5 );
|
||||||
timeString = timeMins+":"+timeSecs;
|
timeString = timeMins+":"+timeSecs;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
var timeHours = parseInt(seconds/3600);
|
var timeHours = parseInt(seconds/3600);
|
||||||
var timeMins = (seconds%3600)/60;
|
var timeMins = (seconds%3600)/60;
|
||||||
var timeSecs = seconds%60;
|
var timeSecs = seconds%60;
|
||||||
|
@ -241,26 +213,20 @@ function secsToTime( seconds )
|
||||||
return( timeString );
|
return( timeString );
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitTab( tab )
|
function submitTab( tab ) {
|
||||||
{
|
|
||||||
var form = $('contentForm');
|
var form = $('contentForm');
|
||||||
form.action.value = "";
|
form.action.value = "";
|
||||||
form.tab.value = tab;
|
form.tab.value = tab;
|
||||||
form.submit();
|
form.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function configureDeleteButton( element )
|
function configureDeleteButton( element ) {
|
||||||
{
|
|
||||||
var form = element.form;
|
var form = element.form;
|
||||||
var checked = element.checked;
|
var checked = element.checked;
|
||||||
if ( !checked )
|
if ( !checked ) {
|
||||||
{
|
for ( var i = 0; i < form.elements.length; i++ ) {
|
||||||
for ( var i = 0; i < form.elements.length; i++ )
|
if ( form.elements[i].name == element.name ) {
|
||||||
{
|
if ( form.elements[i].checked ) {
|
||||||
if ( form.elements[i].name == element.name )
|
|
||||||
{
|
|
||||||
if ( form.elements[i].checked )
|
|
||||||
{
|
|
||||||
checked = true;
|
checked = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -270,18 +236,15 @@ function configureDeleteButton( element )
|
||||||
form.deleteBtn.disabled = !checked;
|
form.deleteBtn.disabled = !checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete( message )
|
function confirmDelete( message ) {
|
||||||
{
|
|
||||||
return( confirm( message?message:'Are you sure you wish to delete?' ) );
|
return( confirm( message?message:'Are you sure you wish to delete?' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( refreshParent )
|
if ( refreshParent ) {
|
||||||
{
|
|
||||||
refreshParentWindow();
|
refreshParentWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( focusWindow )
|
if ( focusWindow ) {
|
||||||
{
|
|
||||||
windowToFront();
|
windowToFront();
|
||||||
}
|
}
|
||||||
window.addEvent( 'domready', checkSize);
|
window.addEvent( 'domready', checkSize);
|
||||||
|
@ -320,4 +283,3 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim
|
||||||
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
|
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
|
||||||
video.appendChild(track);
|
video.appendChild(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,11 @@ xhtmlHeaders( __FILE__, translate('Console') );
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<h3 id="systemTime"><?php echo preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG ) ?></h3>
|
<h3 id="systemTime"><?php echo preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG ) ?></h3>
|
||||||
<h3 id="systemStats"><?php echo translate('Load') ?>: <?php echo getLoad() ?> - <?php echo translate('Disk') ?>: <?php echo getDiskPercent() ?>% - <?php echo ZM_PATH_MAP ?>: <?php echo getDiskPercent(ZM_PATH_MAP) ?>%</h3>
|
<h3 id="systemStats"><?php echo translate('Load') ?>: <?php echo getLoad() ?> - <?php echo translate('Disk') ?>: <?php echo getDiskPercent() ?>% - <?php echo ZM_PATH_MAP ?>: <?php echo getDiskPercent(ZM_PATH_MAP) ?>%</h3>
|
||||||
<h2 id="title"><a href="http://www.zoneminder.com" target="ZoneMinder">ZoneMinder</a> <?php echo translate('Console') ?> - <?php echo makePopupLink( '?view=state', 'zmState', 'state', $status, canEdit( 'System' ) ) ?> - <?php echo $run_state ?> <?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?></h2>
|
<h2 id="title">
|
||||||
|
<a href="http://www.zoneminder.com" target="ZoneMinder">ZoneMinder</a> <?php echo translate('Console') ?> -
|
||||||
|
<?php echo makePopupLink( '?view=state', 'zmState', 'state', $status, canEdit( 'System' ) ) ?> -
|
||||||
|
<?php echo $run_state ?> <?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?>
|
||||||
|
</h2>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
<?php if ( ZM_WEB_CONSOLE_BANNER ) { ?><h3 id="development"><?php echo ZM_WEB_CONSOLE_BANNER ?></h3><?php } ?>
|
<?php if ( ZM_WEB_CONSOLE_BANNER ) { ?><h3 id="development"><?php echo ZM_WEB_CONSOLE_BANNER ?></h3><?php } ?>
|
||||||
<div id="monitorSummary"><?php echo makePopupLink( '?view=groups', 'zmGroups', 'groups', sprintf( $CLANG['MonitorCount'], count($displayMonitors), zmVlang( $VLANG['Monitor'], count($displayMonitors) ) ).($group?' ('.$group['Name'].')':''), canView( 'Groups' ) ); ?></div>
|
<div id="monitorSummary"><?php echo makePopupLink( '?view=groups', 'zmGroups', 'groups', sprintf( $CLANG['MonitorCount'], count($displayMonitors), zmVlang( $VLANG['Monitor'], count($displayMonitors) ) ).($group?' ('.$group['Name'].')':''), canView( 'Groups' ) ); ?></div>
|
||||||
|
@ -248,27 +252,26 @@ foreach( $displayMonitors as $monitor ) {
|
||||||
$Server = new Server( $monitor['ServerId'] );
|
$Server = new Server( $monitor['ServerId'] );
|
||||||
echo $Server->Name();
|
echo $Server->Name();
|
||||||
?></td>
|
?></td>
|
||||||
<?php } ?>
|
|
||||||
<?php if ( $monitor['Type'] == "Local" ) { ?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$monitor['Device'].' ('.$monitor['Channel'].')</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php } elseif ( $monitor['Type'] == "Remote" ) { ?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*@/', '', $monitor['Host'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php } elseif ( $monitor['Type'] == "File" ) { ?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php } elseif ( $monitor['Type'] == "Ffmpeg" || $monitor['Type'] == "Libvlc" ) {
|
|
||||||
$domain = parse_url( $monitor['Path'], PHP_URL_HOST );
|
|
||||||
$shortpath = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
|
||||||
if ( $shortpath == '' ) {
|
|
||||||
$shortpath = 'Monitor ' . $monitor['Id'];
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$shortpath.'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php } elseif ( $monitor['Type'] == "cURL" ) { ?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php } else { ?>
|
|
||||||
<td class="colSource"> </td>
|
|
||||||
<?php } ?>
|
|
||||||
<?php
|
<?php
|
||||||
|
}
|
||||||
|
$source = '';
|
||||||
|
if ( $monitor['Type'] == 'Local' ) {
|
||||||
|
$source = $monitor['Device'].' ('.$monitor['Channel'].')';
|
||||||
|
} elseif ( $monitor['Type'] == 'Remote' ) {
|
||||||
|
$source = preg_replace( '/^.*@/', '', $monitor['Host'] );
|
||||||
|
} elseif ( $monitor['Type'] == 'File' || $monitor['Type'] == 'cURL' ) {
|
||||||
|
$source = preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||||
|
} elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' ) {
|
||||||
|
$domain = parse_url( $monitor['Path'], PHP_URL_HOST );
|
||||||
|
$source = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||||
|
} elseif ( $monitor['Type'] == 'cURL' ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
if ( $source == '' ) {
|
||||||
|
$source = 'Monitor ' . $monitor['Id'];
|
||||||
|
}
|
||||||
|
echo '<td class="colSource">'. makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$source.'</span>', canEdit( 'Monitors' ) ).'</td>';
|
||||||
|
|
||||||
for ( $i = 0; $i < count($eventCounts); $i++ ) {
|
for ( $i = 0; $i < count($eventCounts); $i++ ) {
|
||||||
?>
|
?>
|
||||||
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, $eventsView, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
|
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, $eventsView, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
|
||||||
|
|
|
@ -152,7 +152,7 @@ if ( $Event->DefaultVideo() ) {
|
||||||
?>
|
?>
|
||||||
<div id="videoFeed">
|
<div id="videoFeed">
|
||||||
<video id="videoobj" class="video-js vjs-default-skin" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "playbackRates": [0.5, 1, 1.5, 2, 4, 8, 16, 32, 64, 128, 256], "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
<video id="videoobj" class="video-js vjs-default-skin" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "playbackRates": [0.5, 1, 1.5, 2, 4, 8, 16, 32, 64, 128, 256], "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
||||||
<source src="<?php echo $Event->getStreamSrc( array( "mode=mpeg&format=h264" ) ); ?>" type="video/mp4">
|
<source src="<?php echo $Event->getStreamSrc( array( 'mode'=>'mpeg','format'=>'h264' ) ); ?>" type="video/mp4">
|
||||||
Your browser does not support the video tag.
|
Your browser does not support the video tag.
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -214,7 +214,7 @@ foreach ( $events as $event ) {
|
||||||
<td class="colThumbnail">
|
<td class="colThumbnail">
|
||||||
<?php
|
<?php
|
||||||
$imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&height='.$thumbData['Height'];
|
$imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&height='.$thumbData['Height'];
|
||||||
$streamSrc = getStreamSrc( array( "source=event", "mode=jpeg", "event=".$event->Id(), "scale=".$scale, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "replay=single") );
|
$streamSrc = $event->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single') );
|
||||||
|
|
||||||
$imgHtml = '<img id="thumbnail'.$event->id().'" src="'.$imgSrc.'" alt="'. validHtmlStr('Event '.$event->Id()) .'" style="width:'. validInt($thumbData['Width']) .'px;height:'. validInt( $thumbData['Height'] ).'px;" onmouseover="this.src=\''.$streamSrc.'\';" onmouseout="this.src=\''.$imgSrc.'\';"/>';
|
$imgHtml = '<img id="thumbnail'.$event->id().'" src="'.$imgSrc.'" alt="'. validHtmlStr('Event '.$event->Id()) .'" style="width:'. validInt($thumbData['Width']) .'px;height:'. validInt( $thumbData['Height'] ).'px;" onmouseover="this.src=\''.$streamSrc.'\';" onmouseout="this.src=\''.$imgSrc.'\';"/>';
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canView( 'Events' ) )
|
if ( !canView( 'Events' ) )
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
var logParms = "view=request&request=log&task=query";
|
var logParms = "view=request&request=log&task=query";
|
||||||
var logReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: logResponse } );
|
var logReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: logResponse } );
|
||||||
var logTimer = undefined;
|
var logTimer = undefined;
|
||||||
var logTable = undefined;
|
var logTable = undefined;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,720 @@
|
||||||
|
function evaluateLoadTimes() {
|
||||||
|
// Only consider it a completed event if we load ALL monitors, then zero all and start again
|
||||||
|
var start=0;
|
||||||
|
var end=0;
|
||||||
|
if ( liveMode != 1 && currentSpeed == 0 ) return; // don't evaluate when we are not moving as we can do nothing really fast.
|
||||||
|
for ( var i = 0; i < monitorIndex.length; i++ ) {
|
||||||
|
if ( monitorName[i] > "" ) {
|
||||||
|
if ( monitorLoadEndTimems[i] ==0 ) return; // if we have a monitor with no time yet just wait
|
||||||
|
if ( start == 0 || start > monitorLoadStartTimems[i] ) start = monitorLoadStartTimems[i];
|
||||||
|
if ( end == 0 || end < monitorLoadEndTimems[i] ) end = monitorLoadEndTimems[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( start == 0 || end == 0 ) return; // we really should not get here
|
||||||
|
for ( var i=0; i < numMonitors; i++ ) {
|
||||||
|
var monId = monitorPtr[i];
|
||||||
|
monitorLoadStartTimems[monId] = 0;
|
||||||
|
monitorLoadEndTimems[monId] = 0;
|
||||||
|
}
|
||||||
|
freeTimeLastIntervals[imageLoadTimesEvaluated++] = 1 - ((end - start)/currentDisplayInterval);
|
||||||
|
if( imageLoadTimesEvaluated < imageLoadTimesNeeded ) return;
|
||||||
|
var avgFrac=0;
|
||||||
|
for ( var i=0; i < imageLoadTimesEvaluated; i++ )
|
||||||
|
avgFrac += freeTimeLastIntervals[i];
|
||||||
|
avgFrac = avgFrac / imageLoadTimesEvaluated;
|
||||||
|
// The larger this is(positive) the faster we can go
|
||||||
|
if (avgFrac >= 0.9) currentDisplayInterval = (currentDisplayInterval * 0.50).toFixed(1); // we can go much faster
|
||||||
|
else if (avgFrac >= 0.8) currentDisplayInterval = (currentDisplayInterval * 0.55).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.7) currentDisplayInterval = (currentDisplayInterval * 0.60).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.6) currentDisplayInterval = (currentDisplayInterval * 0.65).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.5) currentDisplayInterval = (currentDisplayInterval * 0.70).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.4) currentDisplayInterval = (currentDisplayInterval * 0.80).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.35) currentDisplayInterval = (currentDisplayInterval * 0.90).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.3) currentDisplayInterval = (currentDisplayInterval * 1.00).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.25) currentDisplayInterval = (currentDisplayInterval * 1.20).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.2) currentDisplayInterval = (currentDisplayInterval * 1.50).toFixed(1);
|
||||||
|
else if (avgFrac >= 0.1) currentDisplayInterval = (currentDisplayInterval * 2.00).toFixed(1);
|
||||||
|
else currentDisplayInterval = (currentDisplayInterval * 2.50).toFixed(1);
|
||||||
|
currentDisplayInterval=Math.min(Math.max(currentDisplayInterval, 30),10000); // limit this from about 30fps to .1 fps
|
||||||
|
imageLoadTimesEvaluated=0;
|
||||||
|
setSpeed(speedIndex);
|
||||||
|
$('fps').innerHTML="Display refresh rate is " + (1000 / currentDisplayInterval).toFixed(1) + " per second, avgFrac=" + avgFrac.toFixed(3) + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetImageSource( monId, val ) {
|
||||||
|
if ( liveMode == 1 ) {
|
||||||
|
return monitorImageObject[monId].src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for ( var i=0, eIdlength = eId.length; i < eIdlength; i++ ) {
|
||||||
|
// Search for a match
|
||||||
|
if ( eMonId[i] == monId && val >= eStartSecs[i] && val <= eEndSecs[i] ) {
|
||||||
|
var frame = parseInt((val - eStartSecs[i])/(eEndSecs[i]-eStartSecs[i])*eventFrames[i])+1;
|
||||||
|
return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
||||||
|
}
|
||||||
|
} // end for
|
||||||
|
return "no data";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback when loading an image. Will load itself to the canvas, or draw no data
|
||||||
|
function imagedone( obj, monId, success ) {
|
||||||
|
if ( success ) {
|
||||||
|
var canvasCtx = monitorCanvasCtx[monId];
|
||||||
|
var canvasObj = monitorCanvasObj[monId];
|
||||||
|
|
||||||
|
canvasCtx.drawImage( monitorImageObject[monId], 0, 0, canvasObj.width, canvasObj.height );
|
||||||
|
var iconSize=(Math.max(canvasObj.width, canvasObj.height) * 0.10);
|
||||||
|
canvasCtx.font = "600 " + iconSize.toString() + "px Arial";
|
||||||
|
canvasCtx.fillStyle = "white";
|
||||||
|
canvasCtx.globalCompositeOperation = "difference";
|
||||||
|
canvasCtx.fillText( "+", iconSize*0.2, iconSize*1.2 );
|
||||||
|
canvasCtx.fillText( "-", canvasObj.width - iconSize*1.2, iconSize*1.2 );
|
||||||
|
canvasCtx.globalCompositeOperation = "source-over";
|
||||||
|
monitorLoadEndTimems[monId] = new Date().getTime(); // elapsed time to load
|
||||||
|
evaluateLoadTimes();
|
||||||
|
}
|
||||||
|
monitorLoading[monId] = false;
|
||||||
|
if ( ! success ) {
|
||||||
|
// if we had a failrue queue up the no-data image
|
||||||
|
//loadImage2Monitor(monId,"no data"); // leave the staged URL if there is one, just ignore it here.
|
||||||
|
loadNoData( monId );
|
||||||
|
} else {
|
||||||
|
if ( monitorLoadingStageURL[monId] == "" ) {
|
||||||
|
console.log("Not showing image for " + monId );
|
||||||
|
// This means that there wasn't a loading image placeholder.
|
||||||
|
// So we weren't actually loading an image... which seems weird.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//loadImage2Monitor(monId,monitorLoadingStageURL[monId] );
|
||||||
|
//monitorLoadingStageURL[monId]="";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadNoData( monId ) {
|
||||||
|
if ( monId ) {
|
||||||
|
var canvasCtx = monitorCanvasCtx[monId];
|
||||||
|
var canvasObj = monitorCanvasObj[monId];
|
||||||
|
canvasCtx.fillStyle="white";
|
||||||
|
canvasCtx.fillRect(0, 0, canvasObj.width, canvasObj.height);
|
||||||
|
var textSize=canvasObj.width * 0.15;
|
||||||
|
var text="No Data";
|
||||||
|
canvasCtx.font = "600 " + textSize.toString() + "px Arial";
|
||||||
|
canvasCtx.fillStyle="black";
|
||||||
|
var textWidth = canvasCtx.measureText(text).width;
|
||||||
|
canvasCtx.fillText(text,canvasObj.width/2 - textWidth/2,canvasObj.height/2);
|
||||||
|
} else {
|
||||||
|
console.log("No monId in loadNoData");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either draws the
|
||||||
|
function loadImage2Monitor( monId, url ) {
|
||||||
|
if ( monitorLoading[monId] && monitorImageObject[monId].src != url ) {
|
||||||
|
// never queue the same image twice (if it's loading it has to be defined, right?
|
||||||
|
monitorLoadingStageURL[monId] = url; // we don't care if we are overriting, it means it didn't change fast enough
|
||||||
|
} else {
|
||||||
|
if ( monitorImageObject[monId].src == url ) return; // do nothing if it's the same
|
||||||
|
if ( url == 'no data' ) {
|
||||||
|
loadNoData( monId );
|
||||||
|
} else {
|
||||||
|
monitorLoading[monId] = true;
|
||||||
|
monitorLoadStartTimems[monId] = new Date().getTime();
|
||||||
|
monitorImageObject[monId].src = url; // starts a load but doesn't refresh yet, wait until ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function timerFire() {
|
||||||
|
// See if we need to reschedule
|
||||||
|
if(currentDisplayInterval != timerInterval || currentSpeed == 0) {
|
||||||
|
// zero just turn off interrupts
|
||||||
|
clearInterval(timerObj);
|
||||||
|
timerInterval=currentDisplayInterval;
|
||||||
|
if(currentSpeed>0 || liveMode!=0) timerObj=setInterval(timerFire,timerInterval); // don't fire out of live mode if speed is zero
|
||||||
|
}
|
||||||
|
|
||||||
|
if (liveMode) outputUpdate(currentTimeSecs); // In live mode we basically do nothing but redisplay
|
||||||
|
else if (currentTimeSecs + playSecsperInterval >= maxTimeSecs) // beyond the end just stop
|
||||||
|
{
|
||||||
|
setSpeed(0);
|
||||||
|
outputUpdate(currentTimeSecs);
|
||||||
|
}
|
||||||
|
else outputUpdate(currentTimeSecs + playSecsperInterval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSliderOnGraph(val) {
|
||||||
|
var sliderWidth=10;
|
||||||
|
var sliderLineWidth=1;
|
||||||
|
var sliderHeight=cHeight;
|
||||||
|
|
||||||
|
if(liveMode==1) {
|
||||||
|
val=Math.floor( Date.now() / 1000);
|
||||||
|
}
|
||||||
|
// Set some sizes
|
||||||
|
|
||||||
|
var labelpx = Math.max( 6, Math.min( 20, parseInt(cHeight * timeLabelsFractOfRow / (numMonitors+1)) ) );
|
||||||
|
var labbottom=parseInt(cHeight * 0.2 / (numMonitors+1)).toString() + "px"; // This is positioning same as row labels below, but from bottom so 1-position
|
||||||
|
var labfont=labelpx + "px Georgia"; // set this like below row labels
|
||||||
|
|
||||||
|
if(numMonitors>0) {
|
||||||
|
// if we have no data to display don't do the slider itself
|
||||||
|
var sliderX=parseInt( (val - minTimeSecs) / rangeTimeSecs * cWidth - sliderWidth/2); // position left side of slider
|
||||||
|
if(sliderX < 0) sliderX=0;
|
||||||
|
if(sliderX+sliderWidth > cWidth) sliderX=cWidth-sliderWidth-1;
|
||||||
|
|
||||||
|
// If we have data already saved first restore it from LAST time
|
||||||
|
|
||||||
|
if(typeof underSlider !== 'undefined')
|
||||||
|
{
|
||||||
|
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
|
||||||
|
underSlider=undefined;
|
||||||
|
}
|
||||||
|
if(liveMode==0) // we get rid of the slider if we switch to live (since it may not be in the "right" place)
|
||||||
|
{
|
||||||
|
// Now save where we are putting it THIS time
|
||||||
|
underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
|
||||||
|
// And add in the slider'
|
||||||
|
ctx.lineWidth=sliderLineWidth;
|
||||||
|
ctx.strokeStyle='black';
|
||||||
|
// looks like strokes are on the outside (or could be) so shrink it by the line width so we replace all the pixels
|
||||||
|
ctx.strokeRect(sliderX+sliderLineWidth,sliderLineWidth,sliderWidth - 2*sliderLineWidth, sliderHeight - 2*sliderLineWidth);
|
||||||
|
underSliderX=sliderX;
|
||||||
|
}
|
||||||
|
var o = $('scruboutput');
|
||||||
|
if(liveMode==1)
|
||||||
|
{
|
||||||
|
o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
|
||||||
|
o.style.color="red";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o.innerHTML=secs2dbstr(val);
|
||||||
|
o.style.color="blue";
|
||||||
|
}
|
||||||
|
o.style.position="absolute";
|
||||||
|
o.style.bottom=labbottom;
|
||||||
|
o.style.font=labfont;
|
||||||
|
// try to get length and then when we get too close to the right switch to the left
|
||||||
|
var len = o.offsetWidth;
|
||||||
|
var x;
|
||||||
|
if(sliderX > cWidth/2)
|
||||||
|
x=sliderX - len - 10;
|
||||||
|
else
|
||||||
|
x=sliderX + 10;
|
||||||
|
o.style.left=x.toString() + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
// This displays (or not) the left/right limits depending on how close the slider is.
|
||||||
|
// Because these change widths if the slider is too close, use the slider width as an estimate for the left/right label length (i.e. don't recalculate len from above)
|
||||||
|
// If this starts to collide increase some of the extra space
|
||||||
|
|
||||||
|
var o = $('scrubleft');
|
||||||
|
o.innerHTML=secs2dbstr(minTimeSecs);
|
||||||
|
o.style.position="absolute";
|
||||||
|
o.style.bottom=labbottom;
|
||||||
|
o.style.font=labfont;
|
||||||
|
o.style.left="5px";
|
||||||
|
if(numMonitors==0) // we need a len calculation if we skipped the slider
|
||||||
|
len = o.offsetWidth;
|
||||||
|
// If the slider will overlay part of this suppress (this is the left side)
|
||||||
|
if(len + 10 > sliderX || cWidth < len * 4 ) // that last check is for very narrow browsers
|
||||||
|
o.style.display="none";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o.style.display="inline";
|
||||||
|
o.style.display="inline-flex"; // safari won't take this but will just ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
var o = $('scrubright');
|
||||||
|
o.innerHTML=secs2dbstr(maxTimeSecs);
|
||||||
|
o.style.position="absolute";
|
||||||
|
o.style.bottom=labbottom;
|
||||||
|
o.style.font=labfont;
|
||||||
|
// If the slider will overlay part of this suppress (this is the right side)
|
||||||
|
o.style.left=(cWidth - len - 15).toString() + "px";
|
||||||
|
if(sliderX > cWidth - len - 20 || cWidth < len * 4 )
|
||||||
|
o.style.display="none";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o.style.display="inline";
|
||||||
|
o.style.display="inline-flex";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGraph()
|
||||||
|
{
|
||||||
|
var divWidth=$('timelinediv').clientWidth
|
||||||
|
canvas.width = cWidth = divWidth; // Let it float and determine width (it should be sized a bit smaller percentage of window)
|
||||||
|
canvas.height=cHeight = parseInt(window.innerHeight * 0.10);
|
||||||
|
if(eId.length==0)
|
||||||
|
{
|
||||||
|
ctx.font="40px Georgia";
|
||||||
|
ctx.fillStyle="Black";
|
||||||
|
ctx.globalAlpha=1;
|
||||||
|
var t="No data found in range - choose differently";
|
||||||
|
var l=ctx.measureText(t).width;
|
||||||
|
ctx.fillText(t,(cWidth - l)/2, cHeight-10);
|
||||||
|
underSlider=undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var rowHeight=parseInt(cHeight / (numMonitors + 1) ); // Leave room for a scale of some sort
|
||||||
|
|
||||||
|
// first fill in the bars for the events (not alarms)
|
||||||
|
|
||||||
|
for(var i=0; i<eId.length; i++) // Display all we loaded
|
||||||
|
{
|
||||||
|
var x1=parseInt( (eStartSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
||||||
|
var x2=parseInt( (eEndSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
|
||||||
|
ctx.fillStyle=monitorColour[eMonId[i]];
|
||||||
|
ctx.globalAlpha = 0.2; // light color for background
|
||||||
|
ctx.clearRect(x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
|
||||||
|
ctx.fillRect (x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight);
|
||||||
|
}
|
||||||
|
for(var i=0; (i<fScore.length) && (maxScore>0); i++) // Now put in scored frames (if any)
|
||||||
|
{
|
||||||
|
var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
||||||
|
var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
|
||||||
|
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
|
||||||
|
ctx.fillStyle=monitorColour[fMonId[i]];
|
||||||
|
ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background
|
||||||
|
ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight);
|
||||||
|
}
|
||||||
|
for(var i=0; i<numMonitors; i++) // Note that this may be a sparse array
|
||||||
|
{
|
||||||
|
ctx.font= parseInt(rowHeight * timeLabelsFractOfRow).toString() + "px Georgia";
|
||||||
|
ctx.fillStyle="Black";
|
||||||
|
ctx.globalAlpha=1;
|
||||||
|
ctx.fillText(monitorName[monitorPtr[i]], 0, (i + 1 - (1 - timeLabelsFractOfRow)/2 ) * rowHeight ); // This should roughly center font in row
|
||||||
|
}
|
||||||
|
underSlider=undefined; // flag we don't have a slider cached
|
||||||
|
drawSliderOnGraph(currentTimeSecs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function redrawScreen()
|
||||||
|
{
|
||||||
|
if(fitMode==0) // if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float
|
||||||
|
{
|
||||||
|
for(var i=0; i<numMonitors; i++)
|
||||||
|
monitorCanvasObj[monitorPtr[i]].style.position="";
|
||||||
|
$('monitors').setStyle('height',"auto");
|
||||||
|
}
|
||||||
|
if(liveMode==1) // if we are not in live view switch to history -- this has to come before fit in case we re-establish the timeline
|
||||||
|
{
|
||||||
|
$('SpeedDiv').style.display="none";
|
||||||
|
$('timelinediv').style.display="none";
|
||||||
|
$('live').innerHTML="History";
|
||||||
|
$('zoomin').style.display="none";
|
||||||
|
$('zoomout').style.display="none";
|
||||||
|
$('panleft').style.display="none";
|
||||||
|
$('panright').style.display="none";
|
||||||
|
|
||||||
|
}
|
||||||
|
else // switch out of liveview mode
|
||||||
|
{
|
||||||
|
$('SpeedDiv').style.display="inline";
|
||||||
|
$('SpeedDiv').style.display="inline-flex";
|
||||||
|
$('timelinediv').style.display=null;
|
||||||
|
$('live').innerHTML="Live";
|
||||||
|
$('zoomin').style.display="inline";
|
||||||
|
$('zoomin').style.display="inline-flex";
|
||||||
|
$('zoomout').style.display="inline";
|
||||||
|
$('zoomout').style.display="inline-flex";
|
||||||
|
$('panleft').style.display="inline";
|
||||||
|
$('panleft').style.display="inline-flex";
|
||||||
|
$('panright').style.display="inline";
|
||||||
|
$('panright').style.display="inline-flex";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fitMode==1)
|
||||||
|
{
|
||||||
|
$('ScaleDiv').style.display="none";
|
||||||
|
$('fit').innerHTML="Scale";
|
||||||
|
var vh=window.innerHeight;
|
||||||
|
var vw=window.innerWidth;
|
||||||
|
var pos=$('monitors').getPosition();
|
||||||
|
var mh=(vh - pos.y - $('fps').getSize().y);
|
||||||
|
$('monitors').setStyle('height',mh.toString() + "px"); // leave a small gap at bottom
|
||||||
|
if(maxfit2($('monitors').getSize().x,$('monitors').getSize().y) == 0) /// if we fail to fix we back out of fit mode -- ??? This may need some better handling
|
||||||
|
fitMode=1-fitMode;
|
||||||
|
}
|
||||||
|
else // switch out of fit mode
|
||||||
|
{
|
||||||
|
$('ScaleDiv').style.display="inline";
|
||||||
|
$('ScaleDiv').style.display="inline-flex";
|
||||||
|
$('fit').innerHTML="Fit";
|
||||||
|
setScale(currentScale);
|
||||||
|
}
|
||||||
|
drawGraph();
|
||||||
|
outputUpdate(currentTimeSecs);
|
||||||
|
timerFire(); // force a fire in case it's not timing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function outputUpdate(val)
|
||||||
|
{
|
||||||
|
drawSliderOnGraph(val);
|
||||||
|
for(var i=0; i<numMonitors; i++)
|
||||||
|
{
|
||||||
|
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val));
|
||||||
|
}
|
||||||
|
var currentTimeMS = new Date(val*1000);
|
||||||
|
currentTimeSecs=val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Found this here: http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element
|
||||||
|
function relMouseCoords(event){
|
||||||
|
var totalOffsetX = 0;
|
||||||
|
var totalOffsetY = 0;
|
||||||
|
var canvasX = 0;
|
||||||
|
var canvasY = 0;
|
||||||
|
var currentElement = this;
|
||||||
|
|
||||||
|
do{
|
||||||
|
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
|
||||||
|
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
|
||||||
|
}
|
||||||
|
while(currentElement = currentElement.offsetParent)
|
||||||
|
|
||||||
|
canvasX = event.pageX - totalOffsetX;
|
||||||
|
canvasY = event.pageY - totalOffsetY;
|
||||||
|
|
||||||
|
return {x:canvasX, y:canvasY}
|
||||||
|
}
|
||||||
|
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
|
||||||
|
|
||||||
|
// These are the functions for mouse movement in the timeline. Note that touch is treated as a mouse move with mouse down
|
||||||
|
|
||||||
|
var mouseisdown=false;
|
||||||
|
function mdown(event) {mouseisdown=true; mmove(event);}
|
||||||
|
function mup(event) {mouseisdown=false;}
|
||||||
|
function mout(event) {mouseisdown=false;} // if we go outside treat it as release
|
||||||
|
function tmove(event) {mouseisdown=true; mmove(event);}
|
||||||
|
|
||||||
|
function mmove(event) {
|
||||||
|
if(mouseisdown) {
|
||||||
|
// only do anything if the mouse is depressed while on the sheet
|
||||||
|
var sec = minTimeSecs + rangeTimeSecs / event.target.width * event.target.relMouseCoords(event).x;
|
||||||
|
outputUpdate(sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function secs2dbstr (s)
|
||||||
|
{
|
||||||
|
var st = (new Date(s * 1000)).format("%Y-%m-%d %H:%M:%S");
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFit(value)
|
||||||
|
{
|
||||||
|
fitMode=value;
|
||||||
|
redrawScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showScale(newscale) // updates slider only
|
||||||
|
{
|
||||||
|
$('scaleslideroutput').innerHTML = parseFloat(newscale).toFixed(2).toString() + " x";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScale(newscale) // makes actual change
|
||||||
|
{
|
||||||
|
showScale(newscale);
|
||||||
|
for(var i=0; i<numMonitors; i++)
|
||||||
|
{
|
||||||
|
monitorCanvasObj[monitorPtr[i]].width=monitorWidth[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
|
||||||
|
monitorCanvasObj[monitorPtr[i]].height=monitorHeight[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
|
||||||
|
}
|
||||||
|
currentScale=newscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSpeed(val) // updates slider only
|
||||||
|
{
|
||||||
|
$('speedslideroutput').innerHTML = parseFloat(speeds[val]).toFixed(2).toString() + " x";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpeed(val) // Note parameter is the index not the speed
|
||||||
|
{
|
||||||
|
var t;
|
||||||
|
if(liveMode==1) return; // we shouldn't actually get here but just in case
|
||||||
|
currentSpeed=parseFloat(speeds[val]);
|
||||||
|
speedIndex=val;
|
||||||
|
playSecsperInterval = currentSpeed * currentDisplayInterval / 1000;
|
||||||
|
showSpeed(val);
|
||||||
|
if( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLive(value)
|
||||||
|
{
|
||||||
|
liveMode=value;
|
||||||
|
redrawScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||||
|
// The section below are to reload this program with new parameters
|
||||||
|
|
||||||
|
function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we can
|
||||||
|
var now = new Date() / 1000;
|
||||||
|
var minStr="";
|
||||||
|
var maxStr="";
|
||||||
|
var currentStr="";
|
||||||
|
if ( minSecs > 0 ) {
|
||||||
|
if(maxSecs > now)
|
||||||
|
maxSecs = parseInt(now);
|
||||||
|
maxStr="&maxTime=" + secs2dbstr(maxSecs);
|
||||||
|
}
|
||||||
|
if ( maxSecs > 0 )
|
||||||
|
minStr="&minTime=" + secs2dbstr(minSecs);
|
||||||
|
if ( maxSecs == 0 && minSecs == 0 ) {
|
||||||
|
minStr="&minTime=01/01/1950 12:00:00";
|
||||||
|
maxStr="&maxTime=12/31/2035 12:00:00";
|
||||||
|
}
|
||||||
|
var intervalStr="&displayinterval=" + currentDisplayInterval.toString();
|
||||||
|
if ( minSecs && maxSecs ) {
|
||||||
|
if ( currentTimeSecs > minSecs && currentTimeSecs < maxSecs ) // make sure time is in the new range
|
||||||
|
currentStr="¤t=" + secs2dbstr(currentTimeSecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
var liveStr="&live=0";
|
||||||
|
if ( live == 1 )
|
||||||
|
liveStr="&live=1";
|
||||||
|
|
||||||
|
var fitStr="&fit=0";
|
||||||
|
if ( fitMode == 1 )
|
||||||
|
fitStr="&fit=1";
|
||||||
|
|
||||||
|
var zoomStr="";
|
||||||
|
for ( var i=0; i < numMonitors; i++ )
|
||||||
|
if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1
|
||||||
|
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
|
||||||
|
|
||||||
|
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + document.getElementById("scaleslider").value + "&speed=" + speeds[$j("#speedslider").value];
|
||||||
|
window.location = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lastHour() {
|
||||||
|
var now = new Date() / 1000;
|
||||||
|
clicknav(now - 3600 + 1, now,1,0);
|
||||||
|
}
|
||||||
|
function lastEight() {
|
||||||
|
var now = new Date() / 1000;
|
||||||
|
clicknav(now - 3600*8 + 1, now,1,0);
|
||||||
|
}
|
||||||
|
function zoomin() {
|
||||||
|
rangeTimeSecs = parseInt(rangeTimeSecs / 2);
|
||||||
|
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
|
||||||
|
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
|
||||||
|
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomout() {
|
||||||
|
rangeTimeSecs = parseInt(rangeTimeSecs * 2);
|
||||||
|
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
|
||||||
|
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
|
||||||
|
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
||||||
|
}
|
||||||
|
function panleft() {
|
||||||
|
minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2);
|
||||||
|
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
|
||||||
|
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
||||||
|
}
|
||||||
|
function panright() {
|
||||||
|
minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2);
|
||||||
|
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
|
||||||
|
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
||||||
|
}
|
||||||
|
function allof() {
|
||||||
|
clicknav(0,0,1,0);
|
||||||
|
}
|
||||||
|
function allnon() {
|
||||||
|
clicknav(0,0,0,0);
|
||||||
|
}
|
||||||
|
/// >>>>>>>>>>>>>>>>> handles packing different size/aspect monitors on screen <<<<<<<<<<<<<<<<<<<<<<<<
|
||||||
|
|
||||||
|
function compSize(a, b) { // sort array by some size parameter - height seems to work best. A semi-greedy algorithm
|
||||||
|
var a_value = monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a];
|
||||||
|
var b_value = monitorHeight[b] * monitorWidth[b] * monitorNormalizeScale[b] * monitorZoomScale[b] * monitorNormalizeScale[b] * monitorZoomScale[b];
|
||||||
|
|
||||||
|
if ( a_value > b_value ) return -1;
|
||||||
|
else if ( a_value == b_value ) return 0;
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function maxfit2(divW, divH) {
|
||||||
|
var bestFitX=[]; // how we arranged the so-far best match
|
||||||
|
var bestFitX2=[];
|
||||||
|
var bestFitY=[];
|
||||||
|
var bestFitY2=[];
|
||||||
|
var bestFitScale;
|
||||||
|
|
||||||
|
var minScale=0.05;
|
||||||
|
var maxScale=5.00;
|
||||||
|
var bestFitArea=0;
|
||||||
|
|
||||||
|
var borders=-1;
|
||||||
|
|
||||||
|
monitorPtr.sort(compSize);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if( maxScale - minScale < 0.01 ) break;
|
||||||
|
var thisScale = (maxScale + minScale) / 2;
|
||||||
|
var allFit=1;
|
||||||
|
var thisArea=0;
|
||||||
|
var thisX=[]; // top left
|
||||||
|
var thisY=[];
|
||||||
|
var thisX2=[]; // bottom right
|
||||||
|
var thisY2=[];
|
||||||
|
|
||||||
|
for ( var m = 0; m < numMonitors; m++ ) {
|
||||||
|
// this loop places each monitor (if it can)
|
||||||
|
var monId = monitorPtr[m];
|
||||||
|
|
||||||
|
function doesItFit(x,y,w,h,d) { // does block (w,h) fit at position (x,y) relative to edge and other nodes already done (0..d)
|
||||||
|
if(x+w>=divW) return 0;
|
||||||
|
if(y+h>=divH) return 0;
|
||||||
|
for(var i=0; i<=d; i++)
|
||||||
|
if( !( thisX[i]>x+w-1 || thisX2[i] < x || thisY[i] > y+h-1 || thisY2[i] < y ) ) return 0;
|
||||||
|
return 1; // it's OK
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( borders <= 0 )
|
||||||
|
borders=$("Monitor"+monId).getStyle("border").toInt() * 2; // assume fixed size border, and added to both sides and top/bottom
|
||||||
|
// try fitting over first, then down. Each new one must land at either upper right or lower left corner of last (try in that order)
|
||||||
|
// Pick the one with the smallest Y, then smallest X if Y equal
|
||||||
|
var fitX = 999999999;
|
||||||
|
var fitY = 999999999;
|
||||||
|
for ( adjacent = 0; adjacent < m; adjacent ++ ) {
|
||||||
|
// try top right of adjacent
|
||||||
|
if ( doesItFit(thisX2[adjacent]+1, thisY[adjacent], monitorWidth[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, monitorHeight[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, m-1) == 1 ) {
|
||||||
|
if ( thisY[adjacent]<fitY || ( thisY[adjacent] == fitY && thisX2[adjacent]+1 < fitX ) ) {
|
||||||
|
fitX = thisX2[adjacent] + 1;
|
||||||
|
fitY = thisY[adjacent];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// try bottom left
|
||||||
|
if ( doesItFit(thisX[adjacent], thisY2[adjacent]+1, monitorWidth[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, monitorHeight[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, m-1) == 1 ) {
|
||||||
|
if ( thisY2[adjacent]+1 < fitY || ( thisY2[adjacent]+1 == fitY && thisX[adjacent] < fitX ) ) {
|
||||||
|
fitX = thisX[adjacent];
|
||||||
|
fitY = thisY2[adjacent] + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( m == 0 ) { // note for the very first one there were no adjacents so the above loop didn't run
|
||||||
|
if ( doesItFit(0,0,monitorWidth[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, monitorHeight[monId] * thisScale * monitorNormalizeScale[monId] * monitorZoomScale[monId] + borders, -1) == 1 ) {
|
||||||
|
fitX = 0;
|
||||||
|
fitY = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( fitX == 999999999 ) {
|
||||||
|
allFit = 0;
|
||||||
|
break; // break out of monitor loop flagging we didn't fit
|
||||||
|
}
|
||||||
|
thisX[m] =fitX;
|
||||||
|
thisX2[m]=fitX + monitorWidth[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders;
|
||||||
|
thisY[m] =fitY;
|
||||||
|
thisY2[m]=fitY + monitorHeight[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders;
|
||||||
|
thisArea += (thisX2[m] - thisX[m])*(thisY2[m] - thisY[m]);
|
||||||
|
}
|
||||||
|
if ( allFit == 1 ) {
|
||||||
|
minScale=thisScale;
|
||||||
|
if(bestFitArea<thisArea) {
|
||||||
|
bestFitArea=thisArea;
|
||||||
|
bestFitX=thisX;
|
||||||
|
bestFitY=thisY;
|
||||||
|
bestFitX2=thisX2;
|
||||||
|
bestFitY2=thisY2;
|
||||||
|
bestFitScale=thisScale;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// didn't fit
|
||||||
|
maxScale=thisScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( bestFitArea > 0 ) { // only rearrange if we could fit -- otherwise just do nothing, let them start coming out, whatever
|
||||||
|
for ( m = 0; m < numMonitors; m++ ) {
|
||||||
|
c = $("Monitor" + monitorPtr[m]);
|
||||||
|
c.style.position="absolute";
|
||||||
|
c.style.left=bestFitX[m].toString() + "px";
|
||||||
|
c.style.top=bestFitY[m].toString() + "px";
|
||||||
|
c.width = bestFitX2[m] - bestFitX[m] + 1 - borders;
|
||||||
|
c.height= bestFitY2[m] - bestFitY[m] + 1 - borders;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>>>>>>>>>>>>>>> Handles individual monitor clicks and navigation to the standard event/watch display
|
||||||
|
|
||||||
|
function showOneMonitor(monId) {
|
||||||
|
// link out to the normal view of one event's data
|
||||||
|
// We know the monitor, need to determine the event based on current time
|
||||||
|
var url;
|
||||||
|
if ( liveMode != 0 )
|
||||||
|
url="?view=watch&mid=" + monId.toString();
|
||||||
|
else
|
||||||
|
for ( var i=0, len=eId.length; i<len; i++ ) {
|
||||||
|
if ( eMonId[i] == monId && currentTimeSecs >= eStartSecs[i] && currentTimeSecs <= eEndSecs[i] )
|
||||||
|
url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) ));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
createPopup(url, 'zmEvent', 'event', monitorWidth[eMonId[i]], monitorHeight[eMonId[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoom(monId,scale) {
|
||||||
|
var lastZoomMonPriorScale = monitorZoomScale[monId];
|
||||||
|
monitorZoomScale[monId] *= scale;
|
||||||
|
if ( redrawScreen() == 0 ) {// failure here is probably because we zoomed too far
|
||||||
|
monitorZoomScale[monId] = lastZoomMonPriorScale;
|
||||||
|
alert("You can't zoom that far -- rolling back");
|
||||||
|
redrawScreen(); // put things back and hope it works
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickMonitor(event,monId) {
|
||||||
|
var monitor_element = $("Monitor"+monId.toString());
|
||||||
|
var pos_x = event.offsetX ? (event.offsetX) : event.pageX - monitor_element.offsetLeft;
|
||||||
|
var pos_y = event.offsetY ? (event.offsetY) : event.pageY - monitor_element.offsetTop;
|
||||||
|
if ( pos_x < monitor_element.width/4 && pos_y < monitor_element.height/4 )
|
||||||
|
zoom(monId,1.15);
|
||||||
|
else if ( pos_x > monitor_element.width * 3/4 && pos_y < monitor_element.height/4 )
|
||||||
|
zoom(monId,1/1.15);
|
||||||
|
else
|
||||||
|
showOneMonitor(monId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// >>>>>>>>> Initialization that runs on window load by being at the bottom
|
||||||
|
|
||||||
|
function initPage() {
|
||||||
|
canvas = $("timeline");
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) {
|
||||||
|
var monId = monitorPtr[i];
|
||||||
|
if ( ! monId ) continue;
|
||||||
|
monitorCanvasObj[monId] = $('Monitor'+monId );
|
||||||
|
if ( ! monitorCanvasObj[monId] ) {
|
||||||
|
alert("Couldn't find DOM element for Monitor"+monId + "monitorPtr.length="+len);
|
||||||
|
} else {
|
||||||
|
monitorCanvasCtx[monId] = monitorCanvasObj[monId].getContext('2d');
|
||||||
|
var imageObject = monitorImageObject[monId] = new Image();
|
||||||
|
imageObject.monId = monId;
|
||||||
|
imageObject.onload = function() {imagedone(this, this.monId, true )};
|
||||||
|
imageObject.onerror = function() {imagedone(this, this.monId, false )};
|
||||||
|
loadImage2Monitor( monId, monitorImageURL[monId] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawGraph();
|
||||||
|
setSpeed(speedIndex);
|
||||||
|
setFit(fitMode); // will redraw
|
||||||
|
setLive(liveMode); // will redraw
|
||||||
|
}
|
||||||
|
window.addEventListener("resize",redrawScreen);
|
||||||
|
// Kick everything off
|
||||||
|
window.addEvent( 'domready', initPage );
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
var currentScale=<?php echo $defaultScale?>;
|
||||||
|
var liveMode=<?php echo $initialModeIsLive?>;
|
||||||
|
console.log("Live mode?"+liveMode);
|
||||||
|
var fitMode=<?php echo $fitMode?>;
|
||||||
|
var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time
|
||||||
|
var speedIndex=<?php echo $speedIndex?>;
|
||||||
|
var currentDisplayInterval=<?php echo $initialDisplayInterval?>; // will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms)
|
||||||
|
var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live)
|
||||||
|
var timerInterval; // milliseconds between interrupts
|
||||||
|
var timerObj; // object to hold timer interval;
|
||||||
|
var freeTimeLastIntervals=[]; // Percentage of current interval used in loading most recent image
|
||||||
|
var imageLoadTimesEvaluated=0; // running count
|
||||||
|
var imageLoadTimesNeeded=15; // and how many we need
|
||||||
|
var timeLabelsFractOfRow = 0.9;
|
||||||
|
var eMonId = [];
|
||||||
|
var eId = [];
|
||||||
|
var eStartSecs = [];
|
||||||
|
var eEndSecs = [];
|
||||||
|
var eventFrames = []; // this is going to presume all frames equal durationlength
|
||||||
|
var groupStr=<?php if($group=="") echo '""'; else echo "\"&group=$group\""; ?>;
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Because we might not have time as the criteria, figure out the min/max time when we run the query
|
||||||
|
|
||||||
|
$minTimeSecs = strtotime('2036-01-01 01:01:01');
|
||||||
|
$maxTimeSecs = strtotime('1950-01-01 01:01:01');
|
||||||
|
|
||||||
|
// This builds the list of events that are eligible from this range
|
||||||
|
|
||||||
|
$index=0;
|
||||||
|
$anyAlarms=false;
|
||||||
|
|
||||||
|
$result = dbQuery( $eventsSql );
|
||||||
|
if ( ! $result ) {
|
||||||
|
Fatal( "SQL-ERR");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( $event = $result->fetch( PDO::FETCH_ASSOC ) ) {
|
||||||
|
|
||||||
|
if ( $minTimeSecs > $event['StartTimeSecs'] ) $minTimeSecs = $event['StartTimeSecs'];
|
||||||
|
if ( $maxTimeSecs < $event['CalcEndTimeSecs'] ) $maxTimeSecs = $event['CalcEndTimeSecs'];
|
||||||
|
echo "
|
||||||
|
eMonId[$index]=" . $event['MonitorId'] . ";
|
||||||
|
eId[$index]=" . $event['Id'] . ";
|
||||||
|
eStartSecs[$index]=" . $event['StartTimeSecs'] . ";
|
||||||
|
eEndSecs[$index]=" . $event['CalcEndTimeSecs'] . ";
|
||||||
|
eventFrames[$index]=" . $event['Frames'] . ";
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
|
$index = $index + 1;
|
||||||
|
if ( $event['MaxScore'] > 0 )
|
||||||
|
$anyAlarms = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is no data set the min/max to the passed in values
|
||||||
|
if ( $index == 0 ) {
|
||||||
|
if ( isset($minTime) && isset($maxTime) ) {
|
||||||
|
$minTimeSecs = strtotime($minTime);
|
||||||
|
$maxTimeSecs = strtotime($maxTime);
|
||||||
|
} else {
|
||||||
|
// this is the case of no passed in times AND no data -- just set something arbitrary
|
||||||
|
$minTimeSecs = strtotime('1950-06-01 01:01:01'); // random time so there's something to display
|
||||||
|
$maxTimeSecs = time() + 86400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only reset the calling time if there was no calling time
|
||||||
|
if ( !isset($minTime) || !isset($maxTime) ) {
|
||||||
|
$maxTime = strftime($maxTimeSecs);
|
||||||
|
$minTime = strftime($minTimeSecs);
|
||||||
|
} else {
|
||||||
|
$minTimeSecs = strtotime($minTime);
|
||||||
|
$maxTimeSecs = strtotime($maxTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we had any alarms in those events, this builds the list of all alarm frames, but consolidated down to (nearly) contiguous segments
|
||||||
|
// comparison in else governs how aggressively it consolidates
|
||||||
|
|
||||||
|
echo "var fMonId = [];\n";
|
||||||
|
echo "var fTimeFromSecs = [];\n";
|
||||||
|
echo "var fTimeToSecs = [];\n";
|
||||||
|
echo "var fScore = [];\n";
|
||||||
|
$maxScore=0;
|
||||||
|
$index=0;
|
||||||
|
$mId=-1;
|
||||||
|
$fromSecs=-1;
|
||||||
|
$toSecs=-1;
|
||||||
|
$maxScore=-1;
|
||||||
|
|
||||||
|
if ( $anyAlarms && $result = dbQuery( $frameSql ) ) {
|
||||||
|
|
||||||
|
while( $frame = $result->fetch( PDO::FETCH_ASSOC ) ) {
|
||||||
|
if ( $mId < 0 ) {
|
||||||
|
$mId = $frame['MonitorId'];
|
||||||
|
$fromSecs = $frame['TimeStampSecs'];
|
||||||
|
$toSecs = $frame['TimeStampSecs'];
|
||||||
|
$maxScore = $frame['Score'];
|
||||||
|
} else if ( $mId != $frame['MonitorId'] || $frame['TimeStampSecs'] - $toSecs > 10 ) {
|
||||||
|
// dump this one start a new
|
||||||
|
$index++;
|
||||||
|
echo "
|
||||||
|
fMonId[$index]= $mId;
|
||||||
|
fTimeFromSecs[$index]= $fromSecs;
|
||||||
|
fTimeToSecs[$index]= $toSecs;
|
||||||
|
fScore[$index]= $maxScore;
|
||||||
|
";
|
||||||
|
$mId = $frame['MonitorId'];
|
||||||
|
$fromSecs = $frame['TimeStampSecs'];
|
||||||
|
$toSecs = $frame['TimeStampSecs'];
|
||||||
|
$maxScore = $frame['Score'];
|
||||||
|
} else {
|
||||||
|
// just add this one on
|
||||||
|
$toSecs = $frame['TimeStampSecs'];
|
||||||
|
if ( $maxScore < $frame['Score'] ) $maxScore = $frame['Score'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $mId > 0 ) {
|
||||||
|
echo "
|
||||||
|
fMonId[$index]= $mId;
|
||||||
|
fTimeFromSecs[$index]= $fromSecs;
|
||||||
|
fTimeToSecs[$index]= $toSecs;
|
||||||
|
fScore[$index]= $maxScore;
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
|
||||||
|
echo "var monitorName = [];\n";
|
||||||
|
echo "var monitorLoading = [];\n";
|
||||||
|
echo "var monitorImageObject = [];\n";
|
||||||
|
echo "var monitorImageURL = [];\n";
|
||||||
|
echo "var monitorLoadingStageURL = [];\n";
|
||||||
|
echo "var monitorLoadStartTimems = [];\n";
|
||||||
|
echo "var monitorLoadEndTimems = [];\n";
|
||||||
|
echo "var monitorColour = [];\n";
|
||||||
|
echo "var monitorWidth = [];\n";
|
||||||
|
echo "var monitorHeight = [];\n";
|
||||||
|
echo "var monitorIndex = [];\n";
|
||||||
|
echo "var monitorNormalizeScale = [];\n";
|
||||||
|
echo "var monitorZoomScale = [];\n";
|
||||||
|
echo "var monitorCanvasObj = [];\n"; // stash location of these here so we don't have to search
|
||||||
|
echo "var monitorCanvasCtx = [];\n";
|
||||||
|
echo "var monitorPtr = []; // monitorName[monitorPtr[0]] is first monitor\n";
|
||||||
|
|
||||||
|
|
||||||
|
$numMonitors=0; // this array is indexed by the monitor ID for faster access later, so it may be sparse
|
||||||
|
$avgArea=floatval(0); // Calculations the normalizing scale
|
||||||
|
|
||||||
|
foreach ( $monitors as $m ) {
|
||||||
|
$avgArea = $avgArea + floatval($m->Width() * $m->Height());
|
||||||
|
$numMonitors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $numMonitors > 0 ) $avgArea = $avgArea / $numMonitors;
|
||||||
|
|
||||||
|
$numMonitors = 0;
|
||||||
|
foreach ( $monitors as $m ) {
|
||||||
|
echo " monitorLoading[" . $m->Id() . "]=false;\n";
|
||||||
|
echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n";
|
||||||
|
echo " monitorLoadingStageURL[" . $m->Id() . "] = '';\n";
|
||||||
|
echo " monitorColour[" . $m->Id() . "]=\"" . $m->WebColour() . "\";\n";
|
||||||
|
echo " monitorWidth[" . $m->Id() . "]=" . $m->Width() . ";\n";
|
||||||
|
echo " monitorHeight[" . $m->Id() . "]=" . $m->Height() . ";\n";
|
||||||
|
echo " monitorIndex[" . $m->Id() . "]=" . $numMonitors . ";\n";
|
||||||
|
echo " monitorName[" . $m->Id() . "]=\"" . $m->Name() . "\";\n";
|
||||||
|
echo " monitorLoadStartTimems[" . $m->Id() . "]=0;\n";
|
||||||
|
echo " monitorLoadEndTimems[" . $m->Id() . "]=0;\n";
|
||||||
|
echo " monitorNormalizeScale[" . $m->Id() . "]=" . sqrt($avgArea / ($m->Width() * $m->Height() )) . ";\n";
|
||||||
|
$zoomScale=1.0;
|
||||||
|
if(isset($_REQUEST[ 'z' . $m->Id() ]) )
|
||||||
|
$zoomScale = floatval( validHtmlStr($_REQUEST[ 'z' . $m->Id() ]) );
|
||||||
|
echo " monitorZoomScale[" . $m->Id() . "]=" . $zoomScale . ";\n";
|
||||||
|
echo " monitorPtr[" . $numMonitors . "]=" . $m->Id() . ";\n";
|
||||||
|
$numMonitors += 1;
|
||||||
|
}
|
||||||
|
echo "var numMonitors = $numMonitors;\n";
|
||||||
|
echo "var minTimeSecs=" . $minTimeSecs . ";\n";
|
||||||
|
echo "var maxTimeSecs=" . $maxTimeSecs . ";\n";
|
||||||
|
echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n";
|
||||||
|
if(isset($defaultCurrentTime))
|
||||||
|
echo "var currentTimeSecs=" . strtotime($defaultCurrentTime) . ";\n";
|
||||||
|
else
|
||||||
|
echo "var currentTimeSecs=" . ($minTimeSecs + $maxTimeSecs)/2 . ";\n";
|
||||||
|
|
||||||
|
echo 'var speeds=[';
|
||||||
|
for ($i=0; $i<count($speeds); $i++)
|
||||||
|
echo (($i>0)?', ':'') . $speeds[$i];
|
||||||
|
echo "];\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
var scrubAsObject=$('scrub');
|
||||||
|
var cWidth; // save canvas width
|
||||||
|
var cHeight; // save canvas height
|
||||||
|
var canvas; // global canvas definition so we don't have to keep looking it up
|
||||||
|
var ctx;
|
||||||
|
var underSlider; // use this to hold what is hidden by the slider
|
||||||
|
var underSliderX; // Where the above was taken from (left side, Y is zero)
|
||||||
|
|
|
@ -51,38 +51,29 @@ if ( ! $Server ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! empty($_REQUEST['mid']) ) {
|
if ( ! empty($_REQUEST['mid']) ) {
|
||||||
$monitor = dbFetchMonitor( $_REQUEST['mid'] );
|
$monitor = new Monitor( $_REQUEST['mid'] );
|
||||||
if ( ZM_OPT_X10 )
|
if ( ZM_OPT_X10 )
|
||||||
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']) );
|
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']) );
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$nextId = getTableAutoInc( 'Monitors' );
|
$nextId = getTableAutoInc( 'Monitors' );
|
||||||
$monitor = getMonitorObject($_REQUEST['dupId']);
|
if ( ! empty( $_REQUEST['dupId'] ) ) {
|
||||||
$clonedName = $monitor['Name'];
|
$monitor = new Monitor( $_REQUEST['dupId'] );
|
||||||
$monitor['Name'] = translate('Monitor').'-'.$nextId;
|
if ( ZM_OPT_X10 )
|
||||||
$monitor['Id']='0';
|
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['dupId']) );
|
||||||
}
|
$clonedName = $monitor->Name();
|
||||||
|
$monitor->Name( translate('Monitor').'-'.$nextId );
|
||||||
if ( ZM_OPT_X10 && empty($x10Monitor) ) {
|
$monitor->Id( $nextId );
|
||||||
$x10Monitor = array(
|
|
||||||
'Activation' => '',
|
|
||||||
'AlarmInput' => '',
|
|
||||||
'AlarmOutput' => '',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMonitorObject( $mid = null ) {
|
|
||||||
if ( $mid !== null ) {
|
|
||||||
$monitor = dbFetchMonitor($mid);
|
|
||||||
} else {
|
} else {
|
||||||
$monitor = array(
|
$monitor = new Monitor();
|
||||||
|
$monitor->set( array(
|
||||||
'Id' => 0,
|
'Id' => 0,
|
||||||
'Name' => 'willbereplaced',
|
'Name' => translate('Monitor').'-'.$nextId,
|
||||||
'Function' => 'Monitor',
|
'Function' => 'Monitor',
|
||||||
'Enabled' => true,
|
'Enabled' => true,
|
||||||
'LinkedMonitors' => '',
|
'LinkedMonitors' => '',
|
||||||
'Type' => '',
|
'Type' => '',
|
||||||
'Device' => '/dev/video0',
|
'Device' => "/dev/video0",
|
||||||
'Channel' => '0',
|
'Channel' => '0',
|
||||||
'Format' => 0x000000ff,
|
'Format' => 0x000000ff,
|
||||||
'Protocol' => '',
|
'Protocol' => '',
|
||||||
|
@ -90,7 +81,7 @@ function getMonitorObject( $mid = null ) {
|
||||||
'Host' => '',
|
'Host' => '',
|
||||||
'Path' => '',
|
'Path' => '',
|
||||||
'Options' => '',
|
'Options' => '',
|
||||||
'Port' => "80",
|
'Port' => '80',
|
||||||
'User' => '',
|
'User' => '',
|
||||||
'Pass' => '',
|
'Pass' => '',
|
||||||
'Colours' => 3,
|
'Colours' => 3,
|
||||||
|
@ -145,40 +136,49 @@ function getMonitorObject( $mid = null ) {
|
||||||
'V4LMultiBuffer' => '',
|
'V4LMultiBuffer' => '',
|
||||||
'V4LCapturesPerFrame' => 1,
|
'V4LCapturesPerFrame' => 1,
|
||||||
'ServerId' => $Server['Id'],
|
'ServerId' => $Server['Id'],
|
||||||
|
) );
|
||||||
|
} # end if $_REQUEST['dupID']
|
||||||
|
} # end if $_REQUEST['mid']
|
||||||
|
|
||||||
|
if ( ZM_OPT_X10 && empty($x10Monitor) ) {
|
||||||
|
$x10Monitor = array(
|
||||||
|
'Activation' => '',
|
||||||
|
'AlarmInput' => '',
|
||||||
|
'AlarmOutput' => '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ($monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fourcc( $a, $b, $c, $d ) {
|
function fourcc( $a, $b, $c, $d ) {
|
||||||
return( ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24) );
|
return( ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $_REQUEST['newMonitor'] ) ) {
|
if ( isset( $_REQUEST['newMonitor'] ) ) {
|
||||||
$newMonitor = $_REQUEST['newMonitor'];
|
# Update the monitor object with whatever has been set so far.
|
||||||
|
$monitor->set( $_REQUEST['newMonitor'] );
|
||||||
|
|
||||||
if ( ZM_OPT_X10 )
|
if ( ZM_OPT_X10 )
|
||||||
$newX10Monitor = $_REQUEST['newX10Monitor'];
|
$newX10Monitor = $_REQUEST['newX10Monitor'];
|
||||||
} else {
|
} else {
|
||||||
$newMonitor = $monitor;
|
# FIXME: Triggers in the db is a comma separated string. Needs to be an array.
|
||||||
$newMonitor['Triggers'] = explode( ',', isset($monitor['Triggers'])?$monitor['Triggers']:'' );
|
#$monitor->Triggers()= explode( ',', isset($monitor->Triggers())?$monitor->Triggers:"" );
|
||||||
if ( ZM_OPT_X10 )
|
if ( ZM_OPT_X10 )
|
||||||
$newX10Monitor = $x10Monitor;
|
$newX10Monitor = $x10Monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
$newMonitor['Name'] = trim($newMonitor['Name']);
|
# What if it has less zeros? This is not robust code.
|
||||||
|
if ( $monitor->AnalysisFPS() == '0.00' )
|
||||||
if ( $newMonitor['AnalysisFPS'] == '0.00' )
|
$monitor->AnalysisFPS( '' );
|
||||||
$newMonitor['AnalysisFPS'] = '';
|
if ( $monitor->MaxFPS() == '0.00' )
|
||||||
if ( $newMonitor['MaxFPS'] == '0.00' )
|
$monitor->MaxFPS( '' );
|
||||||
$newMonitor['MaxFPS'] = '';
|
if ( $monitor->AlarmMaxFPS() == '0.00' )
|
||||||
if ( $newMonitor['AlarmMaxFPS'] == '0.00' )
|
$monitor->AlarmMaxFPS( '' );
|
||||||
$newMonitor['AlarmMaxFPS'] = '';
|
|
||||||
|
|
||||||
if ( !empty($_REQUEST['preset']) ) {
|
if ( !empty($_REQUEST['preset']) ) {
|
||||||
$preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) );
|
$preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) );
|
||||||
foreach ( $preset as $name=>$value ) {
|
foreach ( $preset as $name=>$value ) {
|
||||||
|
# Does isset handle NULL's? I don't think this code is correct.
|
||||||
if ( isset($value) ) {
|
if ( isset($value) ) {
|
||||||
$newMonitor[$name] = $value;
|
$monitor->$name = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,15 +186,16 @@ if ( !empty($_REQUEST['probe']) ) {
|
||||||
$probe = unserialize(base64_decode($_REQUEST['probe']));
|
$probe = unserialize(base64_decode($_REQUEST['probe']));
|
||||||
foreach ( $probe as $name=>$value ) {
|
foreach ( $probe as $name=>$value ) {
|
||||||
if ( isset($value) ) {
|
if ( isset($value) ) {
|
||||||
$newMonitor[$name] = $value;
|
# Does isset handle NULL's? I don't think this code is correct.
|
||||||
|
$monitor->$name = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( ZM_HAS_V4L && $newMonitor['Type'] == 'Local' ) {
|
if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) {
|
||||||
$newMonitor['Palette'] = fourCC( substr($newMonitor['Palette'],0,1), substr($newMonitor['Palette'],1,1), substr($newMonitor['Palette'],2,1), substr($newMonitor['Palette'],3,1) );
|
$monitor->Palette( fourCC( substr($monitor->Palette,0,1), substr($monitor->Palette,1,1), substr($monitor->Palette,2,1), substr($monitor->Palette,3,1) ) );
|
||||||
if ( $newMonitor['Format'] == 'PAL' )
|
if ( $monitor->Format() == 'PAL' )
|
||||||
$newMonitor['Format'] = 0x000000ff;
|
$monitor->Format( 0x000000ff );
|
||||||
elseif ( $newMonitor['Format'] == 'NTSC' )
|
elseif ( $monitor->Format() == 'NTSC' )
|
||||||
$newMonitor['Format'] = 0x0000b000;
|
$monitor->Format( 0x0000b000 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +467,7 @@ $videowriteropts = array(
|
||||||
'H264 Camera Passthrough' => 2
|
'H264 Camera Passthrough' => 2
|
||||||
);
|
);
|
||||||
|
|
||||||
xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor['Name']) );
|
xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) );
|
||||||
?>
|
?>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
|
@ -474,7 +475,6 @@ xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor['Name'])
|
||||||
<?php
|
<?php
|
||||||
if ( canEdit( 'Monitors' ) ) {
|
if ( canEdit( 'Monitors' ) ) {
|
||||||
if ( isset ($_REQUEST['dupId'])) {
|
if ( isset ($_REQUEST['dupId'])) {
|
||||||
$dupId = $_REQUEST['dupId'];
|
|
||||||
?>
|
?>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
Configuration cloned from Monitor: <?php echo $clonedName ?>
|
Configuration cloned from Monitor: <?php echo $clonedName ?>
|
||||||
|
@ -483,20 +483,20 @@ if ( canEdit( 'Monitors' ) ) {
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div id="headerButtons">
|
<div id="headerButtons">
|
||||||
<a href="#" onclick="createPopup( '?view=monitorprobe&mid=<?php echo $monitor['Id'] ?>', 'zmMonitorProbe<?php echo $monitor['Id'] ?>', 'monitorprobe' ); return( false );"><?php echo translate('Probe') ?></a>
|
<a href="#" onclick="createPopup( '?view=monitorprobe&mid=<?php echo $monitor->Id()?>', 'zmMonitorProbe<?php echo $monitor->Id()?>', 'monitorprobe' ); return( false );"><?php echo translate('Probe') ?></a>
|
||||||
<?php
|
<?php
|
||||||
if ( ZM_HAS_ONVIF ) {
|
if ( ZM_HAS_ONVIF ) {
|
||||||
?>
|
?>
|
||||||
<a href="#" onclick="createPopup( '?view=onvifprobe&mid=<?php echo $monitor['Id'] ?>', 'zmOnvifProbe<?php echo $monitor['Id'] ?>', 'onvifprobe' ); return( false );"><?php echo translate('OnvifProbe') ?></a>
|
<a href="#" onclick="createPopup( '?view=onvifprobe&mid=<?php echo $monitor->Id()?>', 'zmOnvifProbe<?php echo $monitor->Id()?>', 'onvifprobe' ); return( false );"><?php echo translate('OnvifProbe') ?></a>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<a href="#" onclick="createPopup( '?view=monitorpreset&mid=<?php echo $monitor['Id'] ?>', 'zmMonitorPreset<?php echo $monitor['Id'] ?>', 'monitorpreset' ); return( false );"><?php echo translate('Presets') ?></a>
|
<a href="#" onclick="createPopup( '?view=monitorpreset&mid=<?php echo $monitor->Id()?>', 'zmMonitorPreset<?php echo $monitor->Id()?>', 'monitorpreset' ); return( false );"><?php echo translate('Presets') ?></a>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
} // end if canEdit('Monitors')
|
||||||
?>
|
?>
|
||||||
<h2><?php echo translate('Monitor') ?> - <?php echo validHtmlStr($monitor['Name']) ?><?php if ( !empty($monitor['Id']) ) { ?> (<?php echo $monitor['Id'] ?>)<?php } ?></h2>
|
<h2><?php echo translate('Monitor') ?> - <?php echo validHtmlStr($monitor->Name()) ?><?php if ( !empty($monitor->Id()) ) { ?> (<?php echo $monitor->Id()?>)<?php } ?></h2>
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ul class="tabList">
|
<ul class="tabList">
|
||||||
|
@ -519,116 +519,116 @@ if ( canEdit( 'Monitors' ) ) {
|
||||||
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
||||||
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
|
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
|
||||||
<input type="hidden" name="action" value="monitor"/>
|
<input type="hidden" name="action" value="monitor"/>
|
||||||
<input type="hidden" name="mid" value="<?php echo $monitor['Id'] ?>"/>
|
<input type="hidden" name="mid" value="<?php echo $monitor->Id()?>"/>
|
||||||
<input type="hidden" name="newMonitor[LinkedMonitors]" value="<?php echo isset($newMonitor['LinkedMonitors'])?$newMonitor['LinkedMonitors']:'' ?>"/>
|
<input type="hidden" name="newMonitor[LinkedMonitors]" value="<?php echo (null !== $monitor->LinkedMonitors())?$monitor->LinkedMonitors():'' ?>"/>
|
||||||
<input type="hidden" name="origMethod" value="<?php echo isset($newMonitor['Method'])?$newMonitor['Method']:'' ?>"/>
|
<input type="hidden" name="origMethod" value="<?php echo ( null !== $monitor->Method())?$monitor->Method():'' ?>"/>
|
||||||
<?php
|
<?php
|
||||||
if ( $tab != 'general' ) {
|
if ( $tab != 'general' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Name]" value="<?php echo validHtmlStr($newMonitor['Name']) ?>"/>
|
<input type="hidden" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ServerId]" value="<?php echo validHtmlStr($newMonitor['ServerId']) ?>"/>
|
<input type="hidden" name="newMonitor[ServerId]" value="<?php echo validHtmlStr($monitor->ServerId() ) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Type]" value="<?php echo validHtmlStr($newMonitor['Type']) ?>"/>
|
<input type="hidden" name="newMonitor[Type]" value="<?php echo validHtmlStr($monitor->Type()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Function]" value="<?php echo validHtmlStr($newMonitor['Function']) ?>"/>
|
<input type="hidden" name="newMonitor[Function]" value="<?php echo validHtmlStr($monitor->Function()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($newMonitor['Enabled']) ?>"/>
|
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($monitor->Enabled()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($newMonitor['RefBlendPerc']) ?>"/>
|
<input type="hidden" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($monitor->RefBlendPerc()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($newMonitor['AlarmRefBlendPerc']) ?>"/>
|
<input type="hidden" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($monitor->AlarmRefBlendPerc()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($newMonitor['AnalysisFPS']) ?>"/>
|
<input type="hidden" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($newMonitor['MaxFPS']) ?>"/>
|
<input type="hidden" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($newMonitor['AlarmMaxFPS']) ?>"/>
|
<input type="hidden" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
if ( isset($newMonitor['Triggers']) ) {
|
if ( null !== $monitor->Triggers() ) {
|
||||||
foreach( $newMonitor['Triggers'] as $newTrigger ) {
|
foreach( explode( ',', $monitor->Triggers() ) as $newTrigger ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Triggers][]" value="<?php echo validHtmlStr($newTrigger) ?>"/>
|
<input type="hidden" name="newMonitor[Triggers][]" value="<?php echo validHtmlStr($newTrigger) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( ZM_HAS_V4L && ($tab != 'source' || $newMonitor['Type'] != 'Local') ) {
|
if ( ZM_HAS_V4L && ($tab != 'source' || $monitor->Type()!= 'Local') ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Device]" value="<?php echo validHtmlStr($newMonitor['Device']) ?>"/>
|
<input type="hidden" name="newMonitor[Device]" value="<?php echo validHtmlStr($monitor->Device()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Channel]" value="<?php echo validHtmlStr($newMonitor['Channel']) ?>"/>
|
<input type="hidden" name="newMonitor[Channel]" value="<?php echo validHtmlStr($monitor->Channel()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Format]" value="<?php echo validHtmlStr($newMonitor['Format']) ?>"/>
|
<input type="hidden" name="newMonitor[Format]" value="<?php echo validHtmlStr($monitor->Format()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Palette]" value="<?php echo validHtmlStr($newMonitor['Palette']) ?>"/>
|
<input type="hidden" name="newMonitor[Palette]" value="<?php echo validHtmlStr($monitor->Palette()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[V4LMultiBuffer]" value="<?php echo validHtmlStr($newMonitor['V4LMultiBuffer']) ?>"/>
|
<input type="hidden" name="newMonitor[V4LMultiBuffer]" value="<?php echo validHtmlStr($monitor->V4LMultiBuffer()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo validHtmlStr($newMonitor['V4LCapturesPerFrame']) ?>"/>
|
<input type="hidden" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo validHtmlStr($monitor->V4LCapturesPerFrame()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || $newMonitor['Type'] != 'Remote' ) {
|
if ( $tab != 'source' || $monitor->Type()!= 'Remote' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Protocol]" value="<?php echo validHtmlStr($newMonitor['Protocol']) ?>"/>
|
<input type="hidden" name="newMonitor[Protocol]" value="<?php echo validHtmlStr($monitor->Protocol()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Host]" value="<?php echo validHtmlStr($newMonitor['Host']) ?>"/>
|
<input type="hidden" name="newMonitor[Host]" value="<?php echo validHtmlStr($monitor->Host()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Port]" value="<?php echo validHtmlStr($newMonitor['Port']) ?>"/>
|
<input type="hidden" name="newMonitor[Port]" value="<?php echo validHtmlStr($monitor->Port()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Local' && $newMonitor['Type'] != 'Remote' && $newMonitor['Type'] != 'Ffmpeg' && $newMonitor['Type'] != 'Libvlc') ) {
|
if ( $tab != 'source' || ($monitor->Type()!= 'Local' && $monitor->Type()!= 'Remote' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc') ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Method]" value="<?php echo validHtmlStr($newMonitor['Method']) ?>"/>
|
<input type="hidden" name="newMonitor[Method]" value="<?php echo validHtmlStr($monitor->Method()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Ffmpeg' && $newMonitor['Type'] != 'Libvlc' )) {
|
if ( $tab != 'source' || ($monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' )) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Options]" value="<?php echo validHtmlStr($newMonitor['Options']) ?>"/>
|
<input type="hidden" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Type'] != 'File' && $newMonitor['Type'] != 'Ffmpeg' && $newMonitor['Type'] != 'Libvlc' && $newMonitor['Type'] != 'cURL') ) {
|
if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL') ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Path]" value="<?php echo validHtmlStr($newMonitor['Path']) ?>"/>
|
<input type="hidden" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[User]" value="<?php echo validHtmlStr($newMonitor['User']) ?>"/>
|
<input type="hidden" name="newMonitor[User]" value="<?php echo validHtmlStr($monitor->User()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Pass]" value="<?php echo validHtmlStr($newMonitor['Pass']) ?>"/>
|
<input type="hidden" name="newMonitor[Pass]" value="<?php echo validHtmlStr($monitor->Pass()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' ) {
|
if ( $tab != 'source' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Colours]" value="<?php echo validHtmlStr($newMonitor['Colours']) ?>"/>
|
<input type="hidden" name="newMonitor[Colours]" value="<?php echo validHtmlStr($monitor->Colours()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Width]" value="<?php echo validHtmlStr($newMonitor['Width']) ?>"/>
|
<input type="hidden" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Height]" value="<?php echo validHtmlStr($newMonitor['Height']) ?>"/>
|
<input type="hidden" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Orientation]" value="<?php echo validHtmlStr($newMonitor['Orientation']) ?>"/>
|
<input type="hidden" name="newMonitor[Orientation]" value="<?php echo validHtmlStr($monitor->Orientation()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Deinterlacing]" value="<?php echo validHtmlStr($newMonitor['Deinterlacing']) ?>"/>
|
<input type="hidden" name="newMonitor[Deinterlacing]" value="<?php echo validHtmlStr($monitor->Deinterlacing()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'storage' ) {
|
if ( $tab != 'storage' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($newMonitor['SaveJPEGs']) ?>"/>
|
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($monitor->SaveJPEGs()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($newMonitor['VideoWriter']) ?>"/>
|
<input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($monitor->VideoWriter()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($newMonitor['EncoderParameters']) ?>"/>
|
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($monitor->EncoderParameters()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($newMonitor['RecordAudio']) ?>"/>
|
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($monitor->RecordAudio()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Protocol'] != 'RTSP')) {
|
if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Protocol()!= 'rtsp') ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[RTSPDescribe]" value="<?php echo validHtmlStr($newMonitor['RTSPDescribe']) ?>"/>
|
<input type="hidden" name="newMonitor[RTSPDescribe]" value="<?php echo validHtmlStr($monitor->RTSPDescribe()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'timestamp' ) {
|
if ( $tab != 'timestamp' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[LabelFormat]" value="<?php echo validHtmlStr($newMonitor['LabelFormat']) ?>"/>
|
<input type="hidden" name="newMonitor[LabelFormat]" value="<?php echo validHtmlStr($monitor->LabelFormat()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[LabelX]" value="<?php echo validHtmlStr($newMonitor['LabelX']) ?>"/>
|
<input type="hidden" name="newMonitor[LabelX]" value="<?php echo validHtmlStr($monitor->LabelX()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[LabelY]" value="<?php echo validHtmlStr($newMonitor['LabelY']) ?>"/>
|
<input type="hidden" name="newMonitor[LabelY]" value="<?php echo validHtmlStr($monitor->LabelY()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[LabelSize]" value="<?php echo validHtmlStr($newMonitor['LabelSize']) ?>"/>
|
<input type="hidden" name="newMonitor[LabelSize]" value="<?php echo validHtmlStr($monitor->LabelSize()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'buffers' ) {
|
if ( $tab != 'buffers' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[ImageBufferCount]" value="<?php echo validHtmlStr($newMonitor['ImageBufferCount']) ?>"/>
|
<input type="hidden" name="newMonitor[ImageBufferCount]" value="<?php echo validHtmlStr($monitor->ImageBufferCount()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[WarmupCount]" value="<?php echo validHtmlStr($newMonitor['WarmupCount']) ?>"/>
|
<input type="hidden" name="newMonitor[WarmupCount]" value="<?php echo validHtmlStr($monitor->WarmupCount()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[PreEventCount]" value="<?php echo validHtmlStr($newMonitor['PreEventCount']) ?>"/>
|
<input type="hidden" name="newMonitor[PreEventCount]" value="<?php echo validHtmlStr($monitor->PreEventCount()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[PostEventCount]" value="<?php echo validHtmlStr($newMonitor['PostEventCount']) ?>"/>
|
<input type="hidden" name="newMonitor[PostEventCount]" value="<?php echo validHtmlStr($monitor->PostEventCount()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[StreamReplayBuffer]" value="<?php echo validHtmlStr($newMonitor['StreamReplayBuffer']) ?>"/>
|
<input type="hidden" name="newMonitor[StreamReplayBuffer]" value="<?php echo validHtmlStr($monitor->StreamReplayBuffer()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AlarmFrameCount]" value="<?php echo validHtmlStr($newMonitor['AlarmFrameCount']) ?>"/>
|
<input type="hidden" name="newMonitor[AlarmFrameCount]" value="<?php echo validHtmlStr($monitor->AlarmFrameCount()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( ZM_OPT_CONTROL && $tab != 'control' ) {
|
if ( ZM_OPT_CONTROL && $tab != 'control' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Controllable]" value="<?php echo validHtmlStr($newMonitor['Controllable']) ?>"/>
|
<input type="hidden" name="newMonitor[Controllable]" value="<?php echo validHtmlStr($monitor->Controllable()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ControlId]" value="<?php echo validHtmlStr($newMonitor['ControlId']) ?>"/>
|
<input type="hidden" name="newMonitor[ControlId]" value="<?php echo validHtmlStr($monitor->ControlId()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($newMonitor['ControlDevice']) ?>"/>
|
<input type="hidden" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($monitor->ControlDevice()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($newMonitor['ControlAddress']) ?>"/>
|
<input type="hidden" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($monitor->ControlAddress()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($newMonitor['AutoStopTimeout']) ?>"/>
|
<input type="hidden" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($monitor->AutoStopTimeout()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[TrackMotion]" value="<?php echo validHtmlStr($newMonitor['TrackMotion']) ?>"/>
|
<input type="hidden" name="newMonitor[TrackMotion]" value="<?php echo validHtmlStr($monitor->TrackMotion()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[TrackDelay]" value="<?php echo validHtmlStr($newMonitor['TrackDelay']) ?>"/>
|
<input type="hidden" name="newMonitor[TrackDelay]" value="<?php echo validHtmlStr($monitor->TrackDelay()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ReturnLocation]" value="<?php echo validHtmlStr($newMonitor['ReturnLocation']) ?>"/>
|
<input type="hidden" name="newMonitor[ReturnLocation]" value="<?php echo validHtmlStr($monitor->ReturnLocation()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[ReturnDelay]" value="<?php echo validHtmlStr($newMonitor['ReturnDelay']) ?>"/>
|
<input type="hidden" name="newMonitor[ReturnDelay]" value="<?php echo validHtmlStr($monitor->ReturnDelay()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( ZM_OPT_X10 && $tab != 'x10' ) {
|
if ( ZM_OPT_X10 && $tab != 'x10' ) {
|
||||||
|
@ -640,22 +640,22 @@ if ( canEdit( 'Monitors' ) ) {
|
||||||
}
|
}
|
||||||
if ( $tab != 'misc' ) {
|
if ( $tab != 'misc' ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[EventPrefix]" value="<?php echo validHtmlStr($newMonitor['EventPrefix']) ?>"/>
|
<input type="hidden" name="newMonitor[EventPrefix]" value="<?php echo validHtmlStr($monitor->EventPrefix()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[SectionLength]" value="<?php echo validHtmlStr($newMonitor['SectionLength']) ?>"/>
|
<input type="hidden" name="newMonitor[SectionLength]" value="<?php echo validHtmlStr($monitor->SectionLength()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[FrameSkip]" value="<?php echo validHtmlStr($newMonitor['FrameSkip']) ?>"/>
|
<input type="hidden" name="newMonitor[FrameSkip]" value="<?php echo validHtmlStr($monitor->FrameSkip()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[MotionFrameSkip]" value="<?php echo validHtmlStr($newMonitor['MotionFrameSkip']) ?>"/>
|
<input type="hidden" name="newMonitor[MotionFrameSkip]" value="<?php echo validHtmlStr($monitor->MotionFrameSkip()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AnalysisUpdateDelay]" value="<?php echo validHtmlStr($newMonitor['AnalysisUpdateDelay']) ?>"/>
|
<input type="hidden" name="newMonitor[AnalysisUpdateDelay]" value="<?php echo validHtmlStr($monitor->AnalysisUpdateDelay()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[FPSReportInterval]" value="<?php echo validHtmlStr($newMonitor['FPSReportInterval']) ?>"/>
|
<input type="hidden" name="newMonitor[FPSReportInterval]" value="<?php echo validHtmlStr($monitor->FPSReportInterval()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[DefaultView]" value="<?php echo validHtmlStr($newMonitor['DefaultView']) ?>"/>
|
<input type="hidden" name="newMonitor[DefaultView]" value="<?php echo validHtmlStr($monitor->DefaultView()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[DefaultRate]" value="<?php echo validHtmlStr($newMonitor['DefaultRate']) ?>"/>
|
<input type="hidden" name="newMonitor[DefaultRate]" value="<?php echo validHtmlStr($monitor->DefaultRate()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[DefaultScale]" value="<?php echo validHtmlStr($newMonitor['DefaultScale']) ?>"/>
|
<input type="hidden" name="newMonitor[DefaultScale]" value="<?php echo validHtmlStr($monitor->DefaultScale()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[WebColour]" value="<?php echo validHtmlStr($newMonitor['WebColour']) ?>"/>
|
<input type="hidden" name="newMonitor[WebColour]" value="<?php echo validHtmlStr($monitor->WebColour()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[Exif]" value="<?php echo validHtmlStr($newMonitor['Exif']) ?>"/>
|
<input type="hidden" name="newMonitor[Exif]" value="<?php echo validHtmlStr($monitor->Exif()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( ZM_HAS_V4L && ($tab != 'misc' || $newMonitor['Type'] != 'Local') ) {
|
if ( ZM_HAS_V4L && ($tab != 'misc' || $monitor->Type()!= 'Local') ) {
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[SignalCheckColour]" value="<?php echo validHtmlStr($newMonitor['SignalCheckColour']) ?>"/>
|
<input type="hidden" name="newMonitor[SignalCheckColour]" value="<?php echo validHtmlStr($monitor->SignalCheckColour()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
@ -666,43 +666,43 @@ switch ( $tab ) {
|
||||||
case 'general' :
|
case 'general' :
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($newMonitor['Name']) ?>" size="16"/></td></tr>
|
<tr><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" size="16"/></td></tr>
|
||||||
<tr><td><?php echo translate('Server') ?></td><td>
|
<tr><td><?php echo translate('Server') ?></td><td>
|
||||||
<?php
|
<?php
|
||||||
$servers = array(''=>'None');
|
$servers = array(''=>'None');
|
||||||
$result = dbQuery( 'SELECT * FROM Servers ORDER BY Name');
|
$result = dbQuery( 'SELECT * FROM Servers ORDER BY Name');
|
||||||
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server' );
|
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server' );
|
||||||
foreach ( $results as $row => $server_obj ) {
|
foreach ( $results as $row => $server_obj ) {
|
||||||
$servers[$server_obj->Id] = $server_obj->Name();
|
$servers[$server_obj->Id()] = $server_obj->Name();
|
||||||
}
|
}
|
||||||
|
echo htmlSelect( 'newMonitor[ServerId]', $servers, $monitor->ServerId() );
|
||||||
?>
|
?>
|
||||||
<?php echo buildSelect( "newMonitor[ServerId]", $servers ); ?>
|
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td><?php echo translate('SourceType') ?></td><td><?php echo buildSelect( "newMonitor[Type]", $sourceTypes ); ?></td></tr>
|
<tr><td><?php echo translate('SourceType') ?></td><td><?php echo htmlSelect( "newMonitor[Type]", $sourceTypes, $monitor->Type() ); ?></td></tr>
|
||||||
<tr><td><?php echo translate('Function') ?></td><td><select name="newMonitor[Function]">
|
<tr><td><?php echo translate('Function') ?></td><td><select name="newMonitor[Function]">
|
||||||
<?php
|
<?php
|
||||||
foreach ( getEnumValues( 'Monitors', 'Function' ) as $optFunction ) {
|
foreach ( getEnumValues( 'Monitors', 'Function' ) as $optFunction ) {
|
||||||
?>
|
?>
|
||||||
<option value="<?php echo $optFunction ?>"<?php if ( $optFunction == $newMonitor['Function'] ) { ?> selected="selected"<?php } ?>><?php echo translate('Fn'.$optFunction) ?></option>
|
<option value="<?php echo $optFunction ?>"<?php if ( $optFunction == $monitor->Function()) { ?> selected="selected"<?php } ?>><?php echo translate('Fn'.$optFunction) ?></option>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</select></td></tr>
|
</select></td></tr>
|
||||||
<tr><td><?php echo translate('Enabled') ?></td><td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php if ( !empty($newMonitor['Enabled']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<tr><td><?php echo translate('Enabled') ?></td><td><input type="checkbox" name="newMonitor[Enabled]" value="1"<?php if ( !empty($monitor->Enabled()) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo translate('LinkedMonitors') ?></td>
|
<td><?php echo translate('LinkedMonitors') ?></td>
|
||||||
<td>
|
<td>
|
||||||
<select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )">
|
<select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )">
|
||||||
<?php
|
<?php
|
||||||
$monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' );
|
$monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' );
|
||||||
if ( !empty($newMonitor['LinkedMonitors']) )
|
if ( !empty($monitor->LinkedMonitors()) )
|
||||||
$monitorIds = array_flip( explode( ',', $newMonitor['LinkedMonitors'] ) );
|
$monitorIds = array_flip( explode( ',', $monitor->LinkedMonitors()) );
|
||||||
else
|
else
|
||||||
$monitorIds = array();
|
$monitorIds = array();
|
||||||
foreach ( $monitors as $monitor ) {
|
foreach ( $monitors as $linked_monitor ) {
|
||||||
if ( (empty($newMonitor['Id']) || ($monitor['Id'] != $newMonitor['Id'])) && visibleMonitor( $monitor['Id'] ) ) {
|
if ( (empty($monitor->Id()) || ($monitor->Id()!= $linked_monitor['Id'])) && visibleMonitor( $linked_monitor['Id'] ) ) {
|
||||||
?>
|
?>
|
||||||
<option value="<?php echo $monitor['Id'] ?>"<?php if ( array_key_exists( $monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($monitor['Name']) ?></option>
|
<option value="<?php echo $linked_monitor['Id'] ?>"<?php if ( array_key_exists( $linked_monitor['Id'], $monitorIds ) ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($linked_monitor['Name']) ?></option>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,34 +710,44 @@ switch ( $tab ) {
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($newMonitor['AnalysisFPS']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>" size="6"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
if ( $newMonitor['Type'] != 'Local' && $newMonitor['Type'] != 'File' ) {
|
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' ) {
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo translate('MaximumFPS') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td>
|
<td><?php echo translate('MaximumFPS') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td>
|
||||||
<td><input type="text" onclick="document.getElementById('newMonitor[MaxFPS]').innerHTML= ' CAUTION: See the help text'" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($newMonitor['MaxFPS']) ?>" size="5"/><span id="newMonitor[MaxFPS]" style="color:red"></span></td>
|
<td><input type="text" onclick="document.getElementById('newMonitor[MaxFPS]').innerHTML= ' CAUTION: See the help text'" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>" size="5"/><span id="newMonitor[MaxFPS]" style="color:red"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo translate('AlarmMaximumFPS') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td>
|
<td><?php echo translate('AlarmMaximumFPS') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_MAXFPS', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td>
|
||||||
<td><input type="text" onclick="document.getElementById('newMonitor[AlarmMaxFPS]').innerHTML= ' CAUTION: See the help text'" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($newMonitor['AlarmMaxFPS']) ?>" size="5"/><span id="newMonitor[AlarmMaxFPS]" style="color:red"></span></td>
|
<td><input type="text" onclick="document.getElementById('newMonitor[AlarmMaxFPS]').innerHTML= ' CAUTION: See the help text'" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>" size="5"/><span id="newMonitor[AlarmMaxFPS]" style="color:red"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php
|
<?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('MaximumFPS') ?></td><td><input type="text" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($newMonitor['MaxFPS']) ?>" size="5"/></td></tr>
|
<tr><td><?php echo translate('MaximumFPS') ?></td><td><input type="text" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>" size="5"/></td></tr>
|
||||||
<tr><td><?php echo translate('AlarmMaximumFPS') ?></td><td><input type="text" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($newMonitor['AlarmMaxFPS']) ?>" size="5"/></td></tr>
|
<tr><td><?php echo translate('AlarmMaximumFPS') ?></td><td><input type="text" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>" size="5"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( ZM_FAST_IMAGE_BLENDS ) {
|
if ( ZM_FAST_IMAGE_BLENDS ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RefImageBlendPct') ?></td><td><select name="newMonitor[RefBlendPerc]"><?php foreach ( $fastblendopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['RefBlendPerc'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr>
|
||||||
<tr><td><?php echo translate('AlarmRefImageBlendPct') ?></td><td><select name="newMonitor[AlarmRefBlendPerc]"><?php foreach ( $fastblendopts_alarm as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['AlarmRefBlendPerc'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<td><?php echo translate('RefImageBlendPct') ?></td>
|
||||||
|
<td><select name="newMonitor[RefBlendPerc]"><?php foreach ( $fastblendopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->RefBlendPerc() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo translate('AlarmRefImageBlendPct') ?></td>
|
||||||
|
<td>
|
||||||
|
<select name="newMonitor[AlarmRefBlendPerc]">
|
||||||
|
<?php foreach ( $fastblendopts_alarm as $name => $value ) { ?>
|
||||||
|
<option value="<?php echo $value ?>"<?php if ( $value == $monitor->AlarmRefBlendPerc() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option>
|
||||||
|
<?php } ?>
|
||||||
|
</select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RefImageBlendPct') ?></td><td><input type="text" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($newMonitor['RefBlendPerc']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('RefImageBlendPct') ?></td><td><input type="text" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($monitor->RefBlendPerc()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('AlarmRefImageBlendPct') ?></td><td><input type="text" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($newMonitor['AlarmRefBlendPerc']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('AlarmRefImageBlendPct') ?></td><td><input type="text" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($monitor->AlarmRefBlendPerc()) ?>" size="4"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
@ -753,10 +763,10 @@ switch ( $tab ) {
|
||||||
if ( $optCount && ($optCount%$breakCount == 0) )
|
if ( $optCount && ($optCount%$breakCount == 0) )
|
||||||
echo '</br>';
|
echo '</br>';
|
||||||
?>
|
?>
|
||||||
<input type="checkbox" name="newMonitor[Triggers][]" value="<?php echo $optTrigger ?>"<?php if ( isset($newMonitor['Triggers']) && in_array( $optTrigger, $newMonitor['Triggers'] ) ) { ?> checked="checked"<?php } ?>/> <?php echo $optTrigger ?>
|
<input type="checkbox" name="newMonitor[Triggers][]" value="<?php echo $optTrigger ?>"<?php if ( ( null !== $monitor->Triggers() ) && in_array( $optTrigger, $monitor->Triggers() ) ) { ?> checked="checked"<?php } ?>/> <?php echo $optTrigger ?>
|
||||||
<?php
|
<?php
|
||||||
$optCount ++;
|
$optCount ++;
|
||||||
} // end foreach $optTrigger
|
}
|
||||||
if ( !$optCount ) {
|
if ( !$optCount ) {
|
||||||
?>
|
?>
|
||||||
<em><?php echo translate('NoneAvailable') ?></em>
|
<em><?php echo translate('NoneAvailable') ?></em>
|
||||||
|
@ -769,132 +779,134 @@ switch ( $tab ) {
|
||||||
}
|
}
|
||||||
case 'source' :
|
case 'source' :
|
||||||
{
|
{
|
||||||
if ( ZM_HAS_V4L && $newMonitor['Type'] == 'Local' ) {
|
if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('DevicePath') ?></td><td><input type="text" name="newMonitor[Device]" value="<?php echo validHtmlStr($newMonitor['Device']) ?>" size="24"/></td></tr>
|
<tr><td><?php echo translate('DevicePath') ?></td><td><input type="text" name="newMonitor[Device]" value="<?php echo validHtmlStr($monitor->Device()) ?>" size="24"/></td></tr>
|
||||||
<tr><td><?php echo translate('CaptureMethod') ?></td><td><?php echo buildSelect( "newMonitor[Method]", $localMethods, "submitTab( '$tab' )" ); ?></td></tr>
|
<tr><td><?php echo translate('CaptureMethod') ?></td><td><?php echo htmlSelect( "newMonitor[Method]", $localMethods, $monitor->Method(), "submitTab( '$tab' );" ); ?></td></tr>
|
||||||
<?php
|
<?php
|
||||||
if ( ZM_HAS_V4L1 && $newMonitor['Method'] == 'v4l1' ) {
|
if ( ZM_HAS_V4L1 && $monitor->Method() == 'v4l1' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('DeviceChannel') ?></td><td><select name="newMonitor[Channel]"><?php foreach ( $v4l1DeviceChannels as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Channel'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('DeviceChannel') ?></td><td><select name="newMonitor[Channel]"><?php foreach ( $v4l1DeviceChannels as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Channel()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('DeviceFormat') ?></td><td><select name="newMonitor[Format]"><?php foreach ( $v4l1DeviceFormats as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Format'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('DeviceFormat') ?></td><td><select name="newMonitor[Format]"><?php foreach ( $v4l1DeviceFormats as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Format()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('CapturePalette') ?></td><td><select name="newMonitor[Palette]"><?php foreach ( $v4l1LocalPalettes as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Palette'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('CapturePalette') ?></td><td><select name="newMonitor[Palette]"><?php foreach ( $v4l1LocalPalettes as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Palette()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('DeviceChannel') ?></td><td><select name="newMonitor[Channel]"><?php foreach ( $v4l2DeviceChannels as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Channel'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('DeviceChannel') ?></td><td><select name="newMonitor[Channel]"><?php foreach ( $v4l2DeviceChannels as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Channel()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('DeviceFormat') ?></td><td><select name="newMonitor[Format]"><?php foreach ( $v4l2DeviceFormats as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Format'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('DeviceFormat') ?></td><td><select name="newMonitor[Format]"><?php foreach ( $v4l2DeviceFormats as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Format()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('CapturePalette') ?></td><td><select name="newMonitor[Palette]"><?php foreach ( $v4l2LocalPalettes as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Palette'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('CapturePalette') ?></td><td><select name="newMonitor[Palette]"><?php foreach ( $v4l2LocalPalettes as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Palette()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('V4LMultiBuffer') ?></td><td>
|
<tr><td><?php echo translate('V4LMultiBuffer') ?></td><td>
|
||||||
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]1" value="1" <?php echo ( $newMonitor['V4LMultiBuffer'] == 1 ? 'checked="checked"' : '' ) ?>/>
|
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]1" value="1" <?php echo ( $monitor->V4LMultiBuffer() == 1 ? 'checked="checked"' : '' ) ?>/>
|
||||||
<label for="newMonitor[V4LMultiBuffer]1">Yes</label>
|
<label for="newMonitor[V4LMultiBuffer]1">Yes</label>
|
||||||
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]0" value="0" <?php echo ( $newMonitor['V4LMultiBuffer'] == 0 ? 'checked="checked"' : '' ) ?>/>
|
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]0" value="0" <?php echo ( $monitor->V4LMultiBuffer() == 0 ? 'checked="checked"' : '' ) ?>/>
|
||||||
<label for="newMonitor[V4LMultiBuffer]0">No</label>
|
<label for="newMonitor[V4LMultiBuffer]0">No</label>
|
||||||
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]" value="" <?php echo ( empty($newMonitor['V4LMultiBuffer']) ? 'checked="checked"' : '' ) ?>/>
|
<input type="radio" name="newMonitor[V4LMultiBuffer]" id="newMonitor[V4LMultiBuffer]" value="" <?php echo ( empty($monitor->V4LMultiBuffer()) ? 'checked="checked"' : '' ) ?>/>
|
||||||
<label for="newMonitor[V4LMultiBuffer]">Use Config Value</label>
|
<label for="newMonitor[V4LMultiBuffer]">Use Config Value</label>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo $newMonitor['V4LCapturesPerFrame'] ?>"/></td></tr>
|
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo $monitor->V4LCapturesPerFrame()?>"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} elseif ( $newMonitor['Type'] == 'Remote' ) {
|
} elseif ( $monitor->Type() == 'Remote' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RemoteProtocol') ?></td><td><?php echo buildSelect( "newMonitor[Protocol]", $remoteProtocols, "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?></td></tr>
|
<tr><td><?php echo translate('RemoteProtocol') ?></td><td><?php echo htmlSelect( "newMonitor[Protocol]", $remoteProtocols, $monitor->Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?></td></tr>
|
||||||
<?php
|
<?php
|
||||||
if ( empty($newMonitor['Protocol']) || $newMonitor['Protocol'] == 'http' ) {
|
if ( empty($monitor->Protocol()) || $monitor->Protocol() == 'http' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo buildSelect( 'newMonitor[Method]', $httpMethods ); ?></td></tr>
|
<tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo htmlSelect( "newMonitor[Method]", $httpMethods, $monitor->Method() ); ?></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo buildSelect( 'newMonitor[Method]', $rtspMethods ); ?></td></tr>
|
<tr><td><?php echo translate('RemoteMethod') ?></td><td><?php echo htmlSelect( "newMonitor[Method]", $rtspMethods, $monitor->Method() ); ?></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('RemoteHostName') ?></td><td><input type="text" name="newMonitor[Host]" value="<?php echo validHtmlStr($newMonitor['Host']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo translate('RemoteHostName') ?></td><td><input type="text" name="newMonitor[Host]" value="<?php echo validHtmlStr($monitor->Host()) ?>" size="36"/></td></tr>
|
||||||
<tr><td><?php echo translate('RemoteHostPort') ?></td><td><input type="text" name="newMonitor[Port]" value="<?php echo validHtmlStr($newMonitor['Port']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('RemoteHostPort') ?></td><td><input type="text" name="newMonitor[Port]" value="<?php echo validHtmlStr($monitor->Port()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('RemoteHostPath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo translate('RemoteHostPath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} elseif ( $newMonitor['Type'] == 'File' ) {
|
} else if ( $monitor->Type() == 'File' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('SourcePath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo translate('SourcePath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} elseif ( $newMonitor['Type'] == 'cURL' ) {
|
} elseif ( $monitor->Type() == 'cURL' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo 'URL' ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo 'URL' ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
|
||||||
<tr><td><?php echo 'Username' ?></td><td><input type="text" name="newMonitor[User]" value="<?php echo validHtmlStr($newMonitor['User']) ?>" size="12"/></td></tr>
|
<tr><td><?php echo 'Username' ?></td><td><input type="text" name="newMonitor[User]" value="<?php echo validHtmlStr($monitor->User()) ?>" size="12"/></td></tr>
|
||||||
<tr><td><?php echo 'Password' ?></td><td><input type="text" name="newMonitor[Pass]" value="<?php echo validHtmlStr($newMonitor['Pass']) ?>" size="12"/></td></tr>
|
<tr><td><?php echo 'Password' ?></td><td><input type="text" name="newMonitor[Pass]" value="<?php echo validHtmlStr($monitor->Pass()) ?>" size="12"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} elseif ( $newMonitor['Type'] == 'Ffmpeg' || $newMonitor['Type'] == 'Libvlc' ) {
|
} elseif ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('SourcePath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo translate('SourcePath') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
|
||||||
<tr><td><?php echo translate('RemoteMethod') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_RTSPTrans', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><?php echo buildSelect( "newMonitor[Method]", $rtspFFMpegMethods ); ?></td></tr>
|
<tr><td><?php echo translate('RemoteMethod') ?> (<?php echo makePopupLink('?view=optionhelp&option=OPTIONS_RTSPTrans', 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><?php echo htmlSelect( "newMonitor[Method]", $rtspFFMpegMethods, $monitor->Method() ); ?></td></tr>
|
||||||
<tr><td><?php echo translate('Options') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_'.strtoupper($newMonitor['Type']), 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><input type="text" name="newMonitor[Options]" value="<?php echo validHtmlStr($newMonitor['Options']) ?>" size="36"/></td></tr>
|
<tr><td><?php echo translate('Options') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_'.strtoupper($monitor->Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)</td><td><input type="text" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>" size="36"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('TargetColorspace') ?></td><td><select name="newMonitor[Colours]"><?php foreach ( $Colours as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Colours'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('TargetColorspace') ?></td><td><select name="newMonitor[Colours]"><?php foreach ( $Colours as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Colours()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($newMonitor['Width']) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
|
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
|
||||||
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($newMonitor['Height']) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
|
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
|
||||||
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
|
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
|
||||||
<tr><td><?php echo translate('Orientation') ?></td><td><select name="newMonitor[Orientation]"><?php foreach ( $orientations as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Orientation'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('Orientation') ?></td><td><select name="newMonitor[Orientation]"><?php foreach ( $orientations as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Orientation()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
if ( $newMonitor['Type'] == 'Local' ) {
|
if ( $monitor->Type() == 'Local' ) {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Deinterlacing'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['Deinterlacing'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $newMonitor['Type'] == 'Remote' ) {
|
|
||||||
?>
|
?>
|
||||||
<tr id="RTSPDescribe"<?php if ( $newMonitor['Protocol'] != 'rtsp' ) { echo ' style="display:none;"'; } ?>><td><?php echo translate('RTSPDescribe') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_RTSPDESCRIBE', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td><td><input type="checkbox" name="newMonitor[RTSPDescribe]" value="1"<?php if ( !empty($newMonitor['RTSPDescribe']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<?php
|
||||||
|
if ( $monitor->Type() == 'Remote' ) {
|
||||||
|
?>
|
||||||
|
<tr id="RTSPDescribe"<?php if ( $monitor->Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>><td><?php echo translate('RTSPDescribe') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_RTSPDESCRIBE', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td><td><input type="checkbox" name="newMonitor[RTSPDescribe]" value="1"<?php if ( !empty($monitor->RTSPDescribe()) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'storage' :
|
case 'storage' :
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['SaveJPEGs'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['VideoWriter'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->VideoWriter() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($newMonitor['EncoderParameters']) ?></textarea></td></tr>
|
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr>
|
||||||
<tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( !empty($newMonitor['RecordAudio']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( !empty($monitor->RecordAudio()) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
case 'timestamp' :
|
case 'timestamp' :
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('TimestampLabelFormat') ?></td><td><input type="text" name="newMonitor[LabelFormat]" value="<?php echo validHtmlStr($newMonitor['LabelFormat']) ?>" size="32"/></td></tr>
|
<tr><td><?php echo translate('TimestampLabelFormat') ?></td><td><input type="text" name="newMonitor[LabelFormat]" value="<?php echo validHtmlStr($monitor->LabelFormat()) ?>" size="32"/></td></tr>
|
||||||
<tr><td><?php echo translate('TimestampLabelX') ?></td><td><input type="text" name="newMonitor[LabelX]" value="<?php echo validHtmlStr($newMonitor['LabelX']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('TimestampLabelX') ?></td><td><input type="text" name="newMonitor[LabelX]" value="<?php echo validHtmlStr($monitor->LabelX()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('TimestampLabelY') ?></td><td><input type="text" name="newMonitor[LabelY]" value="<?php echo validHtmlStr($newMonitor['LabelY']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('TimestampLabelY') ?></td><td><input type="text" name="newMonitor[LabelY]" value="<?php echo validHtmlStr($monitor->LabelY()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('TimestampLabelSize') ?></td><td><select name="newMonitor[LabelSize]"><?php foreach ( $label_size as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['LabelSize'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('TimestampLabelSize') ?></td><td><select name="newMonitor[LabelSize]"><?php foreach ( $label_size as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->LabelSize() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'buffers' :
|
case 'buffers' :
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('ImageBufferSize') ?></td><td><input type="text" name="newMonitor[ImageBufferCount]" value="<?php echo validHtmlStr($newMonitor['ImageBufferCount']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('ImageBufferSize') ?></td><td><input type="text" name="newMonitor[ImageBufferCount]" value="<?php echo validHtmlStr($monitor->ImageBufferCount()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('WarmupFrames') ?></td><td><input type="text" name="newMonitor[WarmupCount]" value="<?php echo validHtmlStr($newMonitor['WarmupCount']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('WarmupFrames') ?></td><td><input type="text" name="newMonitor[WarmupCount]" value="<?php echo validHtmlStr($monitor->WarmupCount()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('PreEventImageBuffer') ?></td><td><input type="text" name="newMonitor[PreEventCount]" value="<?php echo validHtmlStr($newMonitor['PreEventCount']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('PreEventImageBuffer') ?></td><td><input type="text" name="newMonitor[PreEventCount]" value="<?php echo validHtmlStr($monitor->PreEventCount()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('PostEventImageBuffer') ?></td><td><input type="text" name="newMonitor[PostEventCount]" value="<?php echo validHtmlStr($newMonitor['PostEventCount']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('PostEventImageBuffer') ?></td><td><input type="text" name="newMonitor[PostEventCount]" value="<?php echo validHtmlStr($monitor->PostEventCount()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('StreamReplayBuffer') ?></td><td><input type="text" name="newMonitor[StreamReplayBuffer]" value="<?php echo validHtmlStr($newMonitor['StreamReplayBuffer']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('StreamReplayBuffer') ?></td><td><input type="text" name="newMonitor[StreamReplayBuffer]" value="<?php echo validHtmlStr($monitor->StreamReplayBuffer()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('AlarmFrameCount') ?></td><td><input type="text" name="newMonitor[AlarmFrameCount]" value="<?php echo validHtmlStr($newMonitor['AlarmFrameCount']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('AlarmFrameCount') ?></td><td><input type="text" name="newMonitor[AlarmFrameCount]" value="<?php echo validHtmlStr($monitor->AlarmFrameCount()) ?>" size="4"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'control' :
|
case 'control' :
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('Controllable') ?></td><td><input type="checkbox" name="newMonitor[Controllable]" value="1"<?php if ( !empty($newMonitor['Controllable']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<tr><td><?php echo translate('Controllable') ?></td><td><input type="checkbox" name="newMonitor[Controllable]" value="1"<?php if ( !empty($monitor->Controllable()) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<tr><td><?php echo translate('ControlType') ?></td><td><?php echo buildSelect( "newMonitor[ControlId]", $controlTypes, 'loadLocations( this )' ); ?><?php if ( canEdit( 'Control' ) ) { ?> <a href="#" onclick="createPopup( '?view=controlcaps', 'zmControlCaps', 'controlcaps' );"><?php echo translate('Edit') ?></a><?php } ?></td></tr>
|
<tr><td><?php echo translate('ControlType') ?></td><td><?php echo buildSelect( "newMonitor[ControlId]", $controlTypes, 'loadLocations( this )' ); ?><?php if ( canEdit( 'Control' ) ) { ?> <a href="#" onclick="createPopup( '?view=controlcaps', 'zmControlCaps', 'controlcaps' );"><?php echo translate('Edit') ?></a><?php } ?></td></tr>
|
||||||
<tr><td><?php echo translate('ControlDevice') ?></td><td><input type="text" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($newMonitor['ControlDevice']) ?>" size="32"/></td></tr>
|
<tr><td><?php echo translate('ControlDevice') ?></td><td><input type="text" name="newMonitor[ControlDevice]" value="<?php echo validHtmlStr($monitor->ControlDevice()) ?>" size="32"/></td></tr>
|
||||||
<tr><td><?php echo translate('ControlAddress') ?></td><td><input type="text" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($newMonitor['ControlAddress']) ?>" size="32"/></td></tr>
|
<tr><td><?php echo translate('ControlAddress') ?></td><td><input type="text" name="newMonitor[ControlAddress]" value="<?php echo validHtmlStr($monitor->ControlAddress()) ?>" size="32"/></td></tr>
|
||||||
<tr><td><?php echo translate('AutoStopTimeout') ?></td><td><input type="text" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($newMonitor['AutoStopTimeout']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('AutoStopTimeout') ?></td><td><input type="text" name="newMonitor[AutoStopTimeout]" value="<?php echo validHtmlStr($monitor->AutoStopTimeout()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('TrackMotion') ?></td><td><input type="checkbox" name="newMonitor[TrackMotion]" value="1"<?php if ( !empty($newMonitor['TrackMotion']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<tr><td><?php echo translate('TrackMotion') ?></td><td><input type="checkbox" name="newMonitor[TrackMotion]" value="1"<?php if ( !empty($monitor->TrackMotion()) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
$return_options = array(
|
$return_options = array(
|
||||||
'-1' => translate('None'),
|
'-1' => translate('None'),
|
||||||
|
@ -902,9 +914,9 @@ switch ( $tab ) {
|
||||||
'1' => translate('Preset')." 1",
|
'1' => translate('Preset')." 1",
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('TrackDelay') ?></td><td><input type="text" name="newMonitor[TrackDelay]" value="<?php echo validHtmlStr($newMonitor['TrackDelay']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('TrackDelay') ?></td><td><input type="text" name="newMonitor[TrackDelay]" value="<?php echo validHtmlStr($monitor->TrackDelay()) ?>" size="4"/></td></tr>
|
||||||
<tr><td><?php echo translate('ReturnLocation') ?></td><td><?php echo buildSelect( "newMonitor[ReturnLocation]", $return_options ); ?></td></tr>
|
<tr><td><?php echo translate('ReturnLocation') ?></td><td><?php echo buildSelect( "newMonitor[ReturnLocation]", $return_options ); ?></td></tr>
|
||||||
<tr><td><?php echo translate('ReturnDelay') ?></td><td><input type="text" name="newMonitor[ReturnDelay]" value="<?php echo validHtmlStr($newMonitor['ReturnDelay']) ?>" size="4"/></td></tr>
|
<tr><td><?php echo translate('ReturnDelay') ?></td><td><input type="text" name="newMonitor[ReturnDelay]" value="<?php echo validHtmlStr($monitor->ReturnDelay()) ?>" size="4"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -920,34 +932,49 @@ switch ( $tab ) {
|
||||||
case 'misc' :
|
case 'misc' :
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('EventPrefix') ?></td><td><input type="text" name="newMonitor[EventPrefix]" value="<?php echo validHtmlStr($newMonitor['EventPrefix']) ?>" size="24"/></td></tr>
|
<tr><td><?php echo translate('EventPrefix') ?></td><td><input type="text" name="newMonitor[EventPrefix]" value="<?php echo validHtmlStr($monitor->EventPrefix()) ?>" size="24"/></td></tr>
|
||||||
<tr><td><?php echo translate('Sectionlength') ?></td><td><input type="text" name="newMonitor[SectionLength]" value="<?php echo validHtmlStr($newMonitor['SectionLength']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('Sectionlength') ?></td><td><input type="text" name="newMonitor[SectionLength]" value="<?php echo validHtmlStr($monitor->SectionLength()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('FrameSkip') ?></td><td><input type="text" name="newMonitor[FrameSkip]" value="<?php echo validHtmlStr($newMonitor['FrameSkip']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('FrameSkip') ?></td><td><input type="text" name="newMonitor[FrameSkip]" value="<?php echo validHtmlStr($monitor->FrameSkip()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('MotionFrameSkip') ?></td><td><input type="text" name="newMonitor[MotionFrameSkip]" value="<?php echo validHtmlStr($newMonitor['MotionFrameSkip']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('MotionFrameSkip') ?></td><td><input type="text" name="newMonitor[MotionFrameSkip]" value="<?php echo validHtmlStr($monitor->MotionFrameSkip()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('AnalysisUpdateDelay') ?></td><td><input type="text" name="newMonitor[AnalysisUpdateDelay]" value="<?php echo validHtmlStr($newMonitor['AnalysisUpdateDelay']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('AnalysisUpdateDelay') ?></td><td><input type="text" name="newMonitor[AnalysisUpdateDelay]" value="<?php echo validHtmlStr($monitor->AnalysisUpdateDelay()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('FPSReportInterval') ?></td><td><input type="text" name="newMonitor[FPSReportInterval]" value="<?php echo validHtmlStr($newMonitor['FPSReportInterval']) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('FPSReportInterval') ?></td><td><input type="text" name="newMonitor[FPSReportInterval]" value="<?php echo validHtmlStr($monitor->FPSReportInterval()) ?>" size="6"/></td></tr>
|
||||||
<tr><td><?php echo translate('DefaultView') ?></td><td><select name="newMonitor[DefaultView]">
|
<tr><td><?php echo translate('DefaultView') ?></td><td><select name="newMonitor[DefaultView]">
|
||||||
<?php
|
<?php
|
||||||
foreach ( getEnumValues( 'Monitors', 'DefaultView' ) as $opt_view ) {
|
foreach ( getEnumValues( 'Monitors', 'DefaultView' ) as $opt_view ) {
|
||||||
if ( $opt_view == 'Control' && ( !ZM_OPT_CONTROL || !$monitor['Controllable'] ) )
|
if ( $opt_view == 'Control' && ( !ZM_OPT_CONTROL || !$monitor->Controllable()) )
|
||||||
continue;
|
continue;
|
||||||
?>
|
?>
|
||||||
<option value="<?php echo $opt_view ?>"<?php if ( $opt_view == $newMonitor['DefaultView'] ) { ?> selected="selected"<?php } ?>><?php echo $opt_view ?></option>
|
<option value="<?php echo $opt_view ?>"<?php if ( $opt_view == $monitor->DefaultView()) { ?> selected="selected"<?php } ?>><?php echo $opt_view ?></option>
|
||||||
<?php
|
|
||||||
} // end foreach
|
|
||||||
?>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><td><?php echo translate('DefaultRate') ?></td><td><?php echo buildSelect( "newMonitor[DefaultRate]", $rates ); ?></td></tr>
|
|
||||||
<tr><td><?php echo translate('DefaultScale') ?></td><td><?php echo buildSelect( "newMonitor[DefaultScale]", $scales ); ?></td></tr>
|
|
||||||
<?php
|
|
||||||
if ( ZM_HAS_V4L && $newMonitor['Type'] == 'Local' ) {
|
|
||||||
?>
|
|
||||||
<tr><td><?php echo translate('SignalCheckColour') ?></td><td><input type="text" name="newMonitor[SignalCheckColour]" value="<?php echo validHtmlStr($newMonitor['SignalCheckColour']) ?>" size="10" onchange="$('SignalCheckSwatch').setStyle( 'backgroundColor', this.value )"/><span id="SignalCheckSwatch" class="swatch" style="background-color: <?php echo $newMonitor['SignalCheckColour'] ?>;"> </span></td></tr>
|
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<tr><td><?php echo translate('WebColour') ?></td><td><input type="text" name="newMonitor[WebColour]" value="<?php echo validHtmlStr($newMonitor['WebColour']) ?>" size="10" onchange="$('WebSwatch').setStyle( 'backgroundColor', this.value )"/><span id="WebSwatch" class="swatch" style="background-color: <?php echo validHtmlStr($newMonitor['WebColour']) ?>;"> </span></td></tr>
|
</select></td></tr>
|
||||||
<tr><td><?php echo translate('Exif') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_EXIF', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td><td><input type="checkbox" name="newMonitor[Exif]" value="1"<?php if ( !empty($newMonitor['Exif']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
<tr><td><?php echo translate('DefaultRate') ?></td><td><?php echo htmlSelect( "newMonitor[DefaultRate]", $rates, $monitor->DefaultRate() ); ?></td></tr>
|
||||||
|
<tr><td><?php echo translate('DefaultScale') ?></td><td><?php echo htmlSelect( "newMonitor[DefaultScale]", $scales, $monitor->DefaultScale() ); ?></td></tr>
|
||||||
|
<?php
|
||||||
|
if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) {
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo translate('SignalCheckColour') ?></td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="newMonitor[SignalCheckColour]" value="<?php echo validHtmlStr($monitor->SignalCheckColour()) ?>" size="10" onchange="$('SignalCheckSwatch').setStyle( 'backgroundColor', this.value )"/>
|
||||||
|
<span id="SignalCheckSwatch" class="swatch" style="background-color: <?php echo $monitor->SignalCheckColour()?>;"> </span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo translate('WebColour') ?></td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="newMonitor[WebColour]" value="<?php echo validHtmlStr($monitor->WebColour()) ?>" size="10" onchange="$('WebSwatch').setStyle( 'backgroundColor', this.value )"/>
|
||||||
|
<span id="WebSwatch" class="swatch" style="background-color: <?php echo validHtmlStr($monitor->WebColour()) ?>;"> </span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo translate('Exif') ?> (<?php echo makePopupLink( '?view=optionhelp&option=OPTIONS_EXIF', 'zmOptionHelp', 'optionhelp', '?' ) ?>) </td>
|
||||||
|
<td><input type="checkbox" name="newMonitor[Exif]" value="1"<?php if ( !empty($monitor->Exif()) ) { ?> checked="checked"<?php } ?>/></td>
|
||||||
|
</tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,16 @@ $maxHeight = 0;
|
||||||
$showControl = false;
|
$showControl = false;
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$monitors = array();
|
$monitors = array();
|
||||||
|
|
||||||
|
$scale = 100;
|
||||||
|
if ( isset( $_REQUEST['scale'] ) )
|
||||||
|
$scale = validInt($_REQUEST['scale']);
|
||||||
|
else if ( isset( $_COOKIE['zmMontageScale'] ) )
|
||||||
|
$scale = $_COOKIE['zmMontageScale'];
|
||||||
|
|
||||||
|
if ( ! $scale )
|
||||||
|
$scale = 100;
|
||||||
|
|
||||||
foreach( dbFetchAll( $sql ) as $row )
|
foreach( dbFetchAll( $sql ) as $row )
|
||||||
{
|
{
|
||||||
if ( !visibleMonitor( $row['Id'] ) )
|
if ( !visibleMonitor( $row['Id'] ) )
|
||||||
|
@ -47,14 +57,17 @@ foreach( dbFetchAll( $sql ) as $row )
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$monitor_scale = 100;
|
||||||
if ( isset( $_REQUEST['scale'] ) )
|
if ( isset( $_REQUEST['scale'] ) )
|
||||||
$scale = validInt($_REQUEST['scale']);
|
$monitor_scale = validInt($_REQUEST['scale']);
|
||||||
else if ( isset( $_COOKIE['zmMontageScale'] ) )
|
else if ( isset( $_COOKIE['zmMontageScale'] ) )
|
||||||
$scale = $_COOKIE['zmMontageScale'];
|
$monitor_scale = $_COOKIE['zmMontageScale'];
|
||||||
else
|
else
|
||||||
$scale = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
$monitor_scale = reScale( SCALE_BASE, $row['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||||
|
if ( ! $monitor_scale )
|
||||||
|
$monitor_scale = 100;
|
||||||
|
|
||||||
$scaleWidth = reScale( $row['Width'], $scale );
|
$scaleWidth = reScale( $row['Width'], $monitor_scale );
|
||||||
$scaleHeight = reScale( $row['Height'], $scale );
|
$scaleHeight = reScale( $row['Height'], $scale );
|
||||||
if ( $maxWidth < $scaleWidth )
|
if ( $maxWidth < $scaleWidth )
|
||||||
$maxWidth = $scaleWidth;
|
$maxWidth = $scaleWidth;
|
||||||
|
@ -117,14 +130,11 @@ foreach ( $monitors as $monitor )
|
||||||
<div id="monitor<?php echo $monitor->index() ?>" class="monitor idle">
|
<div id="monitor<?php echo $monitor->index() ?>" class="monitor idle">
|
||||||
<div id="imageFeed<?php echo $monitor->index() ?>" class="imageFeed" onclick="createPopup( '?view=watch&mid=<?php echo $monitor->Id() ?>', 'zmWatch<?php echo $monitor->Id() ?>', 'watch', <?php echo $monitor->scaleWidth() ?>, <?php echo $monitor->scaleHeight() ?> );">
|
<div id="imageFeed<?php echo $monitor->index() ?>" class="imageFeed" onclick="createPopup( '?view=watch&mid=<?php echo $monitor->Id() ?>', 'zmWatch<?php echo $monitor->Id() ?>', 'watch', <?php echo $monitor->scaleWidth() ?>, <?php echo $monitor->scaleHeight() ?> );">
|
||||||
<?php
|
<?php
|
||||||
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT )
|
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
||||||
{
|
$streamSrc = $monitor->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>$scale, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_LIVE_FORMAT ) );
|
||||||
$streamSrc = $monitor->getStreamSrc( array( "mode=mpeg", "scale=".$scale, "bitrate=".ZM_WEB_VIDEO_BITRATE, "maxfps=".ZM_WEB_VIDEO_MAXFPS, "format=".ZM_MPEG_LIVE_FORMAT ) );
|
|
||||||
outputVideoStream( "liveStream".$monitor->Id(), $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), ZM_MPEG_LIVE_FORMAT );
|
outputVideoStream( "liveStream".$monitor->Id(), $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), ZM_MPEG_LIVE_FORMAT );
|
||||||
}
|
} else {
|
||||||
else
|
$streamSrc = $monitor->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS ) );
|
||||||
{
|
|
||||||
$streamSrc = $monitor->getStreamSrc( array( "mode=jpeg", "scale=".$scale, "maxfps=".ZM_WEB_VIDEO_MAXFPS ) );
|
|
||||||
if ( canStreamNative() )
|
if ( canStreamNative() )
|
||||||
{
|
{
|
||||||
outputImageStream( "liveStream".$monitor->Id(), $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), validHtmlStr($monitor->Name()) );
|
outputImageStream( "liveStream".$monitor->Id(), $streamSrc, reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), validHtmlStr($monitor->Name()) );
|
||||||
|
|
|
@ -116,7 +116,7 @@ if ( !empty($_REQUEST['group']) ) {
|
||||||
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
|
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
|
||||||
|
|
||||||
$eventsSql = '
|
$eventsSql = '
|
||||||
SELECT E.Id,E.Name,E.StorageId,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
|
SELECT E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
|
||||||
CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id)
|
CASE WHEN E.EndTime IS NULL THEN (SELECT UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval max(Delta)+0.5 Second)) FROM Frames F WHERE F.EventId=E.Id)
|
||||||
ELSE UNIX_TIMESTAMP(E.EndTime)
|
ELSE UNIX_TIMESTAMP(E.EndTime)
|
||||||
END AS CalcEndTimeSecs, E.Length,
|
END AS CalcEndTimeSecs, E.Length,
|
||||||
|
@ -222,7 +222,7 @@ $monitors = array();
|
||||||
$monitorsSql .= ' ORDER BY Sequence ASC';
|
$monitorsSql .= ' ORDER BY Sequence ASC';
|
||||||
$index=0;
|
$index=0;
|
||||||
foreach( dbFetchAll( $monitorsSql ) as $row ) {
|
foreach( dbFetchAll( $monitorsSql ) as $row ) {
|
||||||
$monitors[$index] = $row;
|
$monitors[$index] = new Monitor( $row );
|
||||||
$index = $index + 1;
|
$index = $index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,921 +270,15 @@ input[type=range]::-ms-tooltip {
|
||||||
<span id="scrubright"></span>
|
<span id="scrubright"></span>
|
||||||
<span id="scruboutput"></span>
|
<span id="scruboutput"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="monitors">
|
||||||
<?php
|
<?php
|
||||||
// Monitor images - these had to be loaded after the monitors used were determined (after loading events)
|
// Monitor images - these had to be loaded after the monitors used were determined (after loading events)
|
||||||
|
|
||||||
echo '<div id="monitors">';
|
|
||||||
foreach ($monitors as $m) {
|
foreach ($monitors as $m) {
|
||||||
echo '<canvas width="' . $m['Width'] * $defaultScale . 'px" height="' . $m['Height'] * $defaultScale . 'px" id="Monitor' . $m['Id'] . '" style="border:3px solid ' . $m['WebColour'] . '" onclick="clickMonitor(event,' . $m['Id'] . ')">No Canvas Support!!</canvas>';
|
echo '<canvas width="' . $m->Width() * $defaultScale . 'px" height="' . $m->Height() * $defaultScale . 'px" id="Monitor' . $m->Id() . '" style="border:3px solid ' . $m->WebColour() . '" onclick="clickMonitor(event,' . $m->Id() . ')">No Canvas Support!!</canvas>';
|
||||||
}
|
}
|
||||||
echo "</div>\n";
|
|
||||||
echo "<p id=\"fps\">evaluating fps</p>\n";
|
|
||||||
echo "<script type=\"text/javascript\">\n";
|
|
||||||
?>
|
?>
|
||||||
|
</div>
|
||||||
var currentScale=<?php echo $defaultScale?>;
|
<p id="fps">evaluating fps</p>
|
||||||
var liveMode=<?php echo $initialModeIsLive?>;
|
|
||||||
var fitMode=<?php echo $fitMode?>;
|
|
||||||
var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time
|
|
||||||
var speedIndex=<?php echo $speedIndex?>;
|
|
||||||
var currentDisplayInterval=<?php echo $initialDisplayInterval?>; // will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms)
|
|
||||||
var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live)
|
|
||||||
var timerInterval; // milliseconds between interrupts
|
|
||||||
var timerObj; // object to hold timer interval;
|
|
||||||
var freeTimeLastIntervals=[]; // Percentage of current interval used in loading most recent image
|
|
||||||
var imageLoadTimesEvaluated=0; // running count
|
|
||||||
var imageLoadTimesNeeded=15; // and how many we need
|
|
||||||
var timeLabelsFractOfRow = 0.9;
|
|
||||||
var eMonId = [];
|
|
||||||
var eId = [];
|
|
||||||
var eStartSecs = [];
|
|
||||||
var eEndSecs = [];
|
|
||||||
var ePath = [];
|
|
||||||
var eventFrames = []; // this is going to presume all frames equal durationlength
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
// Because we might not have time as the criteria, figure out the min/max time when we run the query
|
|
||||||
|
|
||||||
$minTimeSecs = strtotime("2036-01-01 01:01:01");
|
|
||||||
$maxTimeSecs = strtotime("1950-01-01 01:01:01");
|
|
||||||
|
|
||||||
// This builds the list of events that are eligible from this range
|
|
||||||
|
|
||||||
$index=0;
|
|
||||||
$anyAlarms=false;
|
|
||||||
|
|
||||||
foreach( dbFetchAll( $eventsSql ) as $event ) {
|
|
||||||
if( $minTimeSecs > $event['StartTimeSecs']) $minTimeSecs = $event['StartTimeSecs'];
|
|
||||||
if( $maxTimeSecs < $event['CalcEndTimeSecs']) $maxTimeSecs = $event['CalcEndTimeSecs'];
|
|
||||||
echo "eMonId[$index]=" . $event['MonitorId'] . ";
|
|
||||||
eId[$index]=" . $event['Id'] . ";
|
|
||||||
eStartSecs[$index]=" . $event['StartTimeSecs'] . ";
|
|
||||||
eEndSecs[$index]=" . $event['CalcEndTimeSecs'] . ";
|
|
||||||
eventFrames[$index]=" . $event['Frames'] . "; ";
|
|
||||||
|
|
||||||
// NO GOOD, need to use view=image
|
|
||||||
if ( ZM_USE_DEEP_STORAGE )
|
|
||||||
echo "ePath[$index] = \"events/" . $event['MonitorId'] . "/" . strftime("%y/%m/%d/%H/%M/%S", $event['StartTimeSecs']) . "/\";" ;
|
|
||||||
else
|
|
||||||
echo "ePath[$index] = \"events/" . $event['MonitorId'] . "/" . $event['Id'] . "/\";" ;
|
|
||||||
$index = $index + 1;
|
|
||||||
if($event['MaxScore']>0)
|
|
||||||
$anyAlarms = true;
|
|
||||||
echo "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is no data set the min/max to the passed in values
|
|
||||||
if($index == 0) {
|
|
||||||
if(isset($minTime) && isset($maxTime)) {
|
|
||||||
$minTimeSecs = strtotime($minTime);
|
|
||||||
$maxTimeSecs = strtotime($maxTime);
|
|
||||||
} else {
|
|
||||||
// this is the case of no passed in times AND no data -- just set something arbitrary
|
|
||||||
$minTimeSecs=strtotime('1950-06-01 01:01:01'); // random time so there's something to display
|
|
||||||
$maxTimeSecs=strtotime('2020-06-02 02:02:02');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only reset the calling time if there was no calling time
|
|
||||||
if(!isset($minTime) || !isset($maxTime)) {
|
|
||||||
$maxTime = strftime($maxTimeSecs);
|
|
||||||
$minTime = strftime($minTimeSecs);
|
|
||||||
} else {
|
|
||||||
$minTimeSecs = strtotime($minTime);
|
|
||||||
$maxTimeSecs = strtotime($maxTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we had any alarms in those events, this builds the list of all alarm frames, but consolidated down to (nearly) contiguous segments
|
|
||||||
// comparison in else governs how aggressively it consolidates
|
|
||||||
|
|
||||||
echo "var fMonId = [];\n";
|
|
||||||
echo "var fTimeFromSecs = [];\n";
|
|
||||||
echo "var fTimeToSecs = [];\n";
|
|
||||||
echo "var fScore = [];\n";
|
|
||||||
$maxScore=0;
|
|
||||||
$index=0;
|
|
||||||
$mId=-1;
|
|
||||||
$fromSecs=-1;
|
|
||||||
$toSecs=-1;
|
|
||||||
$maxScore=-1;
|
|
||||||
|
|
||||||
if($anyAlarms) {
|
|
||||||
foreach( dbFetchAll ($frameSql) as $frame ) {
|
|
||||||
if($mId<0) {
|
|
||||||
$mId=$frame['MonitorId'];
|
|
||||||
$fromSecs=$frame['TimeStampSecs'];
|
|
||||||
$toSecs=$frame['TimeStampSecs'];
|
|
||||||
$maxScore=$frame['Score'];
|
|
||||||
} else if ($mId != $frame['MonitorId'] || $frame['TimeStampSecs'] - $toSecs > 10) {
|
|
||||||
// dump this one start a new
|
|
||||||
$index++;
|
|
||||||
echo " fMonId[$index]=" . $mId . ";";
|
|
||||||
echo " fTimeFromSecs[$index]=" . $fromSecs . ";";
|
|
||||||
echo " fTimeToSecs[$index]=" . $toSecs . ";";
|
|
||||||
echo " fScore[$index]=" . $maxScore . ";\n";
|
|
||||||
$mId=$frame['MonitorId'];
|
|
||||||
$fromSecs=$frame['TimeStampSecs'];
|
|
||||||
$toSecs=$frame['TimeStampSecs'];
|
|
||||||
$maxScore=$frame['Score'];
|
|
||||||
} else {
|
|
||||||
// just add this one on
|
|
||||||
$toSecs=$frame['TimeStampSecs'];
|
|
||||||
if($maxScore < $frame['Score']) $maxScore=$frame['Score'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if($mId>0) {
|
|
||||||
echo " fMonId[$index]=" . $mId . ";";
|
|
||||||
echo " fTimeFromSecs[$index]=" . $fromSecs . ";";
|
|
||||||
echo " fTimeToSecs[$index]=" . $toSecs . ";";
|
|
||||||
echo " fScore[$index]=" . $maxScore . ";\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
|
|
||||||
echo "var monitorName = [];\n";
|
|
||||||
echo "var monitorLoading = [];\n";
|
|
||||||
echo "var monitorImageObject = [];\n";
|
|
||||||
echo "var monitorLoadingStageURL = [];\n";
|
|
||||||
echo "var monitorLoadStartTimems = [];\n";
|
|
||||||
echo "var monitorLoadEndTimems = [];\n";
|
|
||||||
echo "var monitorColour = [];\n";
|
|
||||||
echo "var monitorWidth = [];\n";
|
|
||||||
echo "var monitorHeight = [];\n";
|
|
||||||
echo "var monitorIndex = [];\n";
|
|
||||||
echo "var monitorNormalizeScale = [];\n";
|
|
||||||
echo "var monitorZoomScale = [];\n";
|
|
||||||
echo "var monitorCanvasObj = [];\n"; // stash location of these here so we don't have to search
|
|
||||||
echo "var monitorCanvasCtx = [];\n";
|
|
||||||
echo "var monitorPtr = []; // monitorName[monitorPtr[0]] is first monitor\n";
|
|
||||||
|
|
||||||
|
|
||||||
$numMonitors=0; // this array is indexed by the monitor ID for faster access later, so it may be sparse
|
|
||||||
$avgArea=floatval(0); // Calculations the normalizing scale
|
|
||||||
|
|
||||||
foreach ($monitors as $m) {
|
|
||||||
$avgArea = $avgArea + floatval($m['Width'] * $m['Height']);
|
|
||||||
$numMonitors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($numMonitors>0) $avgArea= $avgArea / $numMonitors;
|
|
||||||
|
|
||||||
$numMonitors=0;
|
|
||||||
foreach ($monitors as $m) {
|
|
||||||
echo " monitorLoading[" . $m['Id'] . "]=false; ";
|
|
||||||
echo " monitorImageObject[" . $m['Id'] . "]=null; ";
|
|
||||||
echo " monitorLoadingStageURL[" . $m['Id'] . "] = ''; ";
|
|
||||||
echo " monitorColour[" . $m['Id'] . "]=\"" . $m['WebColour'] . "\"; ";
|
|
||||||
echo " monitorWidth[" . $m['Id'] . "]=" . $m['Width'] . "; ";
|
|
||||||
echo " monitorHeight[" . $m['Id'] . "]=" . $m['Height'] . "; ";
|
|
||||||
echo " monitorIndex[" . $m['Id'] . "]=" . $numMonitors . "; ";
|
|
||||||
echo " monitorName[" . $m['Id'] . "]=\"" . $m['Name'] . "\"; ";
|
|
||||||
echo " monitorLoadStartTimems[" . $m['Id'] . "]=0; ";
|
|
||||||
echo " monitorLoadEndTimems[" . $m['Id'] . "]=0; ";
|
|
||||||
echo " monitorCanvasObj[" . $m['Id'] . "]=document.getElementById('Monitor" . $m['Id'] . "'); ";
|
|
||||||
echo " monitorCanvasCtx[" . $m['Id'] . "]=monitorCanvasObj[" . $m['Id'] . "].getContext('2d'); ";
|
|
||||||
echo " monitorNormalizeScale[" . $m['Id'] . "]=" . sqrt($avgArea / ($m['Width'] * $m['Height'] )) . "; ";
|
|
||||||
$zoomScale=1.0;
|
|
||||||
if(isset($_REQUEST[ 'z' . $m['Id'] ]) )
|
|
||||||
$zoomScale = floatval( validHtmlStr($_REQUEST[ 'z' . $m['Id'] ]) );
|
|
||||||
echo " monitorZoomScale[" . $m['Id'] . "]=" . $zoomScale . ";";
|
|
||||||
echo " monitorPtr[" . $numMonitors . "]=" . $m['Id'] . ";\n";
|
|
||||||
$numMonitors += 1;
|
|
||||||
}
|
|
||||||
echo "var numMonitors = $numMonitors;\n";
|
|
||||||
echo "var minTimeSecs=" . $minTimeSecs . ";\n";
|
|
||||||
echo "var maxTimeSecs=" . $maxTimeSecs . ";\n";
|
|
||||||
echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n";
|
|
||||||
if(isset($defaultCurrentTime))
|
|
||||||
echo "var currentTimeSecs=" . strtotime($defaultCurrentTime) . ";\n";
|
|
||||||
else
|
|
||||||
echo "var currentTimeSecs=" . ($minTimeSecs + $maxTimeSecs)/2 . ";\n";
|
|
||||||
|
|
||||||
echo "var speeds=[";
|
|
||||||
for ($i=0; $i<count($speeds); $i++)
|
|
||||||
echo (($i>0)?", ":"") . $speeds[$i];
|
|
||||||
echo "];\n";
|
|
||||||
?>
|
|
||||||
|
|
||||||
var scrubAsObject=document.getElementById('scrub');
|
|
||||||
var cWidth; // save canvas width
|
|
||||||
var cHeight; // save canvas height
|
|
||||||
var canvas=document.getElementById("timeline"); // global canvas definition so we don't have to keep looking it up
|
|
||||||
var ctx=canvas.getContext('2d');
|
|
||||||
var underSlider; // use this to hold what is hidden by the slider
|
|
||||||
var underSliderX; // Where the above was taken from (left side, Y is zero)
|
|
||||||
|
|
||||||
function evaluateLoadTimes() {
|
|
||||||
// Only consider it a completed event if we load ALL monitors, then zero all and start again
|
|
||||||
var start=0;
|
|
||||||
var end=0;
|
|
||||||
if(liveMode!=1 && currentSpeed==0) return; // don't evaluate when we are not moving as we can do nothing really fast.
|
|
||||||
for(var i=0; i<monitorIndex.length; i++) {
|
|
||||||
if( monitorName[i]>"") {
|
|
||||||
if( monitorLoadEndTimems[i]==0) return; // if we have a monitor with no time yet just wait
|
|
||||||
if( start == 0 || start > monitorLoadStartTimems[i] ) start = monitorLoadStartTimems[i];
|
|
||||||
if( end == 0 || end < monitorLoadEndTimems[i] ) end = monitorLoadEndTimems[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(start==0 || end==0) return; // we really should not get here
|
|
||||||
for(var i=0; i<numMonitors; i++) {
|
|
||||||
monitorLoadStartTimems[monitorPtr[i]]=0;
|
|
||||||
monitorLoadEndTimems[monitorPtr[i]]=0;
|
|
||||||
}
|
|
||||||
freeTimeLastIntervals[imageLoadTimesEvaluated++] = 1 - ((end - start)/currentDisplayInterval);
|
|
||||||
if( imageLoadTimesEvaluated < imageLoadTimesNeeded ) return;
|
|
||||||
var avgFrac=0;
|
|
||||||
for(var i=0; i<imageLoadTimesEvaluated; i++)
|
|
||||||
avgFrac += freeTimeLastIntervals[i];
|
|
||||||
avgFrac = avgFrac / imageLoadTimesEvaluated;
|
|
||||||
// The larger this is(positive) the faster we can go
|
|
||||||
if (avgFrac >= 0.9) currentDisplayInterval = (currentDisplayInterval * 0.50).toFixed(1); // we can go much faster
|
|
||||||
else if (avgFrac >= 0.8) currentDisplayInterval = (currentDisplayInterval * 0.55).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.7) currentDisplayInterval = (currentDisplayInterval * 0.60).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.6) currentDisplayInterval = (currentDisplayInterval * 0.65).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.5) currentDisplayInterval = (currentDisplayInterval * 0.70).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.4) currentDisplayInterval = (currentDisplayInterval * 0.80).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.35) currentDisplayInterval = (currentDisplayInterval * 0.90).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.3) currentDisplayInterval = (currentDisplayInterval * 1.00).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.25) currentDisplayInterval = (currentDisplayInterval * 1.20).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.2) currentDisplayInterval = (currentDisplayInterval * 1.50).toFixed(1);
|
|
||||||
else if (avgFrac >= 0.1) currentDisplayInterval = (currentDisplayInterval * 2.00).toFixed(1);
|
|
||||||
else currentDisplayInterval = (currentDisplayInterval * 2.50).toFixed(1);
|
|
||||||
currentDisplayInterval=Math.min(Math.max(currentDisplayInterval, 30),10000); // limit this from about 30fps to .1 fps
|
|
||||||
imageLoadTimesEvaluated=0;
|
|
||||||
setSpeed(speedIndex);
|
|
||||||
$('fps').innerHTML="Display refresh rate is " + (1000 / currentDisplayInterval).toFixed(1) + " per second, avgFrac=" + avgFrac.toFixed(3) + ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
function SetImageSource(monId,val) {
|
|
||||||
if(liveMode==1) {
|
|
||||||
// This uses the standard php routine to set up the url and authentication, but because it is called repeatedly the built in random number is not usable, so one is appended below for two total (yuck)
|
|
||||||
var effectiveScale = (100.0 * monitorCanvasObj[monId].width) / monitorWidth[monId];
|
|
||||||
var $x = "<?php echo getStreamSrc( array("mode=single"),"&" )?>" + "&monitor=" + monId.toString() + "&scale=" + effectiveScale + Math.random().toString() ;
|
|
||||||
return $x;
|
|
||||||
} else {
|
|
||||||
var zeropad = <?php echo sprintf("\"%0" . ZM_EVENT_IMAGE_DIGITS . "d\"",0); ?>;
|
|
||||||
for(var i=0, eIdlength = eId.length; i<eIdlength; i++) {
|
|
||||||
// Search for a match
|
|
||||||
if(eMonId[i]==monId && val >= eStartSecs[i] && val <= eEndSecs[i]) {
|
|
||||||
var frame=parseInt((val - eStartSecs[i])/(eEndSecs[i]-eStartSecs[i])*eventFrames[i])+1;
|
|
||||||
img = "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
} // end for
|
|
||||||
return "no data";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function imagedone(obj, monId, success) {
|
|
||||||
if(success) {
|
|
||||||
monitorCanvasCtx[monId].drawImage( monitorImageObject[monId], 0, 0, monitorCanvasObj[monId].width, monitorCanvasObj[monId].height);
|
|
||||||
var iconSize=(Math.max(monitorCanvasObj[monId].width,monitorCanvasObj[monId].height) * 0.10);
|
|
||||||
monitorCanvasCtx[monId].font = "600 " + iconSize.toString() + "px Arial";
|
|
||||||
monitorCanvasCtx[monId].fillStyle="white";
|
|
||||||
monitorCanvasCtx[monId].globalCompositeOperation="difference";
|
|
||||||
monitorCanvasCtx[monId].fillText("+",iconSize*0.2, iconSize*1.2);
|
|
||||||
monitorCanvasCtx[monId].fillText("-",monitorCanvasObj[monId].width - iconSize*1.2, iconSize*1.2);
|
|
||||||
monitorCanvasCtx[monId].globalCompositeOperation="source-over";
|
|
||||||
monitorLoadEndTimems[monId] = new Date().getTime(); // elapsed time to load
|
|
||||||
evaluateLoadTimes();
|
|
||||||
}
|
|
||||||
monitorLoading[monId]=false;
|
|
||||||
if(!success) {
|
|
||||||
// if we had a failrue queue up the no-data image
|
|
||||||
loadImage2Monitor(monId,"no data"); // leave the staged URL if there is one, just ignore it here.
|
|
||||||
} else {
|
|
||||||
if(monitorLoadingStageURL[monId]=="") return;
|
|
||||||
loadImage2Monitor(monId,monitorLoadingStageURL[monId]);
|
|
||||||
monitorLoadingStageURL[monId]="";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadImage2Monitor(monId,url) {
|
|
||||||
if(monitorLoading[monId] && monitorImageObject[monId].src != url ) {
|
|
||||||
// never queue the same image twice (if it's loading it has to be defined, right?
|
|
||||||
monitorLoadingStageURL[monId]=url; // we don't care if we are overriting, it means it didn't change fast enough
|
|
||||||
} else {
|
|
||||||
var skipthis=0;
|
|
||||||
if( typeof monitorImageObject[monId] !== "undefined" && monitorImageObject[monId] != null && monitorImageObject[monId].src == url ) return; // do nothing if it's the same
|
|
||||||
if( monitorImageObject[monId] == null ) {
|
|
||||||
monitorImageObject[monId]=new Image();
|
|
||||||
monitorImageObject[monId].onload = function() {imagedone(this, monId,true )};
|
|
||||||
monitorImageObject[monId].onerror = function() {imagedone(this, monId,false)};
|
|
||||||
}
|
|
||||||
if(url=='no data') {
|
|
||||||
monitorCanvasCtx[monId].fillStyle="white";
|
|
||||||
monitorCanvasCtx[monId].fillRect(0,0,monitorCanvasObj[monId].width,monitorCanvasObj[monId].height);
|
|
||||||
var textSize=monitorCanvasObj[monId].width * 0.15;
|
|
||||||
var text="No Data";
|
|
||||||
monitorCanvasCtx[monId].font = "600 " + textSize.toString() + "px Arial";
|
|
||||||
monitorCanvasCtx[monId].fillStyle="black";
|
|
||||||
var textWidth = monitorCanvasCtx[monId].measureText(text).width;
|
|
||||||
monitorCanvasCtx[monId].fillText(text,monitorCanvasObj[monId].width/2 - textWidth/2,monitorCanvasObj[monId].height/2);
|
|
||||||
} else {
|
|
||||||
monitorLoading[monId]=true;
|
|
||||||
monitorLoadStartTimems[monId]=new Date().getTime();
|
|
||||||
monitorImageObject[monId].src=url; // starts a load but doesn't refresh yet, wait until ready
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function timerFire() {
|
|
||||||
// See if we need to reschedule
|
|
||||||
if(currentDisplayInterval != timerInterval || currentSpeed == 0) {
|
|
||||||
// zero just turn off interrupts
|
|
||||||
clearInterval(timerObj);
|
|
||||||
timerInterval=currentDisplayInterval;
|
|
||||||
if(currentSpeed>0 || liveMode!=0) timerObj=setInterval(timerFire,timerInterval); // don't fire out of live mode if speed is zero
|
|
||||||
}
|
|
||||||
|
|
||||||
if (liveMode) outputUpdate(currentTimeSecs); // In live mode we basically do nothing but redisplay
|
|
||||||
else if (currentTimeSecs + playSecsperInterval >= maxTimeSecs) // beyond the end just stop
|
|
||||||
{
|
|
||||||
setSpeed(0);
|
|
||||||
outputUpdate(currentTimeSecs);
|
|
||||||
}
|
|
||||||
else outputUpdate(currentTimeSecs + playSecsperInterval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSliderOnGraph(val) {
|
|
||||||
var sliderWidth=10;
|
|
||||||
var sliderLineWidth=1;
|
|
||||||
var sliderHeight=cHeight;
|
|
||||||
|
|
||||||
if(liveMode==1) {
|
|
||||||
val=Math.floor( Date.now() / 1000);
|
|
||||||
}
|
|
||||||
// Set some sizes
|
|
||||||
|
|
||||||
var labelpx = Math.max( 6, Math.min( 20, parseInt(cHeight * timeLabelsFractOfRow / (numMonitors+1)) ) );
|
|
||||||
var labbottom=parseInt(cHeight * 0.2 / (numMonitors+1)).toString() + "px"; // This is positioning same as row labels below, but from bottom so 1-position
|
|
||||||
var labfont=labelpx + "px Georgia"; // set this like below row labels
|
|
||||||
|
|
||||||
if(numMonitors>0) {
|
|
||||||
// if we have no data to display don't do the slider itself
|
|
||||||
var sliderX=parseInt( (val - minTimeSecs) / rangeTimeSecs * cWidth - sliderWidth/2); // position left side of slider
|
|
||||||
if(sliderX < 0) sliderX=0;
|
|
||||||
if(sliderX+sliderWidth > cWidth) sliderX=cWidth-sliderWidth-1;
|
|
||||||
|
|
||||||
// If we have data already saved first restore it from LAST time
|
|
||||||
|
|
||||||
if(typeof underSlider !== 'undefined')
|
|
||||||
{
|
|
||||||
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
|
|
||||||
underSlider=undefined;
|
|
||||||
}
|
|
||||||
if(liveMode==0) // we get rid of the slider if we switch to live (since it may not be in the "right" place)
|
|
||||||
{
|
|
||||||
// Now save where we are putting it THIS time
|
|
||||||
underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
|
|
||||||
// And add in the slider'
|
|
||||||
ctx.lineWidth=sliderLineWidth;
|
|
||||||
ctx.strokeStyle='black';
|
|
||||||
// looks like strokes are on the outside (or could be) so shrink it by the line width so we replace all the pixels
|
|
||||||
ctx.strokeRect(sliderX+sliderLineWidth,sliderLineWidth,sliderWidth - 2*sliderLineWidth, sliderHeight - 2*sliderLineWidth);
|
|
||||||
underSliderX=sliderX;
|
|
||||||
}
|
|
||||||
var o = $('scruboutput');
|
|
||||||
if(liveMode==1)
|
|
||||||
{
|
|
||||||
o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
|
|
||||||
o.style.color="red";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.innerHTML=secs2dbstr(val);
|
|
||||||
o.style.color="blue";
|
|
||||||
}
|
|
||||||
o.style.position="absolute";
|
|
||||||
o.style.bottom=labbottom;
|
|
||||||
o.style.font=labfont;
|
|
||||||
// try to get length and then when we get too close to the right switch to the left
|
|
||||||
var len = o.offsetWidth;
|
|
||||||
var x;
|
|
||||||
if(sliderX > cWidth/2)
|
|
||||||
x=sliderX - len - 10;
|
|
||||||
else
|
|
||||||
x=sliderX + 10;
|
|
||||||
o.style.left=x.toString() + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
// This displays (or not) the left/right limits depending on how close the slider is.
|
|
||||||
// Because these change widths if the slider is too close, use the slider width as an estimate for the left/right label length (i.e. don't recalculate len from above)
|
|
||||||
// If this starts to collide increase some of the extra space
|
|
||||||
|
|
||||||
var o = $('scrubleft');
|
|
||||||
o.innerHTML=secs2dbstr(minTimeSecs);
|
|
||||||
o.style.position="absolute";
|
|
||||||
o.style.bottom=labbottom;
|
|
||||||
o.style.font=labfont;
|
|
||||||
o.style.left="5px";
|
|
||||||
if(numMonitors==0) // we need a len calculation if we skipped the slider
|
|
||||||
len = o.offsetWidth;
|
|
||||||
// If the slider will overlay part of this suppress (this is the left side)
|
|
||||||
if(len + 10 > sliderX || cWidth < len * 4 ) // that last check is for very narrow browsers
|
|
||||||
o.style.display="none";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.style.display="inline";
|
|
||||||
o.style.display="inline-flex"; // safari won't take this but will just ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
var o = $('scrubright');
|
|
||||||
o.innerHTML=secs2dbstr(maxTimeSecs);
|
|
||||||
o.style.position="absolute";
|
|
||||||
o.style.bottom=labbottom;
|
|
||||||
o.style.font=labfont;
|
|
||||||
// If the slider will overlay part of this suppress (this is the right side)
|
|
||||||
o.style.left=(cWidth - len - 15).toString() + "px";
|
|
||||||
if(sliderX > cWidth - len - 20 || cWidth < len * 4 )
|
|
||||||
o.style.display="none";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o.style.display="inline";
|
|
||||||
o.style.display="inline-flex";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawGraph()
|
|
||||||
{
|
|
||||||
var divWidth=$('timelinediv').clientWidth
|
|
||||||
canvas.width = cWidth = divWidth; // Let it float and determine width (it should be sized a bit smaller percentage of window)
|
|
||||||
canvas.height=cHeight = parseInt(window.innerHeight * 0.10);
|
|
||||||
if(eId.length==0)
|
|
||||||
{
|
|
||||||
ctx.font="40px Georgia";
|
|
||||||
ctx.fillStyle="Black";
|
|
||||||
ctx.globalAlpha=1;
|
|
||||||
var t="No data found in range - choose differently";
|
|
||||||
var l=ctx.measureText(t).width;
|
|
||||||
ctx.fillText(t,(cWidth - l)/2, cHeight-10);
|
|
||||||
underSlider=undefined;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var rowHeight=parseInt(cHeight / (numMonitors + 1) ); // Leave room for a scale of some sort
|
|
||||||
|
|
||||||
// first fill in the bars for the events (not alarms)
|
|
||||||
|
|
||||||
for(var i=0; i<eId.length; i++) // Display all we loaded
|
|
||||||
{
|
|
||||||
var x1=parseInt( (eStartSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
|
||||||
var x2=parseInt( (eEndSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
|
|
||||||
ctx.fillStyle=monitorColour[eMonId[i]];
|
|
||||||
ctx.globalAlpha = 0.2; // light color for background
|
|
||||||
ctx.clearRect(x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
|
|
||||||
ctx.fillRect (x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight);
|
|
||||||
}
|
|
||||||
for(var i=0; (i<fScore.length) && (maxScore>0); i++) // Now put in scored frames (if any)
|
|
||||||
{
|
|
||||||
var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
|
||||||
var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
|
|
||||||
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
|
|
||||||
ctx.fillStyle=monitorColour[fMonId[i]];
|
|
||||||
ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background
|
|
||||||
ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight);
|
|
||||||
}
|
|
||||||
for(var i=0; i<numMonitors; i++) // Note that this may be a sparse array
|
|
||||||
{
|
|
||||||
ctx.font= parseInt(rowHeight * timeLabelsFractOfRow).toString() + "px Georgia";
|
|
||||||
ctx.fillStyle="Black";
|
|
||||||
ctx.globalAlpha=1;
|
|
||||||
ctx.fillText(monitorName[monitorPtr[i]], 0, (i + 1 - (1 - timeLabelsFractOfRow)/2 ) * rowHeight ); // This should roughly center font in row
|
|
||||||
}
|
|
||||||
underSlider=undefined; // flag we don't have a slider cached
|
|
||||||
drawSliderOnGraph(currentTimeSecs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function redrawScreen()
|
|
||||||
{
|
|
||||||
if(fitMode==0) // if we fit, then monitors were absolutely positioned already (or will be) otherwise release them to float
|
|
||||||
{
|
|
||||||
for(var i=0; i<numMonitors; i++)
|
|
||||||
monitorCanvasObj[monitorPtr[i]].style.position="";
|
|
||||||
$('monitors').setStyle('height',"auto");
|
|
||||||
}
|
|
||||||
if(liveMode==1) // if we are not in live view switch to history -- this has to come before fit in case we re-establish the timeline
|
|
||||||
{
|
|
||||||
$('SpeedDiv').style.display="none";
|
|
||||||
$('timelinediv').style.display="none";
|
|
||||||
$('live').innerHTML="History";
|
|
||||||
$('zoomin').style.display="none";
|
|
||||||
$('zoomout').style.display="none";
|
|
||||||
$('panleft').style.display="none";
|
|
||||||
$('panright').style.display="none";
|
|
||||||
|
|
||||||
}
|
|
||||||
else // switch out of liveview mode
|
|
||||||
{
|
|
||||||
$('SpeedDiv').style.display="inline";
|
|
||||||
$('SpeedDiv').style.display="inline-flex";
|
|
||||||
$('timelinediv').style.display=null;
|
|
||||||
$('live').innerHTML="Live";
|
|
||||||
$('zoomin').style.display="inline";
|
|
||||||
$('zoomin').style.display="inline-flex";
|
|
||||||
$('zoomout').style.display="inline";
|
|
||||||
$('zoomout').style.display="inline-flex";
|
|
||||||
$('panleft').style.display="inline";
|
|
||||||
$('panleft').style.display="inline-flex";
|
|
||||||
$('panright').style.display="inline";
|
|
||||||
$('panright').style.display="inline-flex";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fitMode==1)
|
|
||||||
{
|
|
||||||
$('ScaleDiv').style.display="none";
|
|
||||||
$('fit').innerHTML="Scale";
|
|
||||||
var vh=window.innerHeight;
|
|
||||||
var vw=window.innerWidth;
|
|
||||||
var pos=$('monitors').getPosition();
|
|
||||||
var mh=(vh - pos.y - $('fps').getSize().y);
|
|
||||||
$('monitors').setStyle('height',mh.toString() + "px"); // leave a small gap at bottom
|
|
||||||
if(maxfit2($('monitors').getSize().x,$('monitors').getSize().y) == 0) /// if we fail to fix we back out of fit mode -- ??? This may need some better handling
|
|
||||||
fitMode=1-fitMode;
|
|
||||||
}
|
|
||||||
else // switch out of fit mode
|
|
||||||
{
|
|
||||||
$('ScaleDiv').style.display="inline";
|
|
||||||
$('ScaleDiv').style.display="inline-flex";
|
|
||||||
$('fit').innerHTML="Fit";
|
|
||||||
setScale(currentScale);
|
|
||||||
}
|
|
||||||
drawGraph();
|
|
||||||
outputUpdate(currentTimeSecs);
|
|
||||||
timerFire(); // force a fire in case it's not timing
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function outputUpdate(val)
|
|
||||||
{
|
|
||||||
drawSliderOnGraph(val);
|
|
||||||
for(var i=0; i<numMonitors; i++)
|
|
||||||
{
|
|
||||||
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val));
|
|
||||||
}
|
|
||||||
var currentTimeMS = new Date(val*1000);
|
|
||||||
currentTimeSecs=val;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Found this here: http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element
|
|
||||||
function relMouseCoords(event){
|
|
||||||
var totalOffsetX = 0;
|
|
||||||
var totalOffsetY = 0;
|
|
||||||
var canvasX = 0;
|
|
||||||
var canvasY = 0;
|
|
||||||
var currentElement = this;
|
|
||||||
|
|
||||||
do{
|
|
||||||
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
|
|
||||||
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
|
|
||||||
}
|
|
||||||
while(currentElement = currentElement.offsetParent)
|
|
||||||
|
|
||||||
canvasX = event.pageX - totalOffsetX;
|
|
||||||
canvasY = event.pageY - totalOffsetY;
|
|
||||||
|
|
||||||
return {x:canvasX, y:canvasY}
|
|
||||||
}
|
|
||||||
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
|
|
||||||
|
|
||||||
// These are the functions for mouse movement in the timeline. Note that touch is treated as a mouse move with mouse down
|
|
||||||
|
|
||||||
var mouseisdown=false;
|
|
||||||
function mdown(event) {mouseisdown=true; mmove(event);}
|
|
||||||
function mup(event) {mouseisdown=false;}
|
|
||||||
function mout(event) {mouseisdown=false;} // if we go outside treat it as release
|
|
||||||
function tmove(event) {mouseisdown=true; mmove(event);}
|
|
||||||
|
|
||||||
function mmove(event) {
|
|
||||||
if(mouseisdown) {
|
|
||||||
// only do anything if the mouse is depressed while on the sheet
|
|
||||||
var sec = minTimeSecs + rangeTimeSecs / event.target.width * event.target.relMouseCoords(event).x;
|
|
||||||
outputUpdate(sec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function secs2dbstr (s)
|
|
||||||
{
|
|
||||||
var st = (new Date(s * 1000)).format("%Y-%m-%d %H:%M:%S");
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFit(value)
|
|
||||||
{
|
|
||||||
fitMode=value;
|
|
||||||
redrawScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showScale(newscale) // updates slider only
|
|
||||||
{
|
|
||||||
$('scaleslideroutput').innerHTML = parseFloat(newscale).toFixed(2).toString() + " x";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setScale(newscale) // makes actual change
|
|
||||||
{
|
|
||||||
showScale(newscale);
|
|
||||||
for(var i=0; i<numMonitors; i++)
|
|
||||||
{
|
|
||||||
monitorCanvasObj[monitorPtr[i]].width=monitorWidth[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
|
|
||||||
monitorCanvasObj[monitorPtr[i]].height=monitorHeight[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
|
|
||||||
}
|
|
||||||
currentScale=newscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSpeed(val) // updates slider only
|
|
||||||
{
|
|
||||||
$('speedslideroutput').innerHTML = parseFloat(speeds[val]).toFixed(2).toString() + " x";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSpeed(val) // Note parameter is the index not the speed
|
|
||||||
{
|
|
||||||
var t;
|
|
||||||
if(liveMode==1) return; // we shouldn't actually get here but just in case
|
|
||||||
currentSpeed=parseFloat(speeds[val]);
|
|
||||||
speedIndex=val;
|
|
||||||
playSecsperInterval = currentSpeed * currentDisplayInterval / 1000;
|
|
||||||
showSpeed(val);
|
|
||||||
if( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLive(value)
|
|
||||||
{
|
|
||||||
liveMode=value;
|
|
||||||
redrawScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
|
||||||
// The section below are to reload this program with new parameters
|
|
||||||
|
|
||||||
function clicknav(minSecs,maxSecs,arch,live) // we use the current time if we can
|
|
||||||
{
|
|
||||||
var now = new Date() / 1000;
|
|
||||||
var minStr="";
|
|
||||||
var maxStr="";
|
|
||||||
var currentStr="";
|
|
||||||
if(minSecs>0)
|
|
||||||
{
|
|
||||||
if(maxSecs > now)
|
|
||||||
maxSecs = parseInt(now);
|
|
||||||
maxStr="&maxTime=" + secs2dbstr(maxSecs);
|
|
||||||
}
|
|
||||||
if(maxSecs>0)
|
|
||||||
minStr="&minTime=" + secs2dbstr(minSecs);
|
|
||||||
if(maxSecs==0 && minSecs==0)
|
|
||||||
{
|
|
||||||
minStr="&minTime=01/01/1950 12:00:00";
|
|
||||||
maxStr="&maxTime=12/31/2035 12:00:00";
|
|
||||||
}
|
|
||||||
var intervalStr="&displayinterval=" + currentDisplayInterval.toString();
|
|
||||||
if(minSecs && maxSecs)
|
|
||||||
{
|
|
||||||
if(currentTimeSecs > minSecs && currentTimeSecs < maxSecs) // make sure time is in the new range
|
|
||||||
currentStr="¤t=" + secs2dbstr(currentTimeSecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
var liveStr="&live=0";
|
|
||||||
if(live==1)
|
|
||||||
liveStr="&live=1";
|
|
||||||
|
|
||||||
var fitStr="&fit=0";
|
|
||||||
if(fitMode==1)
|
|
||||||
fitStr="&fit=1";
|
|
||||||
|
|
||||||
var zoomStr="";
|
|
||||||
for(var i=0; i<numMonitors; i++)
|
|
||||||
if(monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01) // allow for some up/down changes and just treat as 1 of almost 1
|
|
||||||
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
|
|
||||||
|
|
||||||
var groupStr=<?php if($group=="") echo '""'; else echo "\"&group=$group\""; ?>;
|
|
||||||
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + document.getElementById("scaleslider").value + "&speed=" + speeds[document.getElementById("speedslider").value];
|
|
||||||
window.location=uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastHour()
|
|
||||||
{
|
|
||||||
var now = new Date() / 1000;
|
|
||||||
clicknav(now - 3600 + 1, now,1,0);
|
|
||||||
}
|
|
||||||
function lastEight()
|
|
||||||
{
|
|
||||||
var now = new Date() / 1000;
|
|
||||||
clicknav(now - 3600*8 + 1, now,1,0);
|
|
||||||
}
|
|
||||||
function zoomin()
|
|
||||||
{
|
|
||||||
rangeTimeSecs = parseInt(rangeTimeSecs / 2);
|
|
||||||
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
|
|
||||||
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
|
|
||||||
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoomout()
|
|
||||||
{
|
|
||||||
rangeTimeSecs = parseInt(rangeTimeSecs * 2);
|
|
||||||
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
|
|
||||||
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
|
|
||||||
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
|
||||||
}
|
|
||||||
function panleft()
|
|
||||||
{
|
|
||||||
minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2);
|
|
||||||
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
|
|
||||||
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
|
||||||
}
|
|
||||||
function panright()
|
|
||||||
{
|
|
||||||
minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2);
|
|
||||||
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
|
|
||||||
clicknav(minTimeSecs,maxTimeSecs,1,0);
|
|
||||||
}
|
|
||||||
function allof()
|
|
||||||
{
|
|
||||||
clicknav(0,0,1,0);
|
|
||||||
}
|
|
||||||
function allnon()
|
|
||||||
{
|
|
||||||
clicknav(0,0,0,0);
|
|
||||||
}
|
|
||||||
/// >>>>>>>>>>>>>>>>> handles packing different size/aspect monitors on screen <<<<<<<<<<<<<<<<<<<<<<<<
|
|
||||||
|
|
||||||
function compSize(a, b) // sort array by some size parameter - height seems to work best. A semi-greedy algorithm
|
|
||||||
{
|
|
||||||
if ( monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a] > monitorHeight[b] * monitorWidth[b] * monitorNormalizeScale[b] * monitorZoomScale[b] * monitorNormalizeScale[b] * monitorZoomScale[b]) return -1;
|
|
||||||
else if ( monitorHeight[a] * monitorWidth[a] * monitorNormalizeScale[a] * monitorZoomScale[a] * monitorNormalizeScale[a] * monitorZoomScale[a] == monitorHeight[b] * monitorWidth[b] * monitorNormalizeScale[b] * monitorZoomScale[b] * monitorNormalizeScale[b] * monitorZoomScale[b]) return 0;
|
|
||||||
else return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function maxfit2(divW, divH)
|
|
||||||
{
|
|
||||||
var bestFitX=[]; // how we arranged the so-far best match
|
|
||||||
var bestFitX2=[];
|
|
||||||
var bestFitY=[];
|
|
||||||
var bestFitY2=[];
|
|
||||||
var bestFitScale;
|
|
||||||
|
|
||||||
var minScale=0.05;
|
|
||||||
var maxScale=5.00;
|
|
||||||
var bestFitArea=0;
|
|
||||||
|
|
||||||
var borders=-1;
|
|
||||||
|
|
||||||
monitorPtr.sort(compSize);
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
if( maxScale - minScale < 0.01 ) break;
|
|
||||||
var thisScale = (maxScale + minScale) / 2;
|
|
||||||
var allFit=1;
|
|
||||||
var thisArea=0;
|
|
||||||
var thisX=[]; // top left
|
|
||||||
var thisY=[];
|
|
||||||
var thisX2=[]; // bottom right
|
|
||||||
var thisY2=[];
|
|
||||||
|
|
||||||
for(var m=0; m<numMonitors; m++)
|
|
||||||
{
|
|
||||||
// this loop places each monitor (if it can)
|
|
||||||
|
|
||||||
function doesItFit(x,y,w,h,d)
|
|
||||||
{ // does block (w,h) fit at position (x,y) relative to edge and other nodes already done (0..d)
|
|
||||||
if(x+w>=divW) return 0;
|
|
||||||
if(y+h>=divH) return 0;
|
|
||||||
for(var i=0; i<=d; i++)
|
|
||||||
if( !( thisX[i]>x+w-1 || thisX2[i] < x || thisY[i] > y+h-1 || thisY2[i] < y ) ) return 0;
|
|
||||||
return 1; // it's OK
|
|
||||||
}
|
|
||||||
|
|
||||||
if(borders<=0) borders=$("Monitor"+monitorPtr[m]).getStyle("border").toInt() * 2; // assume fixed size border, and added to both sides and top/bottom
|
|
||||||
// try fitting over first, then down. Each new one must land at either upper right or lower left corner of last (try in that order)
|
|
||||||
// Pick the one with the smallest Y, then smallest X if Y equal
|
|
||||||
var fitX = 999999999;
|
|
||||||
var fitY = 999999999;
|
|
||||||
for( adjacent=0; adjacent<m; adjacent++)
|
|
||||||
{
|
|
||||||
// try top right of adjacent
|
|
||||||
if( doesItFit(thisX2[adjacent]+1, thisY[adjacent], monitorWidth[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, monitorHeight[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, m-1) == 1 )
|
|
||||||
{
|
|
||||||
if(thisY[adjacent]<fitY || ( thisY[adjacent]==fitY && thisX2[adjacent]+1 < fitX ))
|
|
||||||
{
|
|
||||||
fitX=thisX2[adjacent]+1;
|
|
||||||
fitY=thisY[adjacent];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// try bottom left
|
|
||||||
if ( doesItFit(thisX[adjacent], thisY2[adjacent]+1, monitorWidth[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, monitorHeight[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, m-1) == 1 )
|
|
||||||
{
|
|
||||||
if(thisY2[adjacent]+1<fitY || ( thisY2[adjacent]+1 == fitY && thisX[adjacent]<fitX ))
|
|
||||||
{
|
|
||||||
fitX=thisX[adjacent];
|
|
||||||
fitY=thisY2[adjacent]+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(m==0) // note for teh very first one there were no adjacents so the above loop didn't run
|
|
||||||
{
|
|
||||||
if( doesItFit(0,0,monitorWidth[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, monitorHeight[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders, -1) == 1 )
|
|
||||||
{
|
|
||||||
fitX=0;
|
|
||||||
fitY=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(fitX==999999999)
|
|
||||||
{
|
|
||||||
allFit=0;
|
|
||||||
break; // break out of monitor loop flagging we didn't fit
|
|
||||||
}
|
|
||||||
thisX[m] =fitX;
|
|
||||||
thisX2[m]=fitX + monitorWidth[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders;
|
|
||||||
thisY[m] =fitY;
|
|
||||||
thisY2[m]=fitY + monitorHeight[monitorPtr[m]] * thisScale * monitorNormalizeScale[monitorPtr[m]] * monitorZoomScale[monitorPtr[m]] + borders;
|
|
||||||
thisArea += (thisX2[m] - thisX[m])*(thisY2[m] - thisY[m]);
|
|
||||||
}
|
|
||||||
if(allFit==1)
|
|
||||||
{
|
|
||||||
minScale=thisScale;
|
|
||||||
if(bestFitArea<thisArea)
|
|
||||||
{
|
|
||||||
bestFitArea=thisArea;
|
|
||||||
bestFitX=thisX;
|
|
||||||
bestFitY=thisY;
|
|
||||||
bestFitX2=thisX2;
|
|
||||||
bestFitY2=thisY2;
|
|
||||||
bestFitScale=thisScale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // didn't fit
|
|
||||||
{
|
|
||||||
maxScale=thisScale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(bestFitArea>0) // only rearrange if we could fit -- otherwise just do nothing, let them start coming out, whatever
|
|
||||||
{
|
|
||||||
for(m=0; m<numMonitors; m++)
|
|
||||||
{
|
|
||||||
c = $("Monitor" + monitorPtr[m]);
|
|
||||||
c.style.position="absolute";
|
|
||||||
c.style.left=bestFitX[m].toString() + "px";
|
|
||||||
c.style.top=bestFitY[m].toString() + "px";
|
|
||||||
c.width = bestFitX2[m] - bestFitX[m] + 1 - borders;
|
|
||||||
c.height= bestFitY2[m] - bestFitY[m] + 1 - borders;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// >>>>>>>>>>>>>>>> Handles individual monitor clicks and navigation to the standard event/watch display
|
|
||||||
|
|
||||||
function showOneMonitor(monId) // link out to the normal view of one event's data
|
|
||||||
{
|
|
||||||
// We know the monitor, need to determine the event based on current time
|
|
||||||
var url;
|
|
||||||
if(liveMode!=0) url="?view=watch&mid=" + monId.toString();
|
|
||||||
else
|
|
||||||
for(var i=0; i<eId.length; i++)
|
|
||||||
if(eMonId[i]==monId && currentTimeSecs >= eStartSecs[i] && currentTimeSecs <= eEndSecs[i])
|
|
||||||
url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) ));
|
|
||||||
createPopup(url, 'zmEvent', 'event', monitorWidth[eMonId[i]], monitorHeight[eMonId[i]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoom(monId,scale)
|
|
||||||
{
|
|
||||||
var lastZoomMonPriorScale=monitorZoomScale[monId];
|
|
||||||
monitorZoomScale[monId] *= scale;
|
|
||||||
if(redrawScreen()==0) // failure here is probably because we zoomed too far
|
|
||||||
{
|
|
||||||
monitorZoomScale[monId]=lastZoomMonPriorScale;
|
|
||||||
alert("You can't zoom that far -- rolling back");
|
|
||||||
redrawScreen(); // put things back and hope it works
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickMonitor(event,monId)
|
|
||||||
{
|
|
||||||
var pos_x = event.offsetX ? (event.offsetX) : event.pageX - $("Monitor"+monId.toString()).offsetLeft;
|
|
||||||
var pos_y = event.offsetY ? (event.offsetY) : event.pageY - $("Monitor"+monId.toString()).offsetTop;
|
|
||||||
if(pos_x < $("Monitor"+monId.toString()).width/4 && pos_y < $("Monitor"+monId.toString()).height/4) zoom(monId,1.15);
|
|
||||||
else if(pos_x > $("Monitor"+monId.toString()).width * 3/4 && pos_y < $("Monitor"+monId.toString()).height/4) zoom(monId,1/1.15);
|
|
||||||
else showOneMonitor(monId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// >>>>>>>>> Initialization that runs on window load by being at the bottom
|
|
||||||
|
|
||||||
drawGraph();
|
|
||||||
setSpeed(speedIndex);
|
|
||||||
setFit(fitMode); // will redraw
|
|
||||||
setLive(liveMode); // will redraw
|
|
||||||
window.addEventListener("resize",redrawScreen);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -34,8 +34,7 @@ require_once( $skinJsPhpFile );
|
||||||
<script type="text/javascript" src="<?php echo $skinJsFile ?>"></script>
|
<script type="text/javascript" src="<?php echo $skinJsFile ?>"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<?php
|
<?php
|
||||||
if ( !$debug )
|
if ( !$debug ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
closeWindow();
|
closeWindow();
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -18,9 +18,8 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canEdit( 'Monitors' ) )
|
if ( !canEdit( 'Monitors' ) ) {
|
||||||
{
|
$view = 'error';
|
||||||
$view = "error";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,70 +40,59 @@ function execONVIF( $cmd ) {
|
||||||
$html_output<br/><br/>
|
$html_output<br/><br/>
|
||||||
Please the following command from a command line for more information:<br/><br/>$shell_command"
|
Please the following command from a command line for more information:<br/><br/>$shell_command"
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
Logger::Debug( "Results from probe: " . implode( '<br/>', $output ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function probeCameras( $localIp )
|
function probeCameras( $localIp ) {
|
||||||
{
|
|
||||||
$cameras = array();
|
$cameras = array();
|
||||||
$count = 0;
|
if ( $lines = @execONVIF( 'probe' ) ) {
|
||||||
if ( $lines = @execONVIF( "probe" ) )
|
foreach ( $lines as $line ) {
|
||||||
{
|
|
||||||
foreach ( $lines as $line )
|
|
||||||
{
|
|
||||||
$line = rtrim( $line );
|
$line = rtrim( $line );
|
||||||
if ( preg_match( '|^(.+),(.+),\s\((.*)\)$|', $line, $matches ) )
|
if ( preg_match( '|^(.+),(.+),\s\((.*)\)$|', $line, $matches ) ) {
|
||||||
{
|
|
||||||
$device_ep = $matches[1];
|
$device_ep = $matches[1];
|
||||||
$soapversion = $matches[2];
|
$soapversion = $matches[2];
|
||||||
$camera = array(
|
$camera = array(
|
||||||
'model' => "Unknown ONVIF Camera",
|
'model' => 'Unknown ONVIF Camera',
|
||||||
'monitor' => array(
|
'monitor' => array(
|
||||||
'Function' => "Monitor",
|
'Function' => 'Monitor',
|
||||||
'Type' => 'Ffmpeg',
|
'Type' => 'Ffmpeg',
|
||||||
'Host' => $device_ep,
|
'Host' => $device_ep,
|
||||||
'SOAP' => $soapversion,
|
'SOAP' => $soapversion,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
foreach ( preg_split('|,\s*|', $matches[3]) as $attr_val ) {
|
foreach ( preg_split('|,\s*|', $matches[3]) as $attr_val ) {
|
||||||
if( preg_match( '|(.+)=\'(.*)\'|', $attr_val, $tokens ) )
|
if ( preg_match( '|(.+)=\'(.*)\'|', $attr_val, $tokens ) ) {
|
||||||
{
|
if ( $tokens[1] == 'hardware' ) {
|
||||||
if($tokens[1] == "hardware") {
|
|
||||||
$camera['model'] = $tokens[2];
|
$camera['model'] = $tokens[2];
|
||||||
}
|
} elseif ( $tokens[1] == 'name' ) {
|
||||||
elseif($tokens[1] == "name") {
|
|
||||||
$camera['monitor']['Name'] = $tokens[2];
|
$camera['monitor']['Name'] = $tokens[2];
|
||||||
}
|
} elseif ( $tokens[1] == 'location' ) {
|
||||||
elseif($tokens[1] == "location") {
|
|
||||||
// $camera['location'] = $tokens[2];
|
// $camera['location'] = $tokens[2];
|
||||||
}
|
} else {
|
||||||
|
Logger::Debug('Unknown token ' . $tokens[1] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$cameras[$count ++] = $camera;
|
} // end foreach token
|
||||||
}
|
$cameras[] = $camera;
|
||||||
}
|
}
|
||||||
|
} // end foreach line
|
||||||
}
|
}
|
||||||
return( $cameras );
|
return( $cameras );
|
||||||
}
|
}
|
||||||
|
|
||||||
function probeProfiles( $device_ep, $soapversion, $username, $password )
|
function probeProfiles( $device_ep, $soapversion, $username, $password ) {
|
||||||
{
|
|
||||||
$profiles = array();
|
$profiles = array();
|
||||||
$count = 0;
|
if ( $lines = @execONVIF( "profiles $device_ep $soapversion $username $password" ) ) {
|
||||||
if ( $lines = @execONVIF( "profiles $device_ep $soapversion $username $password" ) )
|
foreach ( $lines as $line ) {
|
||||||
{
|
|
||||||
foreach ( $lines as $line )
|
|
||||||
{
|
|
||||||
$line = rtrim( $line );
|
$line = rtrim( $line );
|
||||||
if ( preg_match( '|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches ) )
|
if ( preg_match( '|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches ) ) {
|
||||||
{
|
|
||||||
$stream_uri = $matches[7];
|
$stream_uri = $matches[7];
|
||||||
// add user@pass to URI
|
// add user@pass to URI
|
||||||
if( preg_match( '|^(\S+://)(.+)$|', $stream_uri, $tokens ) )
|
if ( preg_match( '|^(\S+://)(.+)$|', $stream_uri, $tokens ) ) {
|
||||||
{
|
|
||||||
$stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2];
|
$stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +108,9 @@ function probeProfiles( $device_ep, $soapversion, $username, $password )
|
||||||
'Encoding' => $matches[3],
|
'Encoding' => $matches[3],
|
||||||
|
|
||||||
);
|
);
|
||||||
$profiles[$count ++] = $profile;
|
$profiles[] = $profile;
|
||||||
|
} else {
|
||||||
|
Logger::Debug("Line did not match preg: $line");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,25 +127,19 @@ xhtmlHeaders(__FILE__, translate('MonitorProbe') );
|
||||||
if( !isset($_REQUEST['step']) || ($_REQUEST['step'] == "1")) {
|
if( !isset($_REQUEST['step']) || ($_REQUEST['step'] == "1")) {
|
||||||
|
|
||||||
$monitors = array();
|
$monitors = array();
|
||||||
foreach ( dbFetchAll( "select Id, Name, Host from Monitors where Type = 'Remote' order by Host" ) as $monitor )
|
foreach ( dbFetchAll( "select Id, Name, Host from Monitors where Type = 'Remote' order by Host" ) as $monitor ) {
|
||||||
{
|
if ( preg_match( '/^(.+)@(.+)$/', $monitor['Host'], $matches ) ) {
|
||||||
if ( preg_match( '/^(.+)@(.+)$/', $monitor['Host'], $matches ) )
|
|
||||||
{
|
|
||||||
//echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."<br/>";
|
//echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."<br/>";
|
||||||
$monitors[gethostbyname($matches[2])] = $monitor;
|
$monitors[gethostbyname($matches[2])] = $monitor;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
//echo "2: ".$monitor['Host']." = ".gethostbyname($monitor['Host'])."<br/>";
|
//echo "2: ".$monitor['Host']." = ".gethostbyname($monitor['Host'])."<br/>";
|
||||||
$monitors[gethostbyname($monitor['Host'])] = $monitor;
|
$monitors[gethostbyname($monitor['Host'])] = $monitor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$detcameras = probeCameras( '' );
|
$detcameras = probeCameras( '' );
|
||||||
foreach ( $detcameras as $camera )
|
foreach ( $detcameras as $camera ) {
|
||||||
{
|
if ( preg_match( '|([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|', $camera['monitor']['Host'], $matches ) ) {
|
||||||
if ( preg_match( '|([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|', $camera['monitor']['Host'], $matches ) )
|
|
||||||
{
|
|
||||||
$ip = $matches[1];
|
$ip = $matches[1];
|
||||||
}
|
}
|
||||||
$host = $ip;
|
$host = $ip;
|
||||||
|
@ -230,10 +214,8 @@ else if($_REQUEST['step'] == "2")
|
||||||
#empty($_REQUEST['password']) )
|
#empty($_REQUEST['password']) )
|
||||||
|
|
||||||
$probe = unserialize(base64_decode($_REQUEST['probe']));
|
$probe = unserialize(base64_decode($_REQUEST['probe']));
|
||||||
foreach ( $probe as $name=>$value )
|
foreach ( $probe as $name=>$value ) {
|
||||||
{
|
if ( isset($value) ) {
|
||||||
if ( isset($value) )
|
|
||||||
{
|
|
||||||
$monitor[$name] = $value;
|
$monitor[$name] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,8 +224,7 @@ else if($_REQUEST['step'] == "2")
|
||||||
//print $monitor['Host'].", ".$_REQUEST['username'].", ".$_REQUEST['password']."<br/>";
|
//print $monitor['Host'].", ".$_REQUEST['username'].", ".$_REQUEST['password']."<br/>";
|
||||||
|
|
||||||
$detprofiles = probeProfiles( $monitor['Host'], $monitor['SOAP'], $_REQUEST['username'], $_REQUEST['password']);
|
$detprofiles = probeProfiles( $monitor['Host'], $monitor['SOAP'], $_REQUEST['username'], $_REQUEST['password']);
|
||||||
foreach ( $detprofiles as $profile )
|
foreach ( $detprofiles as $profile ) {
|
||||||
{
|
|
||||||
$monitor = $camera['monitor'];
|
$monitor = $camera['monitor'];
|
||||||
|
|
||||||
$sourceString = "${profile['Name']} : ${profile['Encoding']}" .
|
$sourceString = "${profile['Name']} : ${profile['Encoding']}" .
|
||||||
|
|
|
@ -18,14 +18,13 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canEdit( 'System' ) )
|
if ( !canEdit( 'System' ) ) {
|
||||||
{
|
$view = 'error';
|
||||||
$view = "error";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$running = daemonCheck();
|
$running = daemonCheck();
|
||||||
|
|
||||||
$states = dbFetchAll( "select * from States" );
|
$states = dbFetchAll( 'SELECT * FROM States' );
|
||||||
$focusWindow = true;
|
$focusWindow = true;
|
||||||
|
|
||||||
xhtmlHeaders(__FILE__, translate('RunState') );
|
xhtmlHeaders(__FILE__, translate('RunState') );
|
||||||
|
@ -36,10 +35,9 @@ xhtmlHeaders(__FILE__, translate('RunState') );
|
||||||
<h2><?php echo translate('RunState') ?></h2>
|
<h2><?php echo translate('RunState') ?></h2>
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<form name="contentForm" id="contentForm" method="get" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
||||||
<?php
|
<?php
|
||||||
if ( empty($_REQUEST['apply']) )
|
if ( empty($_REQUEST['apply']) ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
||||||
<input type="hidden" name="action" value=""/>
|
<input type="hidden" name="action" value=""/>
|
||||||
|
@ -47,23 +45,19 @@ if ( empty($_REQUEST['apply']) )
|
||||||
<p>
|
<p>
|
||||||
<select name="runState" onchange="checkState(this);">
|
<select name="runState" onchange="checkState(this);">
|
||||||
<?php
|
<?php
|
||||||
if ( $running )
|
if ( $running ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<option value="stop" selected="selected"><?php echo translate('Stop') ?></option>
|
<option value="stop" selected="selected"><?php echo translate('Stop') ?></option>
|
||||||
<option value="restart"><?php echo translate('Restart') ?></option>
|
<option value="restart"><?php echo translate('Restart') ?></option>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<option value="start" selected="selected"><?php echo translate('Start') ?></option>
|
<option value="start" selected="selected"><?php echo translate('Start') ?></option>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<?php
|
<?php
|
||||||
foreach ( $states as $state )
|
foreach ( $states as $state ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<option value="<?php echo $state['Name'] ?>"><?php echo $state['Name'] ?></option>
|
<option value="<?php echo $state['Name'] ?>"><?php echo $state['Name'] ?></option>
|
||||||
<?php
|
<?php
|
||||||
|
@ -75,8 +69,6 @@ if ( empty($_REQUEST['apply']) )
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row"><?php echo translate('NewState') ?></th>
|
<th scope="row"><?php echo translate('NewState') ?></th>
|
||||||
<!-- PP - added oninput so that changes are detected immediately -->
|
|
||||||
<!-- PP - retained onchange for older browsers -->
|
|
||||||
<td><input type="text" name="newState" value="" size="16" oninput="checkState(this);" onchange="checkState(this);"/></td>
|
<td><input type="text" name="newState" value="" size="16" oninput="checkState(this);" onchange="checkState(this);"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -88,9 +80,7 @@ if ( empty($_REQUEST['apply']) )
|
||||||
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
|
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="view" value="none"/>
|
<input type="hidden" name="view" value="none"/>
|
||||||
<input type="hidden" name="action" value="state"/>
|
<input type="hidden" name="action" value="state"/>
|
||||||
|
|
|
@ -18,12 +18,13 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canView( 'Events' ) )
|
if ( !canView( 'Events' ) ) {
|
||||||
{
|
|
||||||
$view = "error";
|
$view = "error";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_once('includes/Event.php');
|
||||||
|
|
||||||
$eid = validInt($_REQUEST['eid']);
|
$eid = validInt($_REQUEST['eid']);
|
||||||
|
|
||||||
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?';
|
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?';
|
||||||
|
@ -45,36 +46,28 @@ if ( isset( $_REQUEST['scale'] ) )
|
||||||
else
|
else
|
||||||
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
$scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE );
|
||||||
|
|
||||||
$eventPath = ZM_DIR_EVENTS.'/'.getEventPath( $event );
|
$Event = new Event( $event['Id'] );
|
||||||
|
$eventPath = $Event->Path();
|
||||||
|
|
||||||
$videoFormats = array();
|
$videoFormats = array();
|
||||||
$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS );
|
$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS );
|
||||||
foreach ( $ffmpegFormats as $ffmpegFormat )
|
foreach ( $ffmpegFormats as $ffmpegFormat ) {
|
||||||
{
|
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) {
|
||||||
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) )
|
|
||||||
{
|
|
||||||
$videoFormats[$matches[1]] = $matches[1];
|
$videoFormats[$matches[1]] = $matches[1];
|
||||||
if ( !isset($videoFormat) && $matches[2] == "*" )
|
if ( !isset($videoFormat) && $matches[2] == '*' ) {
|
||||||
{
|
|
||||||
$videoFormat = $matches[1];
|
$videoFormat = $matches[1];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$videoFiles = array();
|
$videoFiles = array();
|
||||||
if ( $dir = opendir( $eventPath ) )
|
if ( $dir = opendir( $eventPath ) ) {
|
||||||
{
|
while ( ($file = readdir( $dir )) !== false ) {
|
||||||
while ( ($file = readdir( $dir )) !== false )
|
|
||||||
{
|
|
||||||
$file = $eventPath.'/'.$file;
|
$file = $eventPath.'/'.$file;
|
||||||
if ( is_file( $file ) )
|
if ( is_file( $file ) ) {
|
||||||
{
|
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) {
|
||||||
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) )
|
|
||||||
{
|
|
||||||
$videoFiles[] = $file;
|
$videoFiles[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,15 +75,13 @@ if ( $dir = opendir( $eventPath ) )
|
||||||
closedir( $dir );
|
closedir( $dir );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($_REQUEST['deleteIndex']) )
|
if ( isset($_REQUEST['deleteIndex']) ) {
|
||||||
{
|
|
||||||
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
||||||
unlink( $videoFiles[$deleteIndex] );
|
unlink( $videoFiles[$deleteIndex] );
|
||||||
unset( $videoFiles[$deleteIndex] );
|
unset( $videoFiles[$deleteIndex] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($_REQUEST['downloadIndex']) )
|
if ( isset($_REQUEST['downloadIndex']) ) {
|
||||||
{
|
|
||||||
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
||||||
header( "Pragma: public" );
|
header( "Pragma: public" );
|
||||||
header( "Expires: 0" );
|
header( "Expires: 0" );
|
||||||
|
@ -119,8 +110,7 @@ xhtmlHeaders(__FILE__, translate('Video') );
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<?php
|
<?php
|
||||||
if ( isset($_REQUEST['showIndex']) )
|
if ( isset($_REQUEST['showIndex']) ) {
|
||||||
{
|
|
||||||
$showIndex = validInt($_REQUEST['showIndex']);
|
$showIndex = validInt($_REQUEST['showIndex']);
|
||||||
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
||||||
$name = $matches[1];
|
$name = $matches[1];
|
||||||
|
@ -129,9 +119,7 @@ if ( isset($_REQUEST['showIndex']) )
|
||||||
<h3 id="videoFile"><?php echo substr( $videoFiles[$showIndex], strlen(ZM_DIR_EVENTS)+1 ) ?></h3>
|
<h3 id="videoFile"><?php echo substr( $videoFiles[$showIndex], strlen(ZM_DIR_EVENTS)+1 ) ?></h3>
|
||||||
<div id="imageFeed"><?php outputVideoStream( 'videoStream', $videoFiles[$showIndex], validInt($_REQUEST['width']), validInt($_REQUEST['height']), $videoFormat, $name ) ?></div>
|
<div id="imageFeed"><?php outputVideoStream( 'videoStream', $videoFiles[$showIndex], validInt($_REQUEST['width']), validInt($_REQUEST['height']), $videoFormat, $name ) ?></div>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
||||||
<input type="hidden" name="id" value="<?php echo $event['Id'] ?>"/>
|
<input type="hidden" name="id" value="<?php echo $event['Id'] ?>"/>
|
||||||
|
@ -158,14 +146,11 @@ else
|
||||||
<input type="button" value="<?php echo translate('GenerateVideo') ?>" onclick="generateVideo( this.form );"<?php if ( !ZM_OPT_FFMPEG ) { ?> disabled="disabled"<?php } ?>/>
|
<input type="button" value="<?php echo translate('GenerateVideo') ?>" onclick="generateVideo( this.form );"<?php if ( !ZM_OPT_FFMPEG ) { ?> disabled="disabled"<?php } ?>/>
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
if ( isset($_REQUEST['generated']) )
|
if ( isset($_REQUEST['generated']) ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h2 id="videoProgress" class="<?php echo $_REQUEST['generated']?'infoText':'errorText' ?>"><span id="videoProgressText"><?php echo $_REQUEST['generated']?translate('VideoGenSucceeded'):translate('VideoGenFailed') ?></span><span id="videoProgressTicker"></span></h2>
|
<h2 id="videoProgress" class="<?php echo $_REQUEST['generated']?'infoText':'errorText' ?>"><span id="videoProgressText"><?php echo $_REQUEST['generated']?translate('VideoGenSucceeded'):translate('VideoGenFailed') ?></span><span id="videoProgressTicker"></span></h2>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h2 id="videoProgress" class="hidden warnText"><span id="videoProgressText"><?php echo translate('GeneratingVideo') ?></span><span id="videoProgressTicker"></span></h2>
|
<h2 id="videoProgress" class="hidden warnText"><span id="videoProgressText"><?php echo translate('GeneratingVideo') ?></span><span id="videoProgressTicker"></span></h2>
|
||||||
<?php
|
<?php
|
||||||
|
@ -173,14 +158,11 @@ else
|
||||||
?>
|
?>
|
||||||
<h2 id="videoFilesHeader"><?php echo translate('VideoGenFiles') ?></h2>
|
<h2 id="videoFilesHeader"><?php echo translate('VideoGenFiles') ?></h2>
|
||||||
<?php
|
<?php
|
||||||
if ( count($videoFiles) == 0 )
|
if ( count($videoFiles) == 0 ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h3 id="videoNoFiles"><?php echo translate('VideoGenNoFiles') ?></h3>
|
<h3 id="videoNoFiles"><?php echo translate('VideoGenNoFiles') ?></h3>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<table id="videoTable" class="major" cellspacing="0">
|
<table id="videoTable" class="major" cellspacing="0">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -195,27 +177,19 @@ else
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ( $videoFiles as $file )
|
foreach ( $videoFiles as $file ) {
|
||||||
{
|
if ( filesize( $file ) > 0 ) {
|
||||||
if ( filesize( $file ) > 0 )
|
|
||||||
{
|
|
||||||
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
||||||
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) )
|
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) {
|
||||||
{
|
|
||||||
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||||
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
||||||
}
|
} elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) {
|
||||||
elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) )
|
|
||||||
{
|
|
||||||
$rateText = $temp_matches[1]."fps";
|
$rateText = $temp_matches[1]."fps";
|
||||||
}
|
}
|
||||||
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) )
|
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) {
|
||||||
{
|
|
||||||
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||||
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
||||||
}
|
} elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) {
|
||||||
elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) )
|
|
||||||
{
|
|
||||||
$scaleText = $temp_matches[1];
|
$scaleText = $temp_matches[1];
|
||||||
}
|
}
|
||||||
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
// Calling sequence: ... /zm/index.php?view=video&event_id=123
|
// Calling sequence: ... /zm/index.php?view=video&event_id=123
|
||||||
|
|
57
zm.conf.in
57
zm.conf.in
|
@ -1,15 +1,14 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Base Configuration File
|
# ZoneMinder Base Configuration
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# *** DO NOT EDIT THIS FILE ***
|
# *** DO NOT EDIT THIS FILE ***
|
||||||
# Changes made directly to this configuration file are no longer supported.
|
|
||||||
# They will be overwritten during an upgrade.
|
|
||||||
#
|
#
|
||||||
# Instead, create a custom configuration file, with an extention of ".conf"
|
# To make custom changes to the variables below, create a new configuration
|
||||||
# under the @ZM_CONFIG_SUBDIR@ subfolder containing your desired modifications.
|
# file, with an extention of .conf, under the @ZM_CONFIG_SUBDIR@
|
||||||
|
# folder, containing your desired modifications.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Path to installed data directory, used mostly for finding DB upgrade scripts
|
# Path to installed data directory, used mostly for finding DB upgrade scripts
|
||||||
|
@ -50,43 +49,19 @@ ZM_DB_USER=@ZM_DB_USER@
|
||||||
# ZoneMinder database password
|
# ZoneMinder database password
|
||||||
ZM_DB_PASS=@ZM_DB_PASS@
|
ZM_DB_PASS=@ZM_DB_PASS@
|
||||||
|
|
||||||
# Full path to the folder events are recorded to.
|
# SSL CA certificate for ZoneMinder database
|
||||||
# The web account user must have full read/write permission to this folder.
|
ZM_DB_SSL_CA_CERT=@ZM_DB_SSL_CA_CERT@
|
||||||
ZM_DIR_EVENTS=@ZM_DIR_EVENTS@
|
|
||||||
|
|
||||||
# Full path to the folder images, not directly associated with events,
|
# SSL client key for ZoneMinder database
|
||||||
# are recorded to.
|
ZM_DB_SSL_CLIENT_KEY=@ZM_DB_SSL_CLIENT_KEY@
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_DIR_IMAGES=@ZM_DIR_IMAGES@
|
|
||||||
|
|
||||||
# Foldername under the webroot where ZoneMinder looks for optional sound files
|
# SSL client cert for ZoneMinder database
|
||||||
# to play when an alarm is detected.
|
ZM_DB_SSL_CLIENT_CERT=@ZM_DB_SSL_CLIENT_CERT@
|
||||||
ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@
|
|
||||||
|
|
||||||
# Full path to the folder where exported archives are stored
|
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
||||||
# The web account user must have full read/write permission to this folder.
|
# You have been warned
|
||||||
ZM_DIR_EXPORTS=@ZM_TMPDIR@
|
#
|
||||||
|
# The name specified here must have a corresponding entry
|
||||||
# ZoneMinder url path to the zms streaming server
|
# in the Servers tab under Options
|
||||||
ZM_PATH_ZMS=@ZM_PATH_ZMS@
|
ZM_SERVER_HOST=
|
||||||
|
|
||||||
# Full Path to ZoneMinder's mapped memory files
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_MAP=@ZM_PATH_MAP@
|
|
||||||
|
|
||||||
# Full Path to ZoneMinder's socket folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_SOCKS=@ZM_SOCKDIR@
|
|
||||||
|
|
||||||
# Full path to ZoneMinder's log folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_LOGS=@ZM_LOGDIR@
|
|
||||||
|
|
||||||
# Full path to ZoneMinder's swap folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_SWAP=@ZM_TMPDIR@
|
|
||||||
|
|
||||||
# Full path to optional arp binary
|
|
||||||
# ZoneMinder will find the arp binary automatically on most systems
|
|
||||||
ZM_PATH_ARP=@ZM_PATH_ARP@
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue