Merge branch 'release-1.34' into fix_zms
This commit is contained in:
commit
ae9a5766f5
|
@ -0,0 +1,12 @@
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: zoneminder # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
31
.travis.yml
31
.travis.yml
|
@ -13,7 +13,7 @@ addons:
|
||||||
ssh_known_hosts: zmrepo.zoneminder.com
|
ssh_known_hosts: zmrepo.zoneminder.com
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
- sourceline: ppa:iconnor/zoneminder
|
- sourceline: ppa:iconnor/zoneminder-master
|
||||||
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
|
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
|
||||||
packages:
|
packages:
|
||||||
- gdebi
|
- gdebi
|
||||||
|
@ -33,24 +33,23 @@ install:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- SMPFLAGS=-j4 OS=el DIST=7
|
- SMPFLAGS=-j4 OS=el DIST=7
|
||||||
- SMPFLAGS=-j4 OS=el DIST=8
|
- SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack
|
||||||
- SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack
|
|
||||||
- SMPFLAGS=-j4 OS=fedora DIST=30
|
- SMPFLAGS=-j4 OS=fedora DIST=30
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=fedora DIST=31
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
|
||||||
|
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386
|
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386
|
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic ARCH=i386
|
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386
|
- SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
|
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386
|
- SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
|
- SMPFLAGS=-j4 OS=eslint DIST=eslint
|
||||||
|
|
||||||
compiler:
|
compiler:
|
||||||
- gcc
|
- gcc
|
||||||
|
@ -58,12 +57,6 @@ services:
|
||||||
- mysql
|
- mysql
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- name: eslint
|
|
||||||
install: npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5
|
|
||||||
script: eslint --ext .php,.js .
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- utils/packpack/startpackpack.sh
|
- utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
|
106
CMakeLists.txt
106
CMakeLists.txt
|
@ -138,9 +138,9 @@ set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH
|
||||||
"Location of temporary files, default: /tmp/zm")
|
"Location of temporary files, default: /tmp/zm")
|
||||||
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
|
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
|
||||||
"Location of generated log files, default: /var/log/zm")
|
"Location of generated log files, default: /var/log/zm")
|
||||||
set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH
|
set(ZM_WEBDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/www" CACHE PATH
|
||||||
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
|
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
|
||||||
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
|
set(ZM_CGIDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
|
||||||
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
|
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
|
||||||
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
||||||
"Location of the web server cache busting files, default: /var/cache/zoneminder")
|
"Location of the web server cache busting files, default: /var/cache/zoneminder")
|
||||||
|
@ -347,19 +347,50 @@ else(JPEG_FOUND)
|
||||||
"ZoneMinder requires jpeg but it was not found on your system")
|
"ZoneMinder requires jpeg but it was not found on your system")
|
||||||
endif(JPEG_FOUND)
|
endif(JPEG_FOUND)
|
||||||
|
|
||||||
|
# LIBJWT
|
||||||
|
find_package(LibJWT)
|
||||||
|
if(LIBJWT_FOUND)
|
||||||
|
set(HAVE_LIBJWT 1)
|
||||||
|
set(optlibsfound "${optlibsfound} LIBJWT")
|
||||||
|
list(APPEND ZM_BIN_LIBS "${LIBJWT_LIBRARY}")
|
||||||
|
else(LIBJWT_FOUND)
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} LIBJWT")
|
||||||
|
endif(LIBJWT_FOUND)
|
||||||
|
|
||||||
|
# gnutls (using find_library and find_path)
|
||||||
|
if(HAVE_LIBJWT)
|
||||||
|
find_library(GNUTLS_LIBRARIES gnutls)
|
||||||
|
if(GNUTLS_LIBRARIES)
|
||||||
|
set(HAVE_LIBGNUTLS 1)
|
||||||
|
list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}")
|
||||||
|
find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h)
|
||||||
|
if(GNUTLS_INCLUDE_DIR)
|
||||||
|
include_directories("${GNUTLS_INCLUDE_DIR}")
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
||||||
|
endif(GNUTLS_INCLUDE_DIR)
|
||||||
|
mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR)
|
||||||
|
check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H)
|
||||||
|
set(optlibsfound "${optlibsfound} GnuTLS")
|
||||||
|
else(GNUTLS_LIBRARIES)
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} GnuTLS")
|
||||||
|
endif(GNUTLS_LIBRARIES)
|
||||||
|
endif(HAVE_LIBJWT)
|
||||||
|
|
||||||
# OpenSSL
|
# OpenSSL
|
||||||
find_package(OpenSSL)
|
if(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT)
|
||||||
if(OPENSSL_FOUND)
|
find_package(OpenSSL)
|
||||||
set(HAVE_LIBOPENSSL 1)
|
if(OPENSSL_FOUND)
|
||||||
set(HAVE_LIBCRYPTO 1)
|
set(HAVE_LIBOPENSSL 1)
|
||||||
list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}")
|
set(HAVE_LIBCRYPTO 1)
|
||||||
include_directories("${OPENSSL_INCLUDE_DIR}")
|
list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}")
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
|
include_directories("${OPENSSL_INCLUDE_DIR}")
|
||||||
check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H)
|
set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
|
||||||
set(optlibsfound "${optlibsfound} OpenSSL")
|
check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H)
|
||||||
else(OPENSSL_FOUND)
|
set(optlibsfound "${optlibsfound} OpenSSL")
|
||||||
set(optlibsnotfound "${optlibsnotfound} OpenSSL")
|
else(OPENSSL_FOUND)
|
||||||
endif(OPENSSL_FOUND)
|
set(optlibsnotfound "${optlibsnotfound} OpenSSL")
|
||||||
|
endif(OPENSSL_FOUND)
|
||||||
|
endif(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT)
|
||||||
|
|
||||||
# pthread (using find_library and find_path)
|
# pthread (using find_library and find_path)
|
||||||
find_library(PTHREAD_LIBRARIES pthread)
|
find_library(PTHREAD_LIBRARIES pthread)
|
||||||
|
@ -416,28 +447,6 @@ else(GCRYPT_LIBRARIES)
|
||||||
set(optlibsnotfound "${optlibsnotfound} GCrypt")
|
set(optlibsnotfound "${optlibsnotfound} GCrypt")
|
||||||
endif(GCRYPT_LIBRARIES)
|
endif(GCRYPT_LIBRARIES)
|
||||||
|
|
||||||
# gnutls (using find_library and find_path)
|
|
||||||
find_library(GNUTLS_LIBRARIES gnutls-openssl)
|
|
||||||
if(NOT GNUTLS_LIBRARIES)
|
|
||||||
find_library(GNUTLS_LIBRARIES gnutls)
|
|
||||||
endif(NOT GNUTLS_LIBRARIES)
|
|
||||||
|
|
||||||
if(GNUTLS_LIBRARIES)
|
|
||||||
set(HAVE_LIBGNUTLS 1)
|
|
||||||
list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}")
|
|
||||||
find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h)
|
|
||||||
if(GNUTLS_INCLUDE_DIR)
|
|
||||||
include_directories("${GNUTLS_INCLUDE_DIR}")
|
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
|
||||||
endif(GNUTLS_INCLUDE_DIR)
|
|
||||||
mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR)
|
|
||||||
check_include_file("gnutls/openssl.h" HAVE_GNUTLS_OPENSSL_H)
|
|
||||||
check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H)
|
|
||||||
set(optlibsfound "${optlibsfound} GnuTLS")
|
|
||||||
else(GNUTLS_LIBRARIES)
|
|
||||||
set(optlibsnotfound "${optlibsnotfound} GnuTLS")
|
|
||||||
endif(GNUTLS_LIBRARIES)
|
|
||||||
|
|
||||||
# mysqlclient (using find_library and find_path)
|
# mysqlclient (using find_library and find_path)
|
||||||
find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql)
|
find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql)
|
||||||
if(MYSQLCLIENT_LIBRARIES)
|
if(MYSQLCLIENT_LIBRARIES)
|
||||||
|
@ -731,14 +740,7 @@ if(HAVE_OPENSSL_MD5_H)
|
||||||
"unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h"
|
"unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h"
|
||||||
HAVE_MD5_OPENSSL)
|
HAVE_MD5_OPENSSL)
|
||||||
endif(HAVE_OPENSSL_MD5_H)
|
endif(HAVE_OPENSSL_MD5_H)
|
||||||
if(HAVE_GNUTLS_OPENSSL_H)
|
|
||||||
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
|
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
|
||||||
check_prototype_definition(
|
|
||||||
MD5
|
|
||||||
"unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h"
|
|
||||||
HAVE_MD5_GNUTLS)
|
|
||||||
endif(HAVE_GNUTLS_OPENSSL_H)
|
|
||||||
if(HAVE_GNUTLS_GNUTLS_H)
|
if(HAVE_GNUTLS_GNUTLS_H)
|
||||||
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
|
set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}")
|
||||||
|
@ -747,13 +749,17 @@ if(HAVE_GNUTLS_GNUTLS_H)
|
||||||
"int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h"
|
"int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h"
|
||||||
HAVE_DECL_GNUTLS_FINGERPRINT)
|
HAVE_DECL_GNUTLS_FINGERPRINT)
|
||||||
endif(HAVE_GNUTLS_GNUTLS_H)
|
endif(HAVE_GNUTLS_GNUTLS_H)
|
||||||
if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
|
|
||||||
|
if(HAVE_MD5_OPENSSL)
|
||||||
set(HAVE_DECL_MD5 1)
|
set(HAVE_DECL_MD5 1)
|
||||||
else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
|
endif(HAVE_MD5_OPENSSL)
|
||||||
|
|
||||||
|
if((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT))
|
||||||
message(AUTHOR_WARNING
|
message(AUTHOR_WARNING
|
||||||
"ZoneMinder requires a working MD5 function for hashed authenication but
|
"ZoneMinder requires a working MD5 function for hashed authentication but
|
||||||
none were found - hashed authenication will not be available")
|
none were found - hashed authentication will not be available")
|
||||||
endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS)
|
endif((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT))
|
||||||
|
|
||||||
# Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available.
|
# Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available.
|
||||||
# This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac
|
# This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac
|
||||||
if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
|
if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
|
||||||
|
@ -903,7 +909,7 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}")
|
||||||
|
|
||||||
# Run ZM configuration generator
|
# Run ZM configuration generator
|
||||||
message(STATUS "Running ZoneMinder configuration generator")
|
message(STATUS "Running ZoneMinder configuration generator")
|
||||||
execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result)
|
execute_process(COMMAND perl ${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl RESULT_VARIABLE zmconfgen_result)
|
||||||
if(zmconfgen_result EQUAL 0)
|
if(zmconfgen_result EQUAL 0)
|
||||||
message(STATUS
|
message(STATUS
|
||||||
"ZoneMinder configuration generator completed successfully")
|
"ZoneMinder configuration generator completed successfully")
|
||||||
|
|
18
README.md
18
README.md
|
@ -1,9 +1,10 @@
|
||||||
ZoneMinder
|
ZoneMinder
|
||||||
==========
|
==========
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
|
||||||
|
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||||
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
||||||
|
[![IRC Network](https://img.shields.io/badge/irc-%23zoneminder-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=zoneminder)
|
||||||
|
|
||||||
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
||||||
|
|
||||||
|
@ -21,12 +22,6 @@ https://github.com/ZoneMinder/zmdockerfiles
|
||||||
|
|
||||||
## Installation Methods
|
## Installation Methods
|
||||||
|
|
||||||
### Building from Source is Discouraged
|
|
||||||
|
|
||||||
Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again.
|
|
||||||
|
|
||||||
Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source.
|
|
||||||
|
|
||||||
### Install from a Package Repository
|
### Install from a Package Repository
|
||||||
|
|
||||||
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
|
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
|
||||||
|
@ -42,6 +37,13 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
|
||||||
|
|
||||||
If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own.
|
If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own.
|
||||||
|
|
||||||
|
### Building from Source is Discouraged
|
||||||
|
|
||||||
|
Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again.
|
||||||
|
|
||||||
|
Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source.
|
||||||
|
|
||||||
|
|
||||||
### Building a ZoneMinder Package ###
|
### Building a ZoneMinder Package ###
|
||||||
|
|
||||||
Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier.
|
Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier.
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
pkg_check_modules(PC_LIBJWT QUIET libjwt)
|
||||||
|
|
||||||
|
find_path(LIBJWT_INCLUDE_DIR
|
||||||
|
NAMES jwt.h
|
||||||
|
HINTS ${PC_LIBJWT_INCLUDEDIR} ${PC_LIBJWT_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(LIBJWT_LIBRARY
|
||||||
|
NAMES jwt-gnutls libjwt-gnutls liblibjwt-gnutls
|
||||||
|
HINTS ${PC_LIBJWT_LIBDIR} ${PC_LIBJWT_LIBRARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(LibJWT
|
||||||
|
REQUIRED_VARS LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY
|
||||||
|
)
|
||||||
|
|
||||||
|
if(LIBJWT_FOUND)
|
||||||
|
add_library(libjwt STATIC IMPORTED GLOBAL)
|
||||||
|
set_target_properties(libjwt PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${LIBJWT_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${LIBJWT_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY)
|
|
@ -434,6 +434,7 @@ DROP TABLE IF EXISTS `Monitors`;
|
||||||
CREATE TABLE `Monitors` (
|
CREATE TABLE `Monitors` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
|
`Notes` TEXT,
|
||||||
`ServerId` int(10) unsigned,
|
`ServerId` int(10) unsigned,
|
||||||
`StorageId` smallint(5) unsigned default 0,
|
`StorageId` smallint(5) unsigned default 0,
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local',
|
||||||
|
@ -807,6 +808,7 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,0
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,0,1,0,0,0,0,0,0,0,0,0,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,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,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,0,1,0,0,0,0,0,0,0,0,0,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,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,'D-Link DCS-5020L','Remote','DCS5020L',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,24,1,0,1,1,1,0,1,0,1,0,0,1,30,0,0,0,0,0,1,0,0,1,30,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,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,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,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,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,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,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
|
@ -841,6 +843,7 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rt
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
|
INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'Notes'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column Notes already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD `Notes` TEXT AFTER `Name`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.33.16 database to 1.34.0
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.0 database to 1.34.1
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.1 database to 1.34.2
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.2 database to 1.34.3
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.3 database to 1.34.4
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.4 database to 1.34.5
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.5 database to 1.34.6
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -0,0 +1,5 @@
|
||||||
|
--
|
||||||
|
-- This updates a 1.34.6 database to 1.34.7
|
||||||
|
--
|
||||||
|
-- No changes required
|
||||||
|
--
|
|
@ -22,14 +22,10 @@ What's New
|
||||||
switching between httpd <-> nginx requires manaully changing ownership of
|
switching between httpd <-> nginx requires manaully changing ownership of
|
||||||
all event folders and the php session folder after the change.
|
all event folders and the php session folder after the change.
|
||||||
|
|
||||||
4. If you have installed ZoneMinder from the FedBerry repositories, this build
|
4. The timezone must now be set from the ZoneMinder web console. See the
|
||||||
of ZoneMinder has support for Raspberry Pi hardware acceleration when using
|
appropriate README, mentioned in the next step, for details.
|
||||||
ffmpeg. Unforunately, there is a problem with the same hardware acceleration
|
|
||||||
when using libvlc. Consequently, libvlc support in this build of ZoneMinder
|
|
||||||
has been disabled until the problem is resolved. See the following bug
|
|
||||||
report for details: https://trac.videolan.org/vlc/ticket/18594
|
|
||||||
|
|
||||||
5. Continue on to the next README that corresponds to the chosen webserver:
|
6. Continue on to the next README that corresponds to your chosen webserver:
|
||||||
|
|
||||||
README.httpd - Follow these steps when using Apache
|
README.httpd - Follow these steps when using Apache
|
||||||
README.nginx - Follow these steps when using Nginx
|
README.nginx - Follow these steps when using Nginx
|
||||||
|
|
|
@ -36,20 +36,17 @@ NOTE: EL7 users should replace "dnf" with "yum" in the instructions below.
|
||||||
sudo chown root:apache *.conf
|
sudo chown root:apache *.conf
|
||||||
sudo chmod 640 *.conf
|
sudo chmod 640 *.conf
|
||||||
|
|
||||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
timezone. PHP will complain loudly if this is not set, or if it is set
|
|
||||||
incorrectly, and these complaints will show up in the zoneminder logging
|
|
||||||
system as errors.
|
|
||||||
|
|
||||||
If you are not sure of the proper timezone specification to use, look at
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
http://php.net/date.timezone
|
Do this after completing step 10, below.
|
||||||
|
|
||||||
|
Note that timezone errors will appear in the ZoneMinder log until this
|
||||||
|
has been completed.
|
||||||
|
|
||||||
5. Disable SELinux
|
5. Disable SELinux
|
||||||
|
|
||||||
We currently do not have the resources to create and maintain an accurate
|
SELinux must be disabled or put into permissive mode. This is not optional!
|
||||||
SELinux policy for ZoneMinder on Fedora. We will gladly accept pull
|
|
||||||
reqeusts from anyone who wishes to do the work. In the meantime, SELinux
|
|
||||||
will need to be disabled or put into permissive mode.
|
|
||||||
|
|
||||||
To immediately disbale SELinux for the current seesion, issue the following
|
To immediately disbale SELinux for the current seesion, issue the following
|
||||||
from the command line:
|
from the command line:
|
||||||
|
@ -166,3 +163,11 @@ Upgrades
|
||||||
sudo systemctl restart httpd
|
sudo systemctl restart httpd
|
||||||
sudo systemctl start zoneminder
|
sudo systemctl start zoneminder
|
||||||
|
|
||||||
|
6. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
|
|
||||||
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
|
Do this now.
|
||||||
|
|
||||||
|
Note that timezone errors will appear in the ZoneMinder log until this
|
||||||
|
has been completed.
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,13 @@ New installs
|
||||||
sudo chown root:nginx *.conf
|
sudo chown root:nginx *.conf
|
||||||
sudo chmod 640 *.conf
|
sudo chmod 640 *.conf
|
||||||
|
|
||||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
timezone. PHP will complain loudly if this is not set, or if it is set
|
|
||||||
incorrectly, and these complaints will show up in the zoneminder logging
|
|
||||||
system as errors.
|
|
||||||
|
|
||||||
If you are not sure of the proper timezone specification to use, look at
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
http://php.net/date.timezone
|
Do this after completing step 10, below.
|
||||||
|
|
||||||
|
Note that timezone errors will appear in the ZoneMinder log until this
|
||||||
|
has been completed.
|
||||||
|
|
||||||
5. Disable SELinux
|
5. Disable SELinux
|
||||||
|
|
||||||
|
@ -169,3 +169,11 @@ Upgrades
|
||||||
sudo systemctl restart php-fpm
|
sudo systemctl restart php-fpm
|
||||||
sudo systemctl start zoneminder
|
sudo systemctl start zoneminder
|
||||||
|
|
||||||
|
6. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
|
|
||||||
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
|
Do this now.
|
||||||
|
|
||||||
|
Note that timezone errors will appear in the ZoneMinder log until this
|
||||||
|
has been completed.
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,21 @@
|
||||||
# This will tell zoneminder's cmake process we are building against a known distro
|
# This will tell zoneminder's cmake process we are building against a known distro
|
||||||
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
|
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
|
||||||
|
|
||||||
# Fedora >= 25 needs apcu backwards compatibility module
|
# Fedora needs apcu backwards compatibility module
|
||||||
%if 0%{?fedora} >= 25
|
%if 0%{?fedora}
|
||||||
%global with_apcu_bc 1
|
%global with_apcu_bc 1
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
# Newer php's keep json functions in a subpackage
|
||||||
|
%if 0%{?fedora} || 0%{?rhel} >= 8
|
||||||
|
%global with_php_json 1
|
||||||
|
%endif
|
||||||
|
|
||||||
# The default for everything but el7 these days
|
# The default for everything but el7 these days
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.33.14
|
Version: 1.34.10
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A camera monitoring and analysis tool
|
Summary: A camera monitoring and analysis tool
|
||||||
Group: System Environment/Daemons
|
Group: System Environment/Daemons
|
||||||
|
@ -105,7 +110,7 @@ Summary: Common files for ZoneMinder, not tied to a specific web server
|
||||||
Requires: php-mysqli
|
Requires: php-mysqli
|
||||||
Requires: php-common
|
Requires: php-common
|
||||||
Requires: php-gd
|
Requires: php-gd
|
||||||
%{?fedora:Requires: php-json}
|
%{?with_php_json:Requires: php-json}
|
||||||
Requires: php-pecl-apcu
|
Requires: php-pecl-apcu
|
||||||
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
|
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
|
||||||
Requires: cambozola
|
Requires: cambozola
|
||||||
|
@ -411,26 +416,26 @@ EOF
|
||||||
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Sun Aug 11 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.14-1
|
* Tue Feb 04 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.2-1
|
||||||
- Bump to 1.33.13 Development
|
- 1.34.2 Release
|
||||||
|
|
||||||
* Sun Jul 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.12-1
|
* Fri Jan 31 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.1-1
|
||||||
- Bump to 1.33.12 Development
|
- 1.34.1 Release
|
||||||
|
|
||||||
* Sun Jun 23 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.9-1
|
* Sat Jan 18 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.0-1
|
||||||
- Bump to 1.33.9 Development
|
- 1.34.0 Release
|
||||||
|
|
||||||
* Tue Apr 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.8-1
|
* Tue Dec 17 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-5
|
||||||
- Bump to 1.33.8 Development
|
- Mass rebuild for x264
|
||||||
|
|
||||||
* Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1
|
* Wed Aug 07 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-4
|
||||||
- Bump to 1.33.6 Development
|
- Rebuild for new ffmpeg version
|
||||||
|
|
||||||
* Sat Mar 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.4-1
|
* Tue Mar 12 2019 Sérgio Basto <sergio@serjux.com> - 1.32.3-3
|
||||||
- Bump to 1.33.4 Development
|
- Mass rebuild for x264
|
||||||
|
|
||||||
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
* Tue Mar 05 2019 RPM Fusion Release Engineering <leigh123linux@gmail.com> - 1.32.3-2
|
||||||
- Bump to 1.33.0 Development
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||||
|
|
||||||
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
||||||
- 1.32.3 Release
|
- 1.32.3 Release
|
||||||
|
|
|
@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
|
||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
|
|
||||||
# Order matters. This alias must come first.
|
# Order matters. This alias must come first.
|
||||||
Alias /zm/cache /var/cache/zoneminder/cache
|
Alias /zm/cache /var/cache/zoneminder/cache
|
||||||
<Directory /var/cache/zoneminder/cache>
|
<Directory /var/cache/zoneminder/cache>
|
||||||
|
|
|
@ -61,7 +61,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libjson-maybexs-perl
|
,libjson-maybexs-perl
|
||||||
,libsys-mmap-perl [!hurd-any]
|
,libsys-mmap-perl [!hurd-any]
|
||||||
,liburi-encode-perl
|
,liburi-encode-perl
|
||||||
,libwww-perl
|
,libwww-perl, liburi-perl
|
||||||
,libdata-dump-perl
|
,libdata-dump-perl
|
||||||
,libdatetime-perl
|
,libdatetime-perl
|
||||||
,libclass-std-fast-perl
|
,libclass-std-fast-perl
|
||||||
|
@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libfile-slurp-perl
|
,libfile-slurp-perl
|
||||||
,mysql-client | mariadb-client | virtual-mysql-client
|
,mysql-client | mariadb-client | virtual-mysql-client
|
||||||
,perl-modules
|
,perl-modules
|
||||||
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
|
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc, php-json | php5-json
|
||||||
,policykit-1
|
,policykit-1
|
||||||
,rsyslog | system-log-daemon
|
,rsyslog | system-log-daemon
|
||||||
,zip
|
,zip
|
||||||
|
|
|
@ -127,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent
|
||||||
Key lifetime (v2.0)
|
Key lifetime (v2.0)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_exipres`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
|
In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
|
||||||
|
|
||||||
Understanding access/refresh tokens (v2.0)
|
Understanding access/refresh tokens (v2.0)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -482,6 +482,7 @@ Create a Zone
|
||||||
&Zone[Units]=Percent\
|
&Zone[Units]=Percent\
|
||||||
&Zone[NumCoords]=4\
|
&Zone[NumCoords]=4\
|
||||||
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
||||||
|
&Zone[Area]=307200\
|
||||||
&Zone[AlarmRGB]=16711680\
|
&Zone[AlarmRGB]=16711680\
|
||||||
&Zone[CheckMethod]=Blobs\
|
&Zone[CheckMethod]=Blobs\
|
||||||
&Zone[MinPixelThreshold]=25\
|
&Zone[MinPixelThreshold]=25\
|
||||||
|
|
|
@ -42,7 +42,7 @@ guide you with a quick search.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
add-apt-repository ppa:iconnor/zoneminder-1.32
|
add-apt-repository ppa:iconnor/zoneminder-1.34
|
||||||
|
|
||||||
Update repo and upgrade.
|
Update repo and upgrade.
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ This brings up the new monitor window:
|
||||||
|
|
||||||
* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone`
|
* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone`
|
||||||
|
|
||||||
* In Analytis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
|
* In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example)
|
Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example)
|
||||||
|
@ -123,6 +123,8 @@ This brings up the new monitor window:
|
||||||
* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera)
|
* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera)
|
||||||
* Note that starting ZM 1.34, GPUs are supported. In my case, I have an NVIDIA GeForce GTX1050i. These ``cuda`` and ``cuvid`` parameters are what my system supports to use the NVIDIA hardware decoder and GPU resources. If you don't have a GPU, or don't know how to configure your ffmpeg to support it, leave it empty for now. In future, we will add a section on how to set up a GPU
|
* Note that starting ZM 1.34, GPUs are supported. In my case, I have an NVIDIA GeForce GTX1050i. These ``cuda`` and ``cuvid`` parameters are what my system supports to use the NVIDIA hardware decoder and GPU resources. If you don't have a GPU, or don't know how to configure your ffmpeg to support it, leave it empty for now. In future, we will add a section on how to set up a GPU
|
||||||
|
|
||||||
|
**NOTE**: It is entirely possible that ``cuda`` and ``cuvid`` don't work for you and you need different values. Isaac uses ``cuda`` in ``DecoderHWAccelName`` and leaves ``DecoderHWAccelDevice`` empty. Try that too.
|
||||||
|
|
||||||
.. todo::
|
.. todo::
|
||||||
add GPU docs
|
add GPU docs
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@ LANG_DEFAULT - ZoneMinder allows the web interface to use languages other than E
|
||||||
|
|
||||||
OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder.
|
OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder.
|
||||||
|
|
||||||
AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder.
|
AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authenticated 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured in ZoneMinder.
|
||||||
|
|
||||||
AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways.
|
AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways.
|
||||||
|
|
||||||
AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above
|
AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and passwords. Although these strings are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above.
|
||||||
|
|
||||||
AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failure.
|
AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failures.
|
||||||
|
|
||||||
AUTH_HASH_TTL - Time before ZM auth will expire (does not apply to API tokens). The default has traditionally been 2 hours. A new hash will automatically be regenerated at half this value.
|
AUTH_HASH_TTL - Time before ZM auth will expire (does not apply to API tokens). The default has traditionally been 2 hours. A new hash will automatically be regenerated at half this value.
|
||||||
|
|
||||||
|
@ -34,11 +34,11 @@ OPT_USE_API - A global setting to enable/disable ZoneMinder APIs. If you are usi
|
||||||
|
|
||||||
OPT_USE_LEGACY_AUTH - Starting version 1.34.0, ZoneMinder uses a more secure Authentication mechanism using JWT tokens. Older versions used a less secure MD5 based auth hash. It is recommended you turn this off after you are sure you don't need it. If you are using a 3rd party app that relies on the older API auth mechanisms, you will have to update that app if you turn this off. Note that zmNinja 1.3.057 onwards supports the new token system.
|
OPT_USE_LEGACY_AUTH - Starting version 1.34.0, ZoneMinder uses a more secure Authentication mechanism using JWT tokens. Older versions used a less secure MD5 based auth hash. It is recommended you turn this off after you are sure you don't need it. If you are using a 3rd party app that relies on the older API auth mechanisms, you will have to update that app if you turn this off. Note that zmNinja 1.3.057 onwards supports the new token system.
|
||||||
|
|
||||||
OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if its already installed. Please visit the `Event Notification Server project site <https://github.com/pliablepixels/zmeventserver>`__ for installation instructions.
|
OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if it is already installed. Please visit the `Event Notification Server project site <https://github.com/pliablepixels/zmeventserver>`__ for installation instructions.
|
||||||
|
|
||||||
OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid usernane and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reach out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen)
|
OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid username and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reaching out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen).
|
||||||
|
|
||||||
SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers:
|
SYSTEM_SHUTDOWN - this option puts a poweroff icon in the header of the ZM UI for users with System privilege accessi. This icon will allow the user to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full syst
|
||||||
|
|
||||||
to perform the shutdown or reboot
|
to perform the shutdown or reboot
|
||||||
|
|
||||||
OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system.
|
OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if youxr are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system.
|
||||||
|
|
||||||
FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value.
|
FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. As of 1.34.0 filters should be automatically reloaded when saving a filter so this setting should have little effect.
|
||||||
|
|
||||||
FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements.
|
FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements.
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ STATUS_UPDATE_INTERVAL - The zmstats daemon performs various db queries related
|
||||||
|
|
||||||
WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked.
|
WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked.
|
||||||
|
|
||||||
WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above.
|
WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. Please note that some cameras can take up to 30 seconds to get a valid image, so this setting should be larger than that.
|
||||||
|
|
||||||
RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems.
|
RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems and run it manually if needed after a system crash.
|
||||||
|
|
||||||
AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed.
|
AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed.
|
||||||
|
|
||||||
|
@ -70,11 +70,11 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu
|
||||||
|
|
||||||
OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.
|
OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.
|
||||||
|
|
||||||
CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable
|
CHECK_FOR_UPDATES - To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable.
|
||||||
|
|
||||||
TELEMETRY_DATA - Enable collection of usage information of the local system and send 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 purpose 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. For more details on what information we collect, please refer to Zoneminder's privacy statement (available in the contextual help of TELEMETRY_DATA on your installation).
|
TELEMETRY_DATA - Enable collection of usage information of the local system and send 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 purpose 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. For more details on what information we collect, please refer to Zoneminder's privacy statement (available in the contextual help of TELEMETRY_DATA on your installation).
|
||||||
|
|
||||||
UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://<proxy host>:<proxy port>/``
|
UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://<proxy host>:<proxy port>/``.
|
||||||
|
|
||||||
SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored.
|
SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored.
|
||||||
|
|
||||||
|
|
|
@ -28,3 +28,4 @@ zmtelemetry\[[[:digit:]]+\]: INF \[Telemetry data uploaded successfully.\]$
|
||||||
zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$
|
zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$
|
||||||
zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$
|
zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$
|
||||||
web_php\[[[:digit:]]+\]: INF \[Login successful for user "[[:alnum:]]+"\]$
|
web_php\[[[:digit:]]+\]: INF \[Login successful for user "[[:alnum:]]+"\]$
|
||||||
|
zmeventnotification\[[[:digit:]]+\]: INF
|
||||||
|
|
|
@ -76,7 +76,7 @@ my %soap_version_of :ATTR(:default<('1.1')>);
|
||||||
|
|
||||||
sub service {
|
sub service {
|
||||||
my ($self, $serviceName, $attr) = @_;
|
my ($self, $serviceName, $attr) = @_;
|
||||||
#print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n";
|
#print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n";
|
||||||
# Please note that the Std::Class::Fast docs say not to use ident.
|
# Please note that the Std::Class::Fast docs say not to use ident.
|
||||||
$services_of{ident $self}{$serviceName}{$attr};
|
$services_of{ident $self}{$serviceName}{$attr};
|
||||||
}
|
}
|
||||||
|
@ -114,18 +114,18 @@ sub get_service_urls {
|
||||||
|
|
||||||
my $result = $self->service('device', 'ep')->GetServices( {
|
my $result = $self->service('device', 'ep')->GetServices( {
|
||||||
IncludeCapability => 'true', # boolean
|
IncludeCapability => 'true', # boolean
|
||||||
},,
|
}
|
||||||
);
|
);
|
||||||
if ( $result ) {
|
if ( $result ) {
|
||||||
foreach my $svc ( @{ $result->get_Service() } ) {
|
foreach my $svc ( @{ $result->get_Service() } ) {
|
||||||
my $short_name = $namespace_map{$svc->get_Namespace()};
|
my $short_name = $namespace_map{$svc->get_Namespace()};
|
||||||
my $url_svc = $svc->get_XAddr()->get_value();
|
my $url_svc = $svc->get_XAddr()->get_value();
|
||||||
if(defined $short_name && defined $url_svc) {
|
if ( defined $short_name && defined $url_svc ) {
|
||||||
# print "Got $short_name service\n";
|
#print "Got $short_name service\n";
|
||||||
$self->set_service($short_name, 'url', $url_svc);
|
$self->set_service($short_name, 'url', $url_svc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# } else {
|
#} else {
|
||||||
#print "No results from GetServices: $result\n";
|
#print "No results from GetServices: $result\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,14 +142,14 @@ sub get_service_urls {
|
||||||
if ( my $function = $capabilities->can( "get_$capability" ) ) {
|
if ( my $function = $capabilities->can( "get_$capability" ) ) {
|
||||||
my $Services = $function->( $capabilities );
|
my $Services = $function->( $capabilities );
|
||||||
if ( !$Services ) {
|
if ( !$Services ) {
|
||||||
print "Nothing returned ffrom get_$capability\n";
|
#print "Nothing returned from get_$capability\n";
|
||||||
} else {
|
} else {
|
||||||
foreach my $svc ( @{ $Services } ) {
|
foreach my $svc ( @{ $Services } ) {
|
||||||
# The capability versions don't have a namespace, so just lowercase them.
|
# The capability versions don't have a namespace, so just lowercase them.
|
||||||
my $short_name = lc $capability;
|
my $short_name = lc $capability;
|
||||||
my $url_svc = $svc->get_XAddr()->get_value();
|
my $url_svc = $svc->get_XAddr()->get_value();
|
||||||
if( defined $url_svc) {
|
if ( defined $url_svc ) {
|
||||||
# print "Got $short_name service\n";
|
#print "Got $short_name service\n";
|
||||||
$self->set_service($short_name, 'url', $url_svc);
|
$self->set_service($short_name, 'url', $url_svc);
|
||||||
}
|
}
|
||||||
} # end foreach svr
|
} # end foreach svr
|
||||||
|
@ -202,10 +202,10 @@ sub BUILD {
|
||||||
# deserializer_args => { strict => 0 }
|
# deserializer_args => { strict => 0 }
|
||||||
});
|
});
|
||||||
|
|
||||||
$services_of{$ident}{'device'} = { url => $url_svc_device, ep => $svc_device };
|
$services_of{$ident}{device} = { url => $url_svc_device, ep => $svc_device };
|
||||||
|
|
||||||
# Can't, don't have credentials yet
|
# Can't, don't have credentials yet
|
||||||
#$self->get_service_urls();
|
# $self->get_service_urls();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_users {
|
sub get_users {
|
||||||
|
@ -260,7 +260,7 @@ sub set_credentials {
|
||||||
sub create_services {
|
sub create_services {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
#$self->get_service_urls();
|
$self->get_service_urls();
|
||||||
|
|
||||||
if ( defined $self->service('media', 'url') ) {
|
if ( defined $self->service('media', 'url') ) {
|
||||||
$self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({
|
$self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({
|
||||||
|
|
|
@ -785,7 +785,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_TIMEZONE',
|
name => 'ZM_TIMEZONE',
|
||||||
default => 'UTC',
|
default => '',
|
||||||
description => 'The timezone that php should use.',
|
description => 'The timezone that php should use.',
|
||||||
help => q`
|
help => q`
|
||||||
This should be set equal to the system timezone of the mysql server`,
|
This should be set equal to the system timezone of the mysql server`,
|
||||||
|
@ -1127,7 +1127,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_LOG_LEVEL_FILE',
|
name => 'ZM_LOG_LEVEL_FILE',
|
||||||
default => '-5',
|
default => '1',
|
||||||
description => 'Save logging output to component files',
|
description => 'Save logging output to component files',
|
||||||
help => q`
|
help => q`
|
||||||
ZoneMinder logging is now more integrated between
|
ZoneMinder logging is now more integrated between
|
||||||
|
@ -1312,7 +1312,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_LOG_DEBUG_FILE',
|
name => 'ZM_LOG_DEBUG_FILE',
|
||||||
default => '@ZM_LOGDIR@/zm_debug.log+',
|
default => '',
|
||||||
description => 'Where extra debug is output to',
|
description => 'Where extra debug is output to',
|
||||||
help => q`
|
help => q`
|
||||||
This option allows you to specify a different target for debug
|
This option allows you to specify a different target for debug
|
||||||
|
@ -2468,7 +2468,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_WATCH_MAX_DELAY',
|
name => 'ZM_WATCH_MAX_DELAY',
|
||||||
default => '5',
|
default => '45',
|
||||||
description => 'The maximum delay allowed since the last captured image',
|
description => 'The maximum delay allowed since the last captured image',
|
||||||
help => q`
|
help => q`
|
||||||
The zmwatch daemon checks the image capture performance of the
|
The zmwatch daemon checks the image capture performance of the
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Acrest HTTP API Control Protocol Module, 20180214, Rev 3.0
|
# ZoneMinder Amcrest HTTP API Control Protocol Module
|
||||||
#
|
|
||||||
# Change Log
|
|
||||||
#
|
|
||||||
# Rev 3.0:
|
|
||||||
# - Fixes incorrect method names
|
|
||||||
# - Updates control sequences to Amcrest HTTP Protocol API v 2.12
|
|
||||||
# - Extends control features
|
|
||||||
#
|
|
||||||
# Rev 2.0:
|
|
||||||
# - Fixed installation instructions text, no changes to functionality.
|
|
||||||
#
|
#
|
||||||
# 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
|
||||||
|
@ -38,6 +28,8 @@ use Time::HiRes qw( usleep );
|
||||||
|
|
||||||
require ZoneMinder::Base;
|
require ZoneMinder::Base;
|
||||||
require ZoneMinder::Control;
|
require ZoneMinder::Control;
|
||||||
|
require LWP::UserAgent;
|
||||||
|
use URI;
|
||||||
|
|
||||||
our @ISA = qw(ZoneMinder::Control);
|
our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
|
@ -50,130 +42,130 @@ our @ISA = qw(ZoneMinder::Control);
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
sub new
|
sub new {
|
||||||
{
|
my $class = shift;
|
||||||
my $class = shift;
|
my $id = shift;
|
||||||
my $id = shift;
|
my $self = ZoneMinder::Control->new($id);
|
||||||
my $self = ZoneMinder::Control->new( $id );
|
bless($self, $class);
|
||||||
bless( $self, $class );
|
return $self;
|
||||||
srand( time() );
|
|
||||||
return $self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
our $AUTOLOAD;
|
sub open {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
sub AUTOLOAD
|
$self->loadMonitor();
|
||||||
{
|
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||||
my $self = shift;
|
# Has no scheme at the beginning, so won't parse as a URI
|
||||||
my $class = ref($self) || croak( "$self not object" );
|
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
||||||
my $name = $AUTOLOAD;
|
}
|
||||||
$name =~ s/.*://;
|
my $uri = URI->new($self->{Monitor}->{ControlAddress});
|
||||||
Debug( "Received command: $name" );
|
|
||||||
if ( exists($self->{$name}) )
|
|
||||||
{
|
|
||||||
return( $self->{$name} );
|
|
||||||
}
|
|
||||||
Fatal( "Can't access $name member of object of class $class" );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub open
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
{
|
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||||
my $self = shift;
|
my ( $username, $password );
|
||||||
|
my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice};
|
||||||
|
if ( $self->{Monitor}->{ControlAddress} ) {
|
||||||
|
( $username, $password ) = $uri->authority() =~ /^(.*):(.*)@(.*)$/;
|
||||||
|
|
||||||
$self->loadMonitor();
|
$$self{address} = $uri->host_port();
|
||||||
|
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
||||||
|
# Testing seems to show that we need the username/password in each url as well as credentials
|
||||||
|
$$self{base_url} = $uri->canonical();
|
||||||
|
Debug('Using initial credentials for '.$uri->host_port().", $realm, $username, $password, base_url: $$self{base_url} auth:".$uri->authority());
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect REALM, has to be /cgi-bin/ptz.cgi because just / accepts no auth
|
||||||
|
my $res = $self->{ua}->get($$self{base_url}.'cgi-bin/ptz.cgi');
|
||||||
|
|
||||||
|
if ( $res->is_success ) {
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sub initUA
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $user = undef;
|
|
||||||
my $password = undef;
|
|
||||||
my $address = undef;
|
|
||||||
|
|
||||||
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ )
|
my $headers = $res->headers();
|
||||||
{
|
foreach my $k ( keys %$headers ) {
|
||||||
$user = $1;
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
$password = $2;
|
|
||||||
$address = $3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use LWP::UserAgent;
|
if ( $$headers{'www-authenticate'} ) {
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||||
$self->{ua}->credentials("$address", "Login to " . $self->{Monitor}->{ControlDevice}, "$user", "$password");
|
if ( $tokens =~ /realm="([^"]+)"/i ) {
|
||||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
if ( $realm ne $1 ) {
|
||||||
|
$realm = $1;
|
||||||
|
Debug("Changing REALM to ($realm)");
|
||||||
|
$self->{ua}->credentials($$self{address}, $realm, $username, $password);
|
||||||
|
$res = $self->{ua}->get($$self{base_url}.'cgi-bin/ptz.cgi');
|
||||||
|
if ( $res->is_success() ) {
|
||||||
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
|
} elsif ( $res->status_line eq '400 Bad Request' ) {
|
||||||
|
# In testing, this second request fails with Bad Request, I assume because we didn't actually give it a command.
|
||||||
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Error('Authentication still failed after updating REALM' . $res->status_line);
|
||||||
|
$headers = $res->headers();
|
||||||
|
foreach my $k ( keys %$headers ) {
|
||||||
|
Debug("Header $k => $$headers{$k}");
|
||||||
|
} # end foreach
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error('Authentication failed, not a REALM problem');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error('Failed to match realm in tokens');
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
Debug('No headers line');
|
||||||
|
} # end if headers
|
||||||
|
} else {
|
||||||
|
Error("Failed to get $$self{base_url}cgi-bin/ptz.cgi ".$res->status_line());
|
||||||
|
|
||||||
|
} # end if $res->status_line() eq '401 Unauthorized'
|
||||||
|
|
||||||
|
$self->{state} = 'closed';
|
||||||
}
|
}
|
||||||
|
|
||||||
sub destroyUA
|
sub close {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
$self->{state} = 'closed';
|
||||||
|
|
||||||
$self->{ua} = undef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub close
|
sub sendCmd {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $cmd = shift;
|
||||||
$self->{state} = 'closed';
|
my $result = undef;
|
||||||
}
|
|
||||||
|
|
||||||
sub printMsg
|
$self->printMsg($cmd, 'Tx');
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
Debug( $msg."[".$msg_len."]" );
|
my $res = $self->{ua}->get($$self{base_url}.$cmd);
|
||||||
}
|
|
||||||
|
|
||||||
sub sendCmd
|
if ( $res->is_success ) {
|
||||||
{
|
$result = !undef;
|
||||||
my $self = shift;
|
# Command to camera appears successful, write Info item to log
|
||||||
my $cmd = shift;
|
Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||||
my $result = undef;
|
# TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status.
|
||||||
|
} else {
|
||||||
destroyUA($self);
|
# Try again
|
||||||
initUA($self);
|
$res = $self->{ua}->get($$self{base_url}.$cmd);
|
||||||
|
if ( $res->is_success ) {
|
||||||
my $user = undef;
|
# Command to camera appears successful, write Info item to log
|
||||||
my $password = undef;
|
Info('Camera control 2: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||||
my $address = undef;
|
} else {
|
||||||
|
Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||||
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ )
|
$res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd);
|
||||||
{
|
|
||||||
$user = $1;
|
|
||||||
$password = $2;
|
|
||||||
$address = $3;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printMsg( $cmd, "Tx" );
|
return $result;
|
||||||
|
|
||||||
my $req = HTTP::Request->new( GET=>"http://$address/$cmd" );
|
|
||||||
my $res = $self->{ua}->request($req);
|
|
||||||
|
|
||||||
if ( $res->is_success )
|
|
||||||
{
|
|
||||||
$result = !undef;
|
|
||||||
# Command to camera appears successful, write Info item to log
|
|
||||||
Info( "Camera control: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
|
||||||
# TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error( "Camera control command FAILED: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( $result );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset
|
sub reset {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
# This reboots the camera effectively resetting it
|
||||||
# This reboots the camera effectively resetting it
|
$self->sendCmd('cgi-bin/magicBox.cgi?action=reboot');
|
||||||
Debug( "Camera Reset" );
|
|
||||||
$self->sendCmd( 'cgi-bin/magicBox.cgi?action=reboot' );
|
|
||||||
##FIXME: Exit is a bad idea as it appears to cause zmc to run away.
|
|
||||||
#Exit (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: I'm putting this in, but absolute camera movement does not seem to be well supported in the classic skin ATM.
|
# NOTE: I'm putting this in, but absolute camera movement does not seem to be well supported in the classic skin ATM.
|
||||||
|
@ -184,163 +176,148 @@ sub reset
|
||||||
|
|
||||||
sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here...
|
sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here...
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $pan_degrees = shift || 0;
|
my $pan_degrees = shift || 0;
|
||||||
my $tilt_degrees = shift || 0;
|
my $tilt_degrees = shift || 0;
|
||||||
my $speed = shift || 1;
|
my $speed = shift || 1;
|
||||||
Debug( "Move ABS" );
|
Debug('Move ABS');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUp
|
sub moveConUp {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Up');
|
||||||
Debug( "Move Up" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0' );
|
usleep(500); ##XXX Should this be passed in as a "speed" parameter?
|
||||||
usleep (500); ##XXX Should this be passed in as a "speed" parameter?
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDown
|
sub moveConDown {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Down');
|
||||||
Debug( "Move Down" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConLeft
|
sub moveConLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Left');
|
||||||
Debug( "Move Left" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConRight
|
sub moveConRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Right');
|
||||||
Debug( "Move Right" );
|
# $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' );
|
||||||
# $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
Debug('Move Right Stop');
|
||||||
Debug( "Move Right Stop" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpRight
|
sub moveConUpRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Diagonally Up Right');
|
||||||
Debug( "Move Diagonally Up Right" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownRight
|
sub moveConDownRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Diagonally Down Right');
|
||||||
Debug( "Move Diagonally Down Right" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpLeft
|
sub moveConUpLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Diagonally Up Left');
|
||||||
Debug( "Move Diagonally Up Left" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0' );
|
usleep(500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownLeft
|
sub moveConDownLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Diagonally Down Left');
|
||||||
Debug( "Move Diagonally Down Left" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0' );
|
usleep (500);
|
||||||
usleep (500);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Stop is not "correctly" implemented as control_functions.php translates this to "Center"
|
# Stop is not "correctly" implemented as control_functions.php translates this to "Center"
|
||||||
# So we'll just send the camera to 0* Horz, 0* Vert, zoom out; Also, Amcrest does not seem to
|
# So we'll just send the camera to 0* Horz, 0* Vert, zoom out; Also, Amcrest does not seem to
|
||||||
# support a generic stop-all-current-action command.
|
# support a generic stop-all-current-action command.
|
||||||
|
|
||||||
sub moveStop
|
sub moveStop {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Stop/Center');
|
||||||
Debug( "Move Stop/Center" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Move Camera to Home Position
|
# Move Camera to Home Position
|
||||||
# The current API does not support a Home per se, so we'll just send the camera to preset #1
|
# The current API does not support a Home per se, so we'll just send the camera to preset #1
|
||||||
# NOTE: It goes without saying that the user must have set up preset #1 for this to work.
|
# NOTE: It goes without saying that the user must have set up preset #1 for this to work.
|
||||||
|
|
||||||
sub presetHome
|
sub presetHome {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Home Preset');
|
||||||
Debug( "Home Preset" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetGoto
|
sub presetGoto {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $preset = $self->getParam($params, 'preset');
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
Debug("Go To Preset $preset");
|
||||||
Debug( "Go To Preset $preset" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetSet
|
sub presetSet {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $preset = $self->getParam($params, 'preset');
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
Debug('Set Preset');
|
||||||
Debug( "Set Preset" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# NOTE: This does not appear to be implemented in the classic skin. But we'll leave it here for later.
|
# NOTE: This does not appear to be implemented in the classic skin. But we'll leave it here for later.
|
||||||
|
|
||||||
sub moveMap
|
sub moveMap {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
|
||||||
|
|
||||||
my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 );
|
my $xcoord = $self->getParam( $params, 'xcoord', $self->{Monitor}{Width}/2 );
|
||||||
my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 );
|
my $ycoord = $self->getParam( $params, 'ycoord', $self->{Monitor}{Height}/2 );
|
||||||
# if the camera is mounted upside down, you may have to inverse these coordinates
|
# if the camera is mounted upside down, you may have to inverse these coordinates
|
||||||
# just use 360 minus pan instead of pan, 90 minus tilt instead of tilt
|
# just use 360 minus pan instead of pan, 90 minus tilt instead of tilt
|
||||||
# Convert xcoord into pan position 0 to 359
|
# Convert xcoord into pan position 0 to 359
|
||||||
my $pan = int(360 * $xcoord / $self->{Monitor}{Width});
|
my $pan = int(360 * $xcoord / $self->{Monitor}{Width});
|
||||||
# Convert ycoord into tilt position 0 to 89
|
# Convert ycoord into tilt position 0 to 89
|
||||||
my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height});
|
my $tilt = 90 - int(90 * $ycoord / $self->{Monitor}{Height});
|
||||||
# Now get the following url:
|
# Now get the following url:
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomConTele
|
sub zoomConTele {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Zoom continuous tele');
|
||||||
Debug( "Zoom continuous tele" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' );
|
usleep(100000);
|
||||||
usleep (100000);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomConWide
|
sub zoomConWide {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Zoom continuous wide');
|
||||||
Debug( "Zoom continuous wide" );
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' );
|
usleep (100000);
|
||||||
usleep (100000);
|
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
$self->sendCmd( 'cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -355,7 +332,7 @@ ZoneMinder::Control::Amcrest_HTTP - Amcrest camera control
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
This module contains the implementation of the Amcrest Camera
|
This module contains the implementation of the Amcrest Camera
|
||||||
controllable SDK API.
|
controllable SDK API.
|
||||||
|
|
||||||
NOTE: This module implements interaction with the camera in clear text.
|
NOTE: This module implements interaction with the camera in clear text.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Axis version 2 API Control Protocol Module, $Date$, $Revision$
|
# ZoneMinder Axis version 2 API Control Protocol Module
|
||||||
# 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
|
||||||
|
@ -43,349 +43,359 @@ use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
use Time::HiRes qw( usleep );
|
use Time::HiRes qw( usleep );
|
||||||
|
use URI;
|
||||||
|
|
||||||
sub open
|
our $ADDRESS;
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$self->loadMonitor();
|
sub open {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
use LWP::UserAgent;
|
$self->loadMonitor();
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
# Has no scheme at the beginning, so won't parse as a URI
|
||||||
|
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
||||||
|
}
|
||||||
|
my $uri = URI->new($self->{Monitor}->{ControlAddress});
|
||||||
|
$ADDRESS = $uri->scheme.'://'.$uri->authority().$uri->path().($uri->port()?':'.$uri->port():'');
|
||||||
|
|
||||||
|
use LWP::UserAgent;
|
||||||
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
|
$self->{ua}->cookie_jar( {} );
|
||||||
|
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||||
|
$self->{state} = 'closed';
|
||||||
|
|
||||||
|
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
|
||||||
|
my $realm = $self->{Monitor}->{ControlDevice};
|
||||||
|
|
||||||
|
$self->{ua}->credentials($ADDRESS, $realm, $username, $password);
|
||||||
|
|
||||||
|
# test auth
|
||||||
|
my $res = $self->{ua}->get($ADDRESS.'/cgi/ptdc.cgi');
|
||||||
|
|
||||||
|
if ( $res->is_success ) {
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sub printMsg
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
Debug( $msg."[".$msg_len."]" );
|
my $headers = $res->headers();
|
||||||
}
|
foreach my $k ( keys %$headers ) {
|
||||||
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
sub sendCmd
|
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $cmd = shift;
|
|
||||||
|
|
||||||
my $result = undef;
|
|
||||||
|
|
||||||
printMsg( $cmd, "Tx" );
|
|
||||||
|
|
||||||
#print( "http://$address/$cmd\n" );
|
|
||||||
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
|
||||||
my $res = $self->{ua}->request($req);
|
|
||||||
|
|
||||||
if ( $res->is_success )
|
|
||||||
{
|
|
||||||
$result = !undef;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error( "Error check failed: '".$res->status_line()."'" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return( $result );
|
if ( $$headers{'www-authenticate'} ) {
|
||||||
|
Debug('Authenticating');
|
||||||
|
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||||
|
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
||||||
|
if ( $realm ne $1 ) {
|
||||||
|
$realm = $1;
|
||||||
|
Debug("Changing REALM to $realm");
|
||||||
|
$self->{ua}->credentials($host, $realm, $username, $password);
|
||||||
|
$res = $self->{ua}->get($ADDRESS);
|
||||||
|
if ( $res->is_success() ) {
|
||||||
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Error('Authentication still failed after updating REALM'.$res->status_line);
|
||||||
|
$headers = $res->headers();
|
||||||
|
foreach my $k ( keys %$headers ) {
|
||||||
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
|
} # end foreach
|
||||||
|
} else {
|
||||||
|
Error('Authentication failed, not a REALM problem');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error('Failed to match realm in tokens');
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
Debug('No headers line');
|
||||||
|
} # end if headers
|
||||||
|
} # end if $res->status_line() eq '401 Unauthorized'
|
||||||
|
} # end sub open
|
||||||
|
|
||||||
|
sub sendCmd {
|
||||||
|
my $self = shift;
|
||||||
|
my $cmd = shift;
|
||||||
|
|
||||||
|
$self->printMsg($cmd, 'Tx');
|
||||||
|
|
||||||
|
my $url = $ADDRESS.$cmd;
|
||||||
|
my $res = $self->{ua}->get($url);
|
||||||
|
|
||||||
|
if ( $res->is_success ) {
|
||||||
|
Debug('sndCmd command: ' . $url . ' content: '.$res->content);
|
||||||
|
return !undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error("Error cmd $url failed: '".$res->status_line()."'");
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cameraReset
|
sub cameraReset {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Camera Reset');
|
||||||
Debug( "Camera Reset" );
|
my $cmd = '/axis-cgi/admin/restart.cgi';
|
||||||
my $cmd = "/axis-cgi/admin/restart.cgi";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUp
|
sub moveConUp {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Up');
|
||||||
Debug( "Move Up" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=up';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=up";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDown
|
sub moveConDown {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Down');
|
||||||
Debug( "Move Down" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=down';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=down";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConLeft
|
sub moveConLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Left');
|
||||||
Debug( "Move Left" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=left';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=left";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConRight
|
sub moveConRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Right');
|
||||||
Debug( "Move Right" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=right';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=right";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpRight
|
sub moveConUpRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Up/Right');
|
||||||
Debug( "Move Up/Right" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=upright';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upright";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpLeft
|
sub moveConUpLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Up/Left');
|
||||||
Debug( "Move Up/Left" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=upleft';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upleft";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownRight
|
sub moveConDownRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Down/Right');
|
||||||
Debug( "Move Down/Right" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=downright';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downright";
|
$self->sendCmd( $cmd );
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownLeft
|
sub moveConDownLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Move Down/Left');
|
||||||
Debug( "Move Down/Left" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=downleft';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downleft";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveMap
|
sub moveMap {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $xcoord = $self->getParam($params, 'xcoord');
|
||||||
my $xcoord = $self->getParam( $params, 'xcoord' );
|
my $ycoord = $self->getParam($params, 'ycoord');
|
||||||
my $ycoord = $self->getParam( $params, 'ycoord' );
|
Debug("Move Map to $xcoord,$ycoord");
|
||||||
Debug( "Move Map to $xcoord,$ycoord" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}.'&imageheight='.$self->{Monitor}->{Height};
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}."&imageheight=".$self->{Monitor}->{Height};
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUp
|
sub moveRelUp {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
my $step = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Up $step");
|
||||||
Debug( "Step Up $step" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?rtilt='.$step;
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDown
|
sub moveRelDown {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
my $step = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Down $step");
|
||||||
Debug( "Step Down $step" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?rtilt=-'.$step;
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=-$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelLeft
|
sub moveRelLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'panstep');
|
||||||
my $step = $self->getParam( $params, 'panstep' );
|
Debug("Step Left $step");
|
||||||
Debug( "Step Left $step" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?rpan=-'.$step;
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelRight
|
sub moveRelRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'panstep');
|
||||||
my $step = $self->getParam( $params, 'panstep' );
|
Debug("Step Right $step");
|
||||||
Debug( "Step Right $step" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?rpan='.$step;
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpRight
|
sub moveRelUpRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
my $panstep = $self->getParam( $params, 'panstep' );
|
my $tiltstep = $self->getParam($params, 'tiltstep');
|
||||||
my $tiltstep = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Up/Right $tiltstep/$panstep");
|
||||||
Debug( "Step Up/Right $tiltstep/$panstep" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=$tiltstep";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=$tiltstep";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpLeft
|
sub moveRelUpLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
my $panstep = $self->getParam( $params, 'panstep' );
|
my $tiltstep = $self->getParam($params, 'tiltstep');
|
||||||
my $tiltstep = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Up/Left $tiltstep/$panstep");
|
||||||
Debug( "Step Up/Left $tiltstep/$panstep" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=$tiltstep";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=$tiltstep";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownRight
|
sub moveRelDownRight {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
my $panstep = $self->getParam( $params, 'panstep' );
|
my $tiltstep = $self->getParam($params, 'tiltstep');
|
||||||
my $tiltstep = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Down/Right $tiltstep/$panstep");
|
||||||
Debug( "Step Down/Right $tiltstep/$panstep" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=-$tiltstep";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=-$tiltstep";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownLeft
|
sub moveRelDownLeft {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
my $panstep = $self->getParam( $params, 'panstep' );
|
my $tiltstep = $self->getParam($params, 'tiltstep');
|
||||||
my $tiltstep = $self->getParam( $params, 'tiltstep' );
|
Debug("Step Down/Left $tiltstep/$panstep");
|
||||||
Debug( "Step Down/Left $tiltstep/$panstep" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=-$tiltstep";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=-$tiltstep";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelTele
|
sub zoomRelTele {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Zoom Tele');
|
||||||
Debug( "Zoom Tele" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelWide
|
sub zoomRelWide {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Zoom Wide');
|
||||||
Debug( "Zoom Wide" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusRelNear
|
sub focusRelNear {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Focus Near');
|
||||||
Debug( "Focus Near" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusRelFar
|
sub focusRelFar {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Focus Far');
|
||||||
Debug( "Focus Far" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusAuto
|
sub focusAuto {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Focus Auto');
|
||||||
Debug( "Focus Auto" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=on';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=on";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusMan
|
sub focusMan {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Focus Manual');
|
||||||
Debug( "Focus Manual" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=off';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisRelOpen
|
sub irisRelOpen {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Iris Open');
|
||||||
Debug( "Iris Open" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisRelClose
|
sub irisRelClose {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $step = $self->getParam($params, 'step');
|
||||||
my $step = $self->getParam( $params, 'step' );
|
Debug('Iris Close');
|
||||||
Debug( "Iris Close" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisAuto
|
sub irisAuto {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Iris Auto');
|
||||||
Debug( "Iris Auto" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=on';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=on";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisMan
|
sub irisMan {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Iris Manual');
|
||||||
Debug( "Iris Manual" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=off';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=off";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetClear
|
sub presetClear {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $preset = $self->getParam($params, 'preset');
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
Debug("Clear Preset $preset");
|
||||||
Debug( "Clear Preset $preset" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetSet
|
sub presetSet {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $preset = $self->getParam($params, 'preset');
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
Debug("Set Preset $preset");
|
||||||
Debug( "Set Preset $preset" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?setserverpresetno=$preset";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?setserverpresetno=$preset";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetGoto
|
sub presetGoto {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
my $params = shift;
|
||||||
my $params = shift;
|
my $preset = $self->getParam($params, 'preset');
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
Debug("Goto Preset $preset");
|
||||||
Debug( "Goto Preset $preset" );
|
my $cmd = "/axis-cgi/com/ptz.cgi?gotoserverpresetno=$preset";
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?gotoserverpresetno=$preset";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetHome
|
sub presetHome {
|
||||||
{
|
my $self = shift;
|
||||||
my $self = shift;
|
Debug('Home Preset');
|
||||||
Debug( "Home Preset" );
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=home';
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=home";
|
$self->sendCmd($cmd);
|
||||||
$self->sendCmd( $cmd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -0,0 +1,310 @@
|
||||||
|
# =========================================================================r
|
||||||
|
#
|
||||||
|
# ZoneMinder D-Link DCS-5020L IP Control Protocol Module, $Date: $, $Revision: $
|
||||||
|
# Copyright (C) 2013 Art Scheel
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# This module contains the implementation of the D-Link DCS-5020L IP camera control
|
||||||
|
# protocol.
|
||||||
|
#
|
||||||
|
package ZoneMinder::Control::DCS5020L;
|
||||||
|
|
||||||
|
use 5.006;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
require ZoneMinder::Base;
|
||||||
|
require ZoneMinder::Control;
|
||||||
|
|
||||||
|
our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
|
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# D-Link DCS-5020L Control Protocol
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
|
||||||
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
|
use Time::HiRes qw( usleep );
|
||||||
|
|
||||||
|
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 sendCmd {
|
||||||
|
my $self = shift;
|
||||||
|
my $cmd = shift;
|
||||||
|
my $cgi = shift;
|
||||||
|
|
||||||
|
my $result = undef;
|
||||||
|
|
||||||
|
printMsg($cmd, 'Tx');
|
||||||
|
|
||||||
|
my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" );
|
||||||
|
$req->content($cmd);
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
if ( $res->is_success ) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
Error("Error check failed: '".$res->status_line()."'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub move {
|
||||||
|
my $self = shift;
|
||||||
|
my $dir = shift;
|
||||||
|
my $panStep = shift;
|
||||||
|
my $tiltStep = shift;
|
||||||
|
my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir";
|
||||||
|
$self->sendCmd($cmd, 'pantiltcontrol');
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRel {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $panStep = $self->getParam($params, 'panstep', 0);
|
||||||
|
my $tiltStep = $self->getParam($params, 'tiltstep', 0);
|
||||||
|
my $dir = shift;
|
||||||
|
$self->move( $dir, $panStep, $tiltStep );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUpLeft {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUp {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUpRight {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelLeft {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelRight {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDownLeft {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDown {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDownRight {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
$self->moveRel($params, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
# moves the camera to center on the point that the user clicked on in the video image.
|
||||||
|
# This isn't extremely accurate but good enough for most purposes
|
||||||
|
sub moveMap {
|
||||||
|
# if the camera moves too much or too little, try increasing or decreasing this value
|
||||||
|
my $f = 11;
|
||||||
|
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $xcoord = $self->getParam( $params, 'xcoord' );
|
||||||
|
my $ycoord = $self->getParam( $params, 'ycoord' );
|
||||||
|
|
||||||
|
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
|
||||||
|
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
|
||||||
|
|
||||||
|
my $direction;
|
||||||
|
my $horSteps;
|
||||||
|
my $verSteps;
|
||||||
|
if ($hor < 50 && $ver < 50) {
|
||||||
|
# up left
|
||||||
|
$horSteps = (50 - $hor) / $f;
|
||||||
|
$verSteps = (50 - $ver) / $f;
|
||||||
|
$direction = 0;
|
||||||
|
} elsif ($hor >= 50 && $ver < 50) {
|
||||||
|
# up right
|
||||||
|
$horSteps = ($hor - 50) / $f;
|
||||||
|
$verSteps = (50 - $ver) / $f;
|
||||||
|
$direction = 2;
|
||||||
|
} elsif ($hor < 50 && $ver >= 50) {
|
||||||
|
# down left
|
||||||
|
$horSteps = (50 - $hor) / $f;
|
||||||
|
$verSteps = ($ver - 50) / $f;
|
||||||
|
$direction = 6;
|
||||||
|
} elsif ($hor >= 50 && $ver >= 50) {
|
||||||
|
# down right
|
||||||
|
$horSteps = ($hor - 50) / $f;
|
||||||
|
$verSteps = ($ver - 50) / $f;
|
||||||
|
$direction = 8;
|
||||||
|
}
|
||||||
|
my $v = int($verSteps + .5);
|
||||||
|
my $h = int($horSteps + .5);
|
||||||
|
Debug("Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction");
|
||||||
|
$self->move($direction, $h, $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetClear {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset = $self->getParam( $params, 'preset' );
|
||||||
|
Debug( "Clear Preset $preset" );
|
||||||
|
my $cmd = "ClearPosition=$preset";
|
||||||
|
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetSet {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset = $self->getParam( $params, 'preset' );
|
||||||
|
Debug( "Set Preset $preset" );
|
||||||
|
my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset";
|
||||||
|
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetGoto {
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset = $self->getParam( $params, 'preset' );
|
||||||
|
Debug( "Goto Preset $preset" );
|
||||||
|
my $cmd = "PanTiltPresetPositionMove=$preset";
|
||||||
|
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetHome {
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Home Preset" );
|
||||||
|
$self->move( 4, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
# IR Controls
|
||||||
|
#
|
||||||
|
# wake = IR on
|
||||||
|
# sleep = IR off
|
||||||
|
# reset = IR auto
|
||||||
|
|
||||||
|
sub setDayNightMode {
|
||||||
|
my $self = shift;
|
||||||
|
my $mode = shift;
|
||||||
|
my $cmd = "DayNightMode=$mode&ConfigReboot=No";
|
||||||
|
$self->sendCmd($cmd, 'daynight');
|
||||||
|
}
|
||||||
|
|
||||||
|
sub wake {
|
||||||
|
my $self = shift;
|
||||||
|
Debug('Wake - IR on');
|
||||||
|
$self->setDayNightMode(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub sleep {
|
||||||
|
my $self = shift;
|
||||||
|
Debug('Sleep - IR off');
|
||||||
|
$self->setDayNightMode(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reset {
|
||||||
|
my $self = shift;
|
||||||
|
Debug('Reset - IR auto');
|
||||||
|
$self->setDayNightMode(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
ZoneMinder::Control::DCS5020L - Perl extension for DCS-5020L
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use ZoneMinder::Database;
|
||||||
|
DLINK DCS-5020L
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
ZoneMinder driver for the D-Link consumer camera DCS-5020L.
|
||||||
|
|
||||||
|
=head2 EXPORT
|
||||||
|
|
||||||
|
None by default.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
See if there are better instructions for the DCS-5020L at
|
||||||
|
http://www.zoneminder.com/wiki/index.php/Dlink
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Art Scheel <lt>ascheel (at) gmail<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
Copyright (C) 2018 ZoneMinder LLC
|
||||||
|
|
||||||
|
This library 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 library 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.
|
||||||
|
|
||||||
|
=cut
|
|
@ -95,9 +95,9 @@ sub PutCmd {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
my $content = shift;
|
my $content = shift;
|
||||||
my $req = HTTP::Request->new(PUT => "$self->{BaseURL}/$cmd");
|
my $req = HTTP::Request->new(PUT => $self->{BaseURL}.'/'.$cmd);
|
||||||
if ( defined($content) ) {
|
if ( defined($content) ) {
|
||||||
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
$req->content_type('application/x-www-form-urlencoded; charset=UTF-8');
|
||||||
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||||
}
|
}
|
||||||
my $res = $self->{UA}->request($req);
|
my $res = $self->{UA}->request($req);
|
||||||
|
@ -135,13 +135,13 @@ sub PutCmd {
|
||||||
# Check for username/password
|
# Check for username/password
|
||||||
#
|
#
|
||||||
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||||
Info("Check username/password is correct");
|
Info('Check username/password is correct');
|
||||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
||||||
Info("No password in Control Address. Should there be one?");
|
Info('No password in Control Address. Should there be one?');
|
||||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||||
Info("Password but no username in Control Address.");
|
Info('Password but no username in Control Address.');
|
||||||
} else {
|
} else {
|
||||||
Info("Missing username and password in Control Address.");
|
Info('Missing username and password in Control Address.');
|
||||||
}
|
}
|
||||||
Fatal($res->status_line);
|
Fatal($res->status_line);
|
||||||
}
|
}
|
||||||
|
@ -382,7 +382,7 @@ sub irisRelOpen {
|
||||||
sub reset {
|
sub reset {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->PutCmd("ISAPI/System/reboot");
|
$self->PutCmd('ISAPI/System/reboot');
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module, $Date: 2008-09-13 17:30:29 +0000 (Sat, 13 Sept 2008) $, $Revision: 2229 $
|
# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module
|
||||||
# Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com)
|
# Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com)
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
|
@ -43,8 +43,6 @@ our @ISA = qw(ZoneMinder::Control);
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
use Time::HiRes qw( usleep );
|
|
||||||
|
|
||||||
sub open {
|
sub open {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
@ -52,58 +50,50 @@ sub open {
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||||
|
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
}
|
}
|
||||||
|
|
||||||
sub printMsg {
|
|
||||||
my $self = shift;
|
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
Debug( $msg."[".$msg_len."]" );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub sendCmd {
|
sub sendCmd {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
|
|
||||||
my $result = undef;
|
my $result = undef;
|
||||||
|
|
||||||
printMsg( $cmd, "Tx" );
|
$self->printMsg($cmd, 'Tx');
|
||||||
|
|
||||||
my $url;
|
my $url;
|
||||||
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
|
if ( $self->{Monitor}->{ControlAddress} =~ /^http/i ) {
|
||||||
$url = $self->{Monitor}->{ControlAddress}.$cmd;
|
$url = $self->{Monitor}->{ControlAddress}.$cmd;
|
||||||
} else {
|
} else {
|
||||||
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
|
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
|
||||||
} # en dif
|
} # end if
|
||||||
my $req = HTTP::Request->new( GET=>$url );
|
my $req = HTTP::Request->new(GET=>$url);
|
||||||
|
|
||||||
my $res = $self->{ua}->request($req);
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
if ( $res->is_success ) {
|
if ( $res->is_success ) {
|
||||||
$result = !undef;
|
$result = !undef;
|
||||||
} else {
|
} else {
|
||||||
Error( "Error check failed: '".$res->status_line()."'" );
|
Error('Error check failed: \''.$res->status_line().'\'');
|
||||||
}
|
}
|
||||||
|
|
||||||
return( $result );
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset {
|
sub reset {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Camera Reset" );
|
Debug('Camera Reset');
|
||||||
my $cmd = "/admin/ptctl.cgi?move=reset";
|
my $cmd = '/admin/ptctl.cgi?move=reset';
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveMap {
|
sub moveMap {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $xcoord = $self->getParam( $params, 'xcoord' );
|
my $xcoord = $self->getParam($params, 'xcoord');
|
||||||
my $ycoord = $self->getParam( $params, 'ycoord' );
|
my $ycoord = $self->getParam($params, 'ycoord');
|
||||||
|
|
||||||
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
|
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
|
||||||
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
|
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
|
||||||
|
@ -125,81 +115,81 @@ sub moveMap {
|
||||||
elsif ( $hor > 50 ) {
|
elsif ( $hor > 50 ) {
|
||||||
# right
|
# right
|
||||||
$horSteps = (($hor - 50) / 50) * $maxhor;
|
$horSteps = (($hor - 50) / 50) * $maxhor;
|
||||||
$horDir = "right";
|
$horDir = 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
# Vertical movement
|
# Vertical movement
|
||||||
if ( $ver < 50 ) {
|
if ( $ver < 50 ) {
|
||||||
# up
|
# up
|
||||||
$verSteps = ((50 - $ver) / 50) * $maxver;
|
$verSteps = ((50 - $ver) / 50) * $maxver;
|
||||||
$verDir = "up";
|
$verDir = 'up';
|
||||||
}
|
}
|
||||||
elsif ( $ver > 50 ) {
|
elsif ( $ver > 50 ) {
|
||||||
# down
|
# down
|
||||||
$verSteps = (($ver - 50) / 50) * $maxver;
|
$verSteps = (($ver - 50) / 50) * $maxver;
|
||||||
$verDir = "down";
|
$verDir = 'down';
|
||||||
}
|
}
|
||||||
|
|
||||||
my $v = int($verSteps);
|
my $v = int($verSteps);
|
||||||
my $h = int($horSteps);
|
my $h = int($horSteps);
|
||||||
|
|
||||||
Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir");
|
Debug("Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir");
|
||||||
my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h";
|
my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h";
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
$cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v";
|
$cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v";
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUp {
|
sub moveRelUp {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam( $params, 'tiltstep' );
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug( "Step Up $step" );
|
Debug("Step Up $step");
|
||||||
my $cmd = "/admin/ptctl.cgi?move=up";
|
my $cmd = '/admin/ptctl.cgi?move=up';
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDown {
|
sub moveRelDown {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam( $params, 'tiltstep' );
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug( "Step Down $step" );
|
Debug("Step Down $step");
|
||||||
my $cmd = "/admin/ptctl.cgi?move=down";
|
my $cmd = '/admin/ptctl.cgi?move=down';
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelLeft {
|
sub moveRelLeft {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam( $params, 'panstep' );
|
my $step = $self->getParam($params, 'panstep');
|
||||||
|
|
||||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||||
Debug( "Stepping Right because flipped horizontally " );
|
Debug('Stepping Right because flipped horizontally');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||||
} else {
|
} else {
|
||||||
Debug( "Step Left" );
|
Debug('Step Left');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelRight {
|
sub moveRelRight {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam( $params, 'panstep' );
|
my $step = $self->getParam($params, 'panstep');
|
||||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||||
Debug( "Stepping Left because flipped horizontally " );
|
Debug('Stepping Left because flipped horizontally');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||||
} else {
|
} else {
|
||||||
Debug( "Step Right" );
|
Debug('Step Right');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetClear {
|
sub presetClear {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
my $preset = $self->getParam($params, 'preset');
|
||||||
Debug( "Clear Preset $preset" );
|
Debug("Clear Preset $preset");
|
||||||
#my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
|
#my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
|
||||||
#$self->sendCmd( $cmd );
|
#$self->sendCmd( $cmd );
|
||||||
}
|
}
|
||||||
|
@ -207,26 +197,26 @@ sub presetClear {
|
||||||
sub presetSet {
|
sub presetSet {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
my $preset = $self->getParam($params, 'preset');
|
||||||
Debug( "Set Preset $preset" );
|
Debug("Set Preset $preset");
|
||||||
my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset";
|
my $cmd = '/admin/ptctl.cgi?position=' . ($preset - 1) . "&positionname=zm$preset";
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd( $cmd );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetGoto {
|
sub presetGoto {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam( $params, 'preset' );
|
my $preset = $self->getParam($params, 'preset');
|
||||||
Debug( "Goto Preset $preset" );
|
Debug("Goto Preset $preset");
|
||||||
my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1);
|
my $cmd = '/admin/ptctl.cgi?move=p'.($preset - 1);
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetHome {
|
sub presetHome {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Home Preset" );
|
Debug('Home Preset');
|
||||||
my $cmd = "/admin/ptctl.cgi?move=h";
|
my $cmd = '/admin/ptctl.cgi?move=h';
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -301,7 +301,7 @@ sub zmMemVerify {
|
||||||
}
|
}
|
||||||
|
|
||||||
return !undef;
|
return !undef;
|
||||||
}
|
} # end sub zmMemVerify
|
||||||
|
|
||||||
sub zmMemRead {
|
sub zmMemRead {
|
||||||
my $monitor = shift;
|
my $monitor = shift;
|
||||||
|
@ -375,10 +375,8 @@ sub zmMemInvalidate {
|
||||||
my $mem_key = zmMemKey($monitor);
|
my $mem_key = zmMemKey($monitor);
|
||||||
if ( $mem_key ) {
|
if ( $mem_key ) {
|
||||||
zmMemDetach($monitor);
|
zmMemDetach($monitor);
|
||||||
} else {
|
|
||||||
Warning('no memkey in zmMemInvalidate');
|
|
||||||
}
|
}
|
||||||
}
|
} # end sub zmMemInvalidate
|
||||||
|
|
||||||
sub zmMemTidy {
|
sub zmMemTidy {
|
||||||
zmMemClean();
|
zmMemClean();
|
||||||
|
@ -504,10 +502,10 @@ sub zmHasAlarmed {
|
||||||
my $last_event_id = shift;
|
my $last_event_id = shift;
|
||||||
|
|
||||||
my ( $state, $last_event ) = zmMemRead($monitor,
|
my ( $state, $last_event ) = zmMemRead($monitor,
|
||||||
['shared_data:state' ,'shared_data:last_event']
|
['shared_data:state', 'shared_data:last_event']
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( $state == STATE_ALARM || $state == STATE_ALERT ) {
|
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
|
||||||
return $last_event;
|
return $last_event;
|
||||||
} elsif( $last_event != $last_event_id ) {
|
} elsif( $last_event != $last_event_id ) {
|
||||||
return $last_event;
|
return $last_event;
|
||||||
|
|
|
@ -51,7 +51,7 @@ our %EXPORT_TAGS = (
|
||||||
);
|
);
|
||||||
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} } );
|
||||||
|
|
||||||
our @EXPORT = @EXPORT_OK;
|
our @EXPORT = @EXPORT_OK;
|
||||||
|
|
||||||
|
@ -77,17 +77,17 @@ sub zmMemAttach {
|
||||||
my ( $monitor, $size ) = @_;
|
my ( $monitor, $size ) = @_;
|
||||||
|
|
||||||
if ( !$size ) {
|
if ( !$size ) {
|
||||||
Error("No size passed to zmMemAttach for monitor $$monitor{Id}");
|
Error('No size passed to zmMemAttach for monitor '.$$monitor{Id});
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
if ( defined($monitor->{MMapAddr}) ) {
|
if ( defined($monitor->{MMapAddr}) ) {
|
||||||
Debug("zmMemAttach already attached at $monitor->{MMapAddr}");
|
Debug("zmMemAttach already attached at $monitor->{MMapAddr} for $$monitor{Id}");
|
||||||
return !undef;
|
return !undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $mmap_file = $Config{ZM_PATH_MAP}.'/zm.mmap.'.$monitor->{Id};
|
my $mmap_file = $Config{ZM_PATH_MAP}.'/zm.mmap.'.$monitor->{Id};
|
||||||
if ( ! -e $mmap_file ) {
|
if ( ! -e $mmap_file ) {
|
||||||
Error("Memory map file '$mmap_file' does not exist. zmc might not be running.");
|
Error("Memory map file '$mmap_file' does not exist in zmMemAttach. zmc might not be running.");
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
my $mmap_file_size = -s $mmap_file;
|
my $mmap_file_size = -s $mmap_file;
|
||||||
|
@ -119,18 +119,24 @@ sub zmMemDetach {
|
||||||
|
|
||||||
if ( $monitor->{MMap} ) {
|
if ( $monitor->{MMap} ) {
|
||||||
if ( ! munmap(${$monitor->{MMap}}) ) {
|
if ( ! munmap(${$monitor->{MMap}}) ) {
|
||||||
Warn( "Unable to munmap for monitor $$monitor{Id}\n");
|
Warn("Unable to munmap for monitor $$monitor{Id}");
|
||||||
}
|
}
|
||||||
delete $monitor->{MMap};
|
delete $monitor->{MMap};
|
||||||
|
} else {
|
||||||
|
Warn("No MMap for $$monitor{Id}");
|
||||||
}
|
}
|
||||||
if ( $monitor->{MMapAddr} ) {
|
if ( $monitor->{MMapAddr} ) {
|
||||||
delete $monitor->{MMapAddr};
|
delete $monitor->{MMapAddr};
|
||||||
|
} else {
|
||||||
|
Warn("No MMapAddr in $$monitor{Id}");
|
||||||
}
|
}
|
||||||
if ( $monitor->{MMapHandle} ) {
|
if ( $monitor->{MMapHandle} ) {
|
||||||
close($monitor->{MMapHandle});
|
close($monitor->{MMapHandle});
|
||||||
delete $monitor->{MMapHandle};
|
delete $monitor->{MMapHandle};
|
||||||
|
} else {
|
||||||
|
Warn("No MMapHandle in $$monitor{Id}");
|
||||||
}
|
}
|
||||||
}
|
} # end sub zmMemDetach
|
||||||
|
|
||||||
sub zmMemGet {
|
sub zmMemGet {
|
||||||
my $monitor = shift;
|
my $monitor = shift;
|
||||||
|
@ -162,7 +168,7 @@ sub zmMemPut {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zmMemClean {
|
sub zmMemClean {
|
||||||
Debug("Removing memory map files");
|
Debug('Removing memory map files');
|
||||||
my $mapPath = $Config{ZM_PATH_MAP}.'/zm.mmap.*';
|
my $mapPath = $Config{ZM_PATH_MAP}.'/zm.mmap.*';
|
||||||
foreach my $mapFile( glob( $mapPath ) ) {
|
foreach my $mapFile( glob( $mapPath ) ) {
|
||||||
( $mapFile ) = $mapFile =~ /^(.+)$/;
|
( $mapFile ) = $mapFile =~ /^(.+)$/;
|
||||||
|
|
|
@ -36,9 +36,172 @@ require ZoneMinder::Server;
|
||||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
use parent qw(ZoneMinder::Object);
|
use parent qw(ZoneMinder::Object);
|
||||||
|
|
||||||
use vars qw/ $table $primary_key /;
|
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||||
$table = 'Monitors';
|
$table = 'Monitors';
|
||||||
$primary_key = 'Id';
|
$serial = $primary_key = 'Id';
|
||||||
|
%fields = map { $_ => $_ } qw(
|
||||||
|
Id
|
||||||
|
Name
|
||||||
|
Notes
|
||||||
|
ServerId
|
||||||
|
StorageId
|
||||||
|
Type
|
||||||
|
Function
|
||||||
|
Enabled
|
||||||
|
LinkedMonitors
|
||||||
|
Triggers
|
||||||
|
Device
|
||||||
|
Channel
|
||||||
|
Format
|
||||||
|
V4LMultiBuffer
|
||||||
|
V4LCapturesPerFrame
|
||||||
|
Protocol
|
||||||
|
Method
|
||||||
|
Host
|
||||||
|
Port
|
||||||
|
SubPath
|
||||||
|
Path
|
||||||
|
Options
|
||||||
|
User
|
||||||
|
Pass
|
||||||
|
Width
|
||||||
|
Height
|
||||||
|
Colours
|
||||||
|
Palette
|
||||||
|
Orientation
|
||||||
|
Deinterlacing
|
||||||
|
DecoderHWAccelName
|
||||||
|
DecoderHWAccelDevice
|
||||||
|
SaveJPEGs
|
||||||
|
VideoWriter
|
||||||
|
OutputCodec
|
||||||
|
OutputContainer
|
||||||
|
EncoderParameters
|
||||||
|
RecordAudio
|
||||||
|
RTSPDescribe
|
||||||
|
Brightness
|
||||||
|
Contrast
|
||||||
|
Hue
|
||||||
|
Colour
|
||||||
|
EventPrefix
|
||||||
|
LabelFormat
|
||||||
|
LabelX
|
||||||
|
LabelY
|
||||||
|
LabelSize
|
||||||
|
ImageBufferCount
|
||||||
|
WarmupCount
|
||||||
|
PreEventCount
|
||||||
|
PostEventCount
|
||||||
|
StreamReplayBuffer
|
||||||
|
AlarmFrameCount
|
||||||
|
SectionLength
|
||||||
|
MinSectionLength
|
||||||
|
FrameSkip
|
||||||
|
MotionFrameSkip
|
||||||
|
AnalysisFPSLimit
|
||||||
|
AnalysisUpdateDelay
|
||||||
|
MaxFPS
|
||||||
|
AlarmMaxFPS
|
||||||
|
FPSReportInterval
|
||||||
|
RefBlendPerc
|
||||||
|
AlarmRefBlendPerc
|
||||||
|
Controllable
|
||||||
|
ControlId
|
||||||
|
ControlDevice
|
||||||
|
ControlAddress
|
||||||
|
AutoStopTimeout
|
||||||
|
TrackMotion
|
||||||
|
TrackDelay
|
||||||
|
ReturnLocation
|
||||||
|
ReturnDelay
|
||||||
|
DefaultRate
|
||||||
|
DefaultScale
|
||||||
|
SignalCheckPoints
|
||||||
|
SignalCheckColour
|
||||||
|
WebColour
|
||||||
|
Exif
|
||||||
|
Sequence
|
||||||
|
);
|
||||||
|
|
||||||
|
%defaults = (
|
||||||
|
ServerId => 0,
|
||||||
|
StorageId => 0,
|
||||||
|
Type => 'Ffmpeg',
|
||||||
|
Function => 'Mocord',
|
||||||
|
Enabled => 1,
|
||||||
|
LinkedMonitors => undef,
|
||||||
|
Device => '',
|
||||||
|
Channel => 0,
|
||||||
|
Format => 0,
|
||||||
|
V4LMultiBuffer => undef,
|
||||||
|
V4LCapturesPerFrame => 1,
|
||||||
|
Protocol => undef,
|
||||||
|
Method => '',
|
||||||
|
Host => undef,
|
||||||
|
Port => '',
|
||||||
|
SubPath => '',
|
||||||
|
Path => undef,
|
||||||
|
Options => undef,
|
||||||
|
User => undef,
|
||||||
|
Pass => undef,
|
||||||
|
Width => undef,
|
||||||
|
Height => undef,
|
||||||
|
Colours => 4,
|
||||||
|
Palette => 0,
|
||||||
|
Orientation => undef,
|
||||||
|
Deinterlacing => 0,
|
||||||
|
DecoderHWAccelName => undef,
|
||||||
|
DecoderHWAccelDevice => undef,
|
||||||
|
SaveJPEGs => 3,
|
||||||
|
VideoWriter => 0,
|
||||||
|
OutputCodec => undef,
|
||||||
|
OutputContainer => undef,
|
||||||
|
EncoderParameters => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||||
|
RecordAudio=>0,
|
||||||
|
RTSPDescribe=>0,
|
||||||
|
Brightness => -1,
|
||||||
|
Contrast => -1,
|
||||||
|
Hue => -1,
|
||||||
|
Colour => -1,
|
||||||
|
EventPrefix => 'Event-',
|
||||||
|
LabelFormat => '%N - %d/%m/%y %H:%M:%S',
|
||||||
|
LabelX => 0,
|
||||||
|
LabelY => 0,
|
||||||
|
LabelSize => 1,
|
||||||
|
ImageBufferCount => 20,
|
||||||
|
WarmupCount => 0,
|
||||||
|
PreEventCount => 5,
|
||||||
|
PostEventCount => 5,
|
||||||
|
StreamReplayBuffer => 0,
|
||||||
|
AlarmFrameCount => 1,
|
||||||
|
SectionLength => 600,
|
||||||
|
MinSectionLength => 10,
|
||||||
|
FrameSkip => 0,
|
||||||
|
MotionFrameSkip => 0,
|
||||||
|
AnalysisFPSLimit => undef,
|
||||||
|
AnalysisUpdateDelay => 0,
|
||||||
|
MaxFPS => undef,
|
||||||
|
AlarmMaxFPS => undef,
|
||||||
|
FPSReportInterval => 100,
|
||||||
|
RefBlendPerc => 6,
|
||||||
|
AlarmRefBlendPerc => 6,
|
||||||
|
Controllable => 0,
|
||||||
|
ControlId => undef,
|
||||||
|
ControlDevice => undef,
|
||||||
|
ControlAddress => undef,
|
||||||
|
AutoStopTimeout => undef,
|
||||||
|
TrackMotion => 0,
|
||||||
|
TrackDelay => undef,
|
||||||
|
ReturnLocation => -1,
|
||||||
|
ReturnDelay => undef,
|
||||||
|
DefaultRate => 100,
|
||||||
|
DefaultScale => 100,
|
||||||
|
SignalCheckPoints => 0,
|
||||||
|
SignalCheckColour => '#0000BE',
|
||||||
|
WebColour => '#ff0000',
|
||||||
|
Exif => 0,
|
||||||
|
Sequence => undef,
|
||||||
|
);
|
||||||
|
|
||||||
sub Server {
|
sub Server {
|
||||||
return new ZoneMinder::Server( $_[0]{ServerId} );
|
return new ZoneMinder::Server( $_[0]{ServerId} );
|
||||||
|
|
|
@ -255,15 +255,15 @@ sub discover {
|
||||||
sub profiles {
|
sub profiles {
|
||||||
my ( $client ) = @_;
|
my ( $client ) = @_;
|
||||||
|
|
||||||
my $endpoint = $client->get_endpoint('media');
|
my $media = $client->get_endpoint('media');
|
||||||
if ( ! $endpoint ) {
|
if ( ! $media ) {
|
||||||
print "No media enpoint for client.\n";
|
print "No media endpoint for client.\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $result = $endpoint->GetProfiles( { } ,, );
|
my $result = $media->GetProfiles( { } ,, );
|
||||||
if ( ! $result ) {
|
if ( ! $result ) {
|
||||||
print "No result from GetProfiles\n";
|
print "No result from GetProfiles.\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( $verbose ) {
|
if ( $verbose ) {
|
||||||
|
@ -272,48 +272,52 @@ sub profiles {
|
||||||
|
|
||||||
my $profiles = $result->get_Profiles();
|
my $profiles = $result->get_Profiles();
|
||||||
|
|
||||||
foreach my $profile ( @{ $profiles } ) {
|
foreach my $profile ( @{ $profiles } ) {
|
||||||
|
|
||||||
my $token = $profile->attr()->get_token() ;
|
my $token = $profile->attr()->get_token() ;
|
||||||
my $video_encoder_configuration = $profile->get_VideoEncoderConfiguration();
|
my $Name = $profile->get_Name();
|
||||||
if ( ! $video_encoder_configuration ) {
|
|
||||||
print "Unknown profile $token " . $profile->get_Name()."\n";
|
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
||||||
|
if ( ! $VideoEncoderConfiguration ) {
|
||||||
|
print "Unknown profile $token $Name.\n";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
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
|
||||||
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) {
|
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
|
||||||
$result = $client->get_endpoint('media')->GetStreamUri( {
|
my $StreamUri = $media->GetStreamUri( {
|
||||||
StreamSetup => { # ONVIF::Media::Types::StreamSetup
|
StreamSetup => { # ONVIF::Media::Types::StreamSetup
|
||||||
Stream => $streamtype, # StreamType
|
Stream => $streamtype, # StreamType
|
||||||
Transport => { # ONVIF::Media::Types::Transport
|
Transport => { # ONVIF::Media::Types::Transport
|
||||||
Protocol => 'RTSP', # TransportProtocol
|
Protocol => 'RTSP', # TransportProtocol
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProfileToken => $token, # ReferenceToken
|
ProfileToken => $token, # ReferenceToken
|
||||||
} ,, );
|
} );
|
||||||
last if $result;
|
next if ! ( $StreamUri and $StreamUri->can('get_MediaUri') );
|
||||||
}
|
my $MediaUri = $StreamUri->get_MediaUri();
|
||||||
die $result if not $result;
|
next if ! $MediaUri;
|
||||||
# print $result . "\n";
|
my $Uri = $MediaUri->get_Uri();
|
||||||
|
next if ! $Uri;
|
||||||
|
|
||||||
|
print join(', ', $token,
|
||||||
|
$Name,
|
||||||
|
$VideoEncoderConfiguration->get_Encoding(),
|
||||||
|
$VideoEncoderConfiguration->get_Resolution()->get_Width(),
|
||||||
|
$VideoEncoderConfiguration->get_Resolution()->get_Height(),
|
||||||
|
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
|
||||||
|
$Uri,
|
||||||
|
) . "\n";
|
||||||
|
} # end foreach streamtype
|
||||||
|
|
||||||
print $result->get_MediaUri()->get_Uri() .
|
|
||||||
"\n";
|
|
||||||
} # end foreach profile
|
} # end foreach profile
|
||||||
|
|
||||||
#
|
#
|
||||||
# use message parser without schema validation ???
|
# use message parser without schema validation ???
|
||||||
#
|
#
|
||||||
|
|
||||||
}
|
} # end sub profiles
|
||||||
|
|
||||||
sub move {
|
sub move {
|
||||||
my ($client, $dir) = @_;
|
my ($client, $dir) = @_;
|
||||||
|
@ -326,13 +330,22 @@ sub move {
|
||||||
|
|
||||||
sub metadata {
|
sub metadata {
|
||||||
my ( $client ) = @_;
|
my ( $client ) = @_;
|
||||||
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, );
|
my $media = $client->get_endpoint('media');
|
||||||
die $result if not $result;
|
die 'No media endpoint.' if !$media;
|
||||||
print $result . "\n";
|
|
||||||
|
|
||||||
$result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, );
|
my $result = $media->GetMetadataConfigurations( { } ,, );
|
||||||
die $result if not $result;
|
if ( ! $result ) {
|
||||||
print $result . "\n";
|
print "No MetaDataConfigurations\n" if $verbose;
|
||||||
|
} else {
|
||||||
|
print $result . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $media->GetVideoAnalyticsConfigurations( { } ,, );
|
||||||
|
if ( ! $result ) {
|
||||||
|
print "No VideoAnalyticsConfigurations\n" if $verbose;
|
||||||
|
} else {
|
||||||
|
print $result . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
|
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
|
||||||
# die $result if not $result;
|
# die $result if not $result;
|
||||||
|
|
|
@ -420,15 +420,15 @@ MAIN: while( $loop ) {
|
||||||
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
||||||
{
|
{
|
||||||
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
|
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
|
||||||
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . ' entries.' );
|
Debug('glob("'.$monitor_dir.'/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned '.(scalar @event_dirs).' entries.');
|
||||||
foreach my $event_dir ( @event_dirs ) {
|
foreach my $event_dir ( @event_dirs ) {
|
||||||
if ( ! -d $event_dir ) {
|
if ( ! -d $event_dir ) {
|
||||||
Debug( "$event_dir is not a dir. Skipping" );
|
Debug("$event_dir is not a dir. Skipping");
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
|
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
|
||||||
if ( ! $event_id ) {
|
if ( !$event_id ) {
|
||||||
Debug("Unable to parse date/event_id from $event_dir");
|
Debug('Unable to parse date/event_id from '.$event_dir);
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
|
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
|
||||||
|
@ -438,8 +438,9 @@ MAIN: while( $loop ) {
|
||||||
$Event->MonitorId( $monitor_dir );
|
$Event->MonitorId( $monitor_dir );
|
||||||
$Event->StorageId( $Storage->Id() );
|
$Event->StorageId( $Storage->Id() );
|
||||||
$Event->Path();
|
$Event->Path();
|
||||||
|
$Event->age();
|
||||||
Debug("Have event $$Event{Id} at $$Event{Path}");
|
Debug("Have event $$Event{Id} at $$Event{Path}");
|
||||||
$Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())) ) );
|
$Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path()))));
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,13 +467,13 @@ MAIN: while( $loop ) {
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
chdir( $Storage->Path() );
|
chdir( $Storage->Path() );
|
||||||
} # if USE_DEEP_STORAGE
|
} # if USE_DEEP_STORAGE
|
||||||
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir" );
|
Debug('Got '.int(keys(%$fs_events)).' filesystem events for monitor '.$monitor_dir);
|
||||||
|
|
||||||
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
|
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
|
||||||
if ( $cleaned ) {
|
if ( $cleaned ) {
|
||||||
Debug("First stage cleaning done. Restarting.");
|
Debug('First stage cleaning done. Restarting.');
|
||||||
redo MAIN;
|
redo MAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +485,7 @@ MAIN: while( $loop ) {
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
my @event_ids = keys %$fs_events;
|
my @event_ids = keys %$fs_events;
|
||||||
Debug('Have ' .scalar @event_ids . " events for monitor $monitor_id");
|
Debug('Have ' .scalar @event_ids . ' events for monitor '.$monitor_id);
|
||||||
|
|
||||||
foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) {
|
foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) {
|
||||||
|
|
||||||
|
@ -499,8 +500,8 @@ MAIN: while( $loop ) {
|
||||||
}
|
}
|
||||||
my $age = $Event->age();
|
my $age = $Event->age();
|
||||||
|
|
||||||
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
|
if ( $age and ($age > $Config{ZM_AUDIT_MIN_AGE}) ) {
|
||||||
aud_print( "Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old" );
|
aud_print("Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old");
|
||||||
if ( confirm() ) {
|
if ( confirm() ) {
|
||||||
$Event->delete_files();
|
$Event->delete_files();
|
||||||
$cleaned = 1;
|
$cleaned = 1;
|
||||||
|
@ -586,7 +587,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||||
} else {
|
} else {
|
||||||
Debug("$$Event{Id} Not found at $path");
|
Debug("$$Event{Id} Not found at $path");
|
||||||
}
|
}
|
||||||
}
|
} # end foreach Storage
|
||||||
if ( $Event->Archived() ) {
|
if ( $Event->Archived() ) {
|
||||||
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
||||||
next;
|
next;
|
||||||
|
@ -638,18 +639,13 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||||
Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}");
|
Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}");
|
||||||
$Event->StorageId($$fs_events{$db_event}->StorageId());
|
$Event->StorageId($$fs_events{$db_event}->StorageId());
|
||||||
}
|
}
|
||||||
if ( $$fs_events{$db_event}->StartTime() ne $Event->StartTime() ) {
|
if ( ! $Event->StartTime() ) {
|
||||||
Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}");
|
Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}");
|
||||||
if ( $$Event{Scheme} eq 'Deep' ) {
|
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
||||||
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
|
||||||
} else {
|
|
||||||
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
|
||||||
}
|
|
||||||
$Event->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$Event->save();
|
$Event->save();
|
||||||
}
|
} # end if Event exists in db and not in filesystem
|
||||||
} # end if ! in fs_events
|
} # end if ! in fs_events
|
||||||
} # foreach db_event
|
} # foreach db_event
|
||||||
} # end foreach db_monitor
|
} # end foreach db_monitor
|
||||||
|
|
|
@ -351,8 +351,14 @@ sub exportsql {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ARGV[0]) {
|
my $name = $ARGV[0];
|
||||||
$command .= qq( --where="Name = '$ARGV[0]'");
|
if ( $name ) {
|
||||||
|
if ( $name =~ /^([A-Za-z0-9 ,.&()\/\-]+)$/ ) { # Allow alphanumeric and " ,.&()/-"
|
||||||
|
$name = $1;
|
||||||
|
$command .= qq( --where="Name = '$name'");
|
||||||
|
} else {
|
||||||
|
print "Invalid characters in Name\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$command .= " zm Controls MonitorPresets";
|
$command .= " zm Controls MonitorPresets";
|
||||||
|
|
|
@ -108,6 +108,9 @@ if ( $options{command} ) {
|
||||||
Fatal("Unable to load control data for monitor $id");
|
Fatal("Unable to load control data for monitor $id");
|
||||||
}
|
}
|
||||||
my $protocol = $monitor->{Protocol};
|
my $protocol = $monitor->{Protocol};
|
||||||
|
if ( !$protocol ) {
|
||||||
|
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||||
|
}
|
||||||
|
|
||||||
if ( -x $protocol ) {
|
if ( -x $protocol ) {
|
||||||
# Protocol is actually a script!
|
# Protocol is actually a script!
|
||||||
|
|
|
@ -860,7 +860,7 @@ sub sendEmail {
|
||||||
From => $Config{ZM_FROM_EMAIL},
|
From => $Config{ZM_FROM_EMAIL},
|
||||||
To => $Config{ZM_EMAIL_ADDRESS},
|
To => $Config{ZM_EMAIL_ADDRESS},
|
||||||
Subject => $subject,
|
Subject => $subject,
|
||||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||||
Data => $body
|
Data => $body
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -962,7 +962,7 @@ sub sendMessage {
|
||||||
From => $Config{ZM_FROM_EMAIL},
|
From => $Config{ZM_FROM_EMAIL},
|
||||||
To => $Config{ZM_MESSAGE_ADDRESS},
|
To => $Config{ZM_MESSAGE_ADDRESS},
|
||||||
Subject => $subject,
|
Subject => $subject,
|
||||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||||
Data => $body
|
Data => $body
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use bytes;
|
use bytes;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
@EXTRA_PERL_LIB@
|
@EXTRA_PERL_LIB@
|
||||||
use ZoneMinder;
|
use ZoneMinder;
|
||||||
|
@ -34,6 +35,7 @@ use Sys::MemInfo qw(totalmem);
|
||||||
use Sys::CPU qw(cpu_count);
|
use Sys::CPU qw(cpu_count);
|
||||||
use POSIX qw(strftime uname);
|
use POSIX qw(strftime uname);
|
||||||
use JSON::MaybeXS;
|
use JSON::MaybeXS;
|
||||||
|
use Encode;
|
||||||
|
|
||||||
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||||
|
@ -43,6 +45,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
my $help = 0;
|
my $help = 0;
|
||||||
my $force = 0;
|
my $force = 0;
|
||||||
|
my $show = 0;
|
||||||
# Interval between version checks
|
# Interval between version checks
|
||||||
my $interval;
|
my $interval;
|
||||||
my $version;
|
my $version;
|
||||||
|
@ -50,6 +53,7 @@ my $version;
|
||||||
GetOptions(
|
GetOptions(
|
||||||
force => \$force,
|
force => \$force,
|
||||||
help => \$help,
|
help => \$help,
|
||||||
|
show => \$show,
|
||||||
interval => \$interval,
|
interval => \$interval,
|
||||||
version => \$version
|
version => \$version
|
||||||
);
|
);
|
||||||
|
@ -57,6 +61,14 @@ if ( $version ) {
|
||||||
print( ZoneMinder::Base::ZM_VERSION . "\n");
|
print( ZoneMinder::Base::ZM_VERSION . "\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
if ($show) {
|
||||||
|
my %telemetry;
|
||||||
|
my $dbh = zmDbConnect();
|
||||||
|
collectData($dbh, \%telemetry);
|
||||||
|
my $result = jsonEncode(\%telemetry);
|
||||||
|
print ($result);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
if ( $help ) {
|
if ( $help ) {
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
@ -87,21 +99,9 @@ while( 1 ) {
|
||||||
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);
|
collectData($dbh,\%telemetry);
|
||||||
@telemetry{qw(city region country latitude longitude)} = getGeo();
|
|
||||||
$telemetry{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime());
|
|
||||||
$telemetry{monitor_count} = countQuery($dbh,'Monitors');
|
|
||||||
$telemetry{event_count} = countQuery($dbh,'Events');
|
|
||||||
$telemetry{architecture} = runSysCmd('uname -p');
|
|
||||||
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
|
|
||||||
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
|
||||||
$telemetry{system_memory} = totalmem();
|
|
||||||
$telemetry{processor_count} = cpu_count();
|
|
||||||
$telemetry{monitors} = getMonitorRef($dbh);
|
|
||||||
|
|
||||||
Info('Sending data to ZoneMinder Telemetry server.');
|
|
||||||
|
|
||||||
my $result = jsonEncode(\%telemetry);
|
my $result = jsonEncode(\%telemetry);
|
||||||
|
|
||||||
if ( sendData($result) ) {
|
if ( sendData($result) ) {
|
||||||
|
@ -124,6 +124,24 @@ print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', loc
|
||||||
# SUBROUTINES #
|
# SUBROUTINES #
|
||||||
###############
|
###############
|
||||||
|
|
||||||
|
# collect data to send
|
||||||
|
sub collectData {
|
||||||
|
my $dbh = shift;
|
||||||
|
my $telemetry = shift;
|
||||||
|
$telemetry->{uuid} = getUUID($dbh);
|
||||||
|
($telemetry->{city},$telemetry->{region},$telemetry->{country},$telemetry->{latitude},$telemetry->{longitude})=getGeo();
|
||||||
|
$telemetry->{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime());
|
||||||
|
$telemetry->{monitor_count} = countQuery($dbh,'Monitors');
|
||||||
|
$telemetry->{event_count} = countQuery($dbh,'Events');
|
||||||
|
$telemetry->{architecture} = runSysCmd('uname -p');
|
||||||
|
($telemetry->{kernel}, $telemetry->{distro}, $telemetry->{version}) = getDistro();
|
||||||
|
$telemetry->{zm_version} = ZoneMinder::Base::ZM_VERSION;
|
||||||
|
$telemetry->{system_memory} = totalmem();
|
||||||
|
$telemetry->{processor_count} = cpu_count();
|
||||||
|
$telemetry->{use_event_server} = $Config{ZM_OPT_USE_EVENTNOTIFICATION};
|
||||||
|
$telemetry->{monitors} = getMonitorRef($dbh);
|
||||||
|
}
|
||||||
|
|
||||||
# Find, verify, then run the supplied system command
|
# Find, verify, then run the supplied system command
|
||||||
sub runSysCmd {
|
sub runSysCmd {
|
||||||
my $msg = shift;
|
my $msg = shift;
|
||||||
|
@ -166,7 +184,7 @@ sub sendData {
|
||||||
$req->header('content-length' => length($msg));
|
$req->header('content-length' => length($msg));
|
||||||
$req->header('connection' => 'Close');
|
$req->header('connection' => 'Close');
|
||||||
|
|
||||||
$req->content($msg);
|
$req->content(encode('UTF-8',$msg));
|
||||||
|
|
||||||
my $resp = $ua->request($req);
|
my $resp = $ua->request($req);
|
||||||
my $resp_msg = $resp->decoded_content;
|
my $resp_msg = $resp->decoded_content;
|
||||||
|
@ -196,7 +214,7 @@ sub getUUID {
|
||||||
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
|
||||||
$sql = q`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();
|
||||||
|
@ -232,9 +250,9 @@ sub countQuery {
|
||||||
my $dbh = shift;
|
my $dbh = shift;
|
||||||
my $table = shift;
|
my $table = shift;
|
||||||
|
|
||||||
my $sql = "SELECT count(*) FROM $table";
|
my $sql = "SELECT count(*) FROM `$table`";
|
||||||
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 $count = $sth->fetchrow_array();
|
my $count = $sth->fetchrow_array();
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
|
||||||
|
@ -245,7 +263,7 @@ sub countQuery {
|
||||||
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({});
|
||||||
|
@ -363,7 +381,7 @@ zmtelemetry.pl - Send usage information to the ZoneMinder development team
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
zmtelemetry.pl [--force] [--help] [--interval=seconds] [--version]
|
zmtelemetry.pl [--force] [--help] [--show] [--interval=seconds] [--version]
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
@ -380,6 +398,7 @@ console under Options.
|
||||||
--force Force the script to upload it's data instead of waiting
|
--force Force the script to upload it's data instead of waiting
|
||||||
for the defined interval since last upload.
|
for the defined interval since last upload.
|
||||||
--help Display usage information
|
--help Display usage information
|
||||||
|
--show Displays telemetry data that is sent to zoneminder
|
||||||
--interval Override the default configured interval since last upload.
|
--interval Override the default configured interval since last upload.
|
||||||
The value should be given in seconds, but can be an expression
|
The value should be given in seconds, but can be an expression
|
||||||
such as 24*60*60.
|
such as 24*60*60.
|
||||||
|
|
|
@ -329,14 +329,13 @@ sub loadMonitor {
|
||||||
} # end sub loadMonitor
|
} # end sub loadMonitor
|
||||||
|
|
||||||
sub loadMonitors {
|
sub loadMonitors {
|
||||||
Debug('Loading monitors');
|
|
||||||
$monitor_reload_time = time();
|
$monitor_reload_time = time();
|
||||||
|
|
||||||
my %new_monitors = ();
|
my %new_monitors = ();
|
||||||
|
|
||||||
my $sql = q`SELECT * FROM Monitors
|
my $sql = 'SELECT * FROM `Monitors`
|
||||||
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )`.
|
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect\' )'.
|
||||||
( $Config{ZM_SERVER_ID} ? ' AND ServerId=?' : '' )
|
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
|
||||||
;
|
;
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
|
@ -357,11 +356,16 @@ sub handleMessage {
|
||||||
my $connection = shift;
|
my $connection = shift;
|
||||||
my $message = shift;
|
my $message = shift;
|
||||||
|
|
||||||
|
# CUA - Axis camera send the message quoted with"
|
||||||
|
# CUA - Also Axis camera cannot save the plus sign which
|
||||||
|
$message =~ s/^\"//g;
|
||||||
|
$message =~ s/\"$//g;
|
||||||
|
|
||||||
my ( $id, $action, $score, $cause, $text, $showtext )
|
my ( $id, $action, $score, $cause, $text, $showtext )
|
||||||
= split( /\|/, $message );
|
= split( /\|/, $message );
|
||||||
$score = 0 if ( !defined($score) );
|
$score = 0 if !defined($score);
|
||||||
$cause = '' if ( !defined($cause) );
|
$cause = '' if !defined($cause);
|
||||||
$text = '' if ( !defined($text) );
|
$text = '' if !defined($text);
|
||||||
|
|
||||||
my $monitor = $monitors{$id};
|
my $monitor = $monitors{$id};
|
||||||
if ( !$monitor ) {
|
if ( !$monitor ) {
|
||||||
|
@ -373,7 +377,7 @@ sub handleMessage {
|
||||||
next if !zmMemVerify($monitor);
|
next if !zmMemVerify($monitor);
|
||||||
|
|
||||||
Debug("Handling action '$action'");
|
Debug("Handling action '$action'");
|
||||||
if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) {
|
if ( $action =~ /^(enable|disable)(?:[\+ ](\d+))?$/ ) {
|
||||||
my $state = $1;
|
my $state = $1;
|
||||||
my $delay = $2;
|
my $delay = $2;
|
||||||
if ( $state eq 'enable' ) {
|
if ( $state eq 'enable' ) {
|
||||||
|
|
|
@ -847,9 +847,9 @@ if ( $version ) {
|
||||||
}
|
}
|
||||||
$cascade = !undef;
|
$cascade = !undef;
|
||||||
}
|
}
|
||||||
if ( $cascade || $version eq "1.24.4" ) {
|
if ( $cascade || $version eq '1.24.4' ) {
|
||||||
# Patch the database
|
# Patch the database
|
||||||
patchDB( $dbh, "1.24.4" );
|
patchDB($dbh, '1.24.4');
|
||||||
|
|
||||||
# Copy the FTP specific values to the new general config
|
# Copy the FTP specific values to the new general config
|
||||||
my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'";
|
my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'";
|
||||||
|
@ -863,12 +863,12 @@ if ( $version ) {
|
||||||
}
|
}
|
||||||
$cascade = !undef;
|
$cascade = !undef;
|
||||||
}
|
}
|
||||||
if ( $cascade || $version lt "1.26.0" ) {
|
if ( $cascade || $version lt '1.26.0' ) {
|
||||||
my $sth = $dbh->prepare_cached( 'select * from Monitors LIMIT 0,1' );
|
my $sth = $dbh->prepare_cached('SELECT * FROM Monitors LIMIT 0,1');
|
||||||
die "Error: " . $dbh->errstr . "\n" unless ($sth);
|
die "Error: " . $dbh->errstr . "\n" unless ($sth);
|
||||||
die "Error: " . $sth->errstr . "\n" unless ($sth->execute);
|
die "Error: " . $sth->errstr . "\n" unless ($sth->execute);
|
||||||
|
|
||||||
my $columns = $sth->{'NAME'};
|
my $columns = $sth->{NAME};
|
||||||
if ( ! grep(/^Colours$/, @$columns ) ) {
|
if ( ! grep(/^Colours$/, @$columns ) ) {
|
||||||
$dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;});
|
$dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;});
|
||||||
} # end if
|
} # end if
|
||||||
|
@ -898,28 +898,31 @@ if ( $version ) {
|
||||||
die "Should have found upgrade scripts at $updateDir\n";
|
die "Should have found upgrade scripts at $updateDir\n";
|
||||||
} # end if
|
} # end if
|
||||||
|
|
||||||
|
my $sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_DB_VERSION'";
|
||||||
|
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
|
|
||||||
foreach my $patch ( @files ) {
|
foreach my $patch ( @files ) {
|
||||||
my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/;
|
my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/;
|
||||||
#PP make sure we use version compare
|
#PP make sure we use version compare
|
||||||
if ( version->parse('v' . $v) > version->parse('v' . $version) ) {
|
if ( version->parse('v'.$v) > version->parse('v'.$version) ) {
|
||||||
print( "Upgrading DB to $v from $version\n" );
|
print("Upgrading DB to $v from $version\n");
|
||||||
patchDB( $dbh, $v );
|
if ( patchDB($dbh, $v) ) {
|
||||||
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'";
|
my $res = $sth->execute($version) or die( "Can't execute: ".$sth->errstr() );
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
}
|
||||||
my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() );
|
|
||||||
$sth->finish();
|
|
||||||
#patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch );
|
#patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch );
|
||||||
} # end if newer version
|
} # end if newer version
|
||||||
} # end foreach patchfile
|
} # end foreach patchfile
|
||||||
|
|
||||||
|
$sth->finish();
|
||||||
$cascade = !undef;
|
$cascade = !undef;
|
||||||
} # end if
|
} # end if
|
||||||
|
|
||||||
if ( $cascade ) {
|
if ( $cascade ) {
|
||||||
my $installed_version = ZM_VERSION;
|
# This is basically here so that we don't need zm-update-blah.sql files for versions without db changes
|
||||||
my $sql = 'update Config set Value = ? where Name = ?';
|
my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = ?';
|
||||||
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( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() );
|
$sth->execute(ZM_VERSION, 'ZM_DYN_DB_VERSION') or die( "Can't execute: ".$sth->errstr() );
|
||||||
$res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() );
|
$sth->execute(ZM_VERSION, 'ZM_DYN_CURR_VERSION') or die( "Can't execute: ".$sth->errstr() );
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
} else {
|
} else {
|
||||||
zmDbDisconnect();
|
zmDbDisconnect();
|
||||||
|
@ -930,41 +933,42 @@ if ( $version ) {
|
||||||
#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() );
|
||||||
#$sth->finish();
|
#$sth->finish();
|
||||||
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
|
print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n");
|
||||||
}
|
} # end if version
|
||||||
|
|
||||||
zmDbDisconnect();
|
zmDbDisconnect();
|
||||||
exit( 0 );
|
exit(0);
|
||||||
|
|
||||||
sub patchDB_using_do {
|
sub patchDB_using_do {
|
||||||
my ( $dbh, $version, $file ) = @_;
|
my ( $dbh, $version, $file ) = @_;
|
||||||
|
|
||||||
open( my $fh, '<', $file ) or die "Unable to open $file $!";
|
open(my $fh, '<', $file) or die "Unable to open $file $!";
|
||||||
$/ = undef;
|
$/ = undef;
|
||||||
my $sql = <$fh>;
|
my $sql = <$fh>;
|
||||||
close $fh;
|
close $fh;
|
||||||
if ( $sql ) {
|
if ( $sql ) {
|
||||||
$dbh->{'AutoCommit'} = 0;
|
$dbh->{AutoCommit} = 0;
|
||||||
$dbh->do($sql);
|
$dbh->do($sql);
|
||||||
if ( $dbh->errstr() ) {
|
if ( $dbh->errstr() ) {
|
||||||
$dbh->rollback();
|
$dbh->rollback();
|
||||||
die "Error: " . $dbh->errstr(). ". Rolled back.\n";
|
die 'Error: '.$dbh->errstr().". Rolled back.\n";
|
||||||
} # end if error
|
} # end if error
|
||||||
|
|
||||||
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'";
|
my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = \'ZM_DYN_DB_VERSION\'';
|
||||||
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( $version ) or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute($version) or die 'Can\'t execute: '.$sth->errstr();
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
|
||||||
$dbh->{'AutoCommit'} = 1;
|
$dbh->{AutoCommit} = 1;
|
||||||
} else {
|
} else {
|
||||||
Warning("Empty db update file at $file");
|
Warning("Empty db update file at $file");
|
||||||
}
|
}
|
||||||
}
|
} # end sub patchDB_using_do
|
||||||
|
|
||||||
sub patchDB {
|
sub patchDB {
|
||||||
my $dbh = shift;
|
my $dbh = shift;
|
||||||
my $version = shift;
|
my $version = shift;
|
||||||
|
|
||||||
|
|
||||||
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
|
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
|
||||||
my $command = 'mysql';
|
my $command = 'mysql';
|
||||||
if ( defined($portOrSocket) ) {
|
if ( defined($portOrSocket) ) {
|
||||||
|
@ -988,39 +992,38 @@ sub patchDB {
|
||||||
}
|
}
|
||||||
$command .= '/zm_update-'.$version.'.sql';
|
$command .= '/zm_update-'.$version.'.sql';
|
||||||
|
|
||||||
print( "Executing '$command'\n" ) if ( logDebugging() );
|
print("Executing '$command'\n") if logDebugging();
|
||||||
my $output = qx($command);
|
my $output = qx($command);
|
||||||
my $status = $? >> 8;
|
my $status = $? >> 8;
|
||||||
if ( $status || logDebugging() ) {
|
if ( $status || logDebugging() ) {
|
||||||
chomp( $output );
|
chomp($output);
|
||||||
print( "Output: $output\n" );
|
print("Output: $output\n");
|
||||||
}
|
}
|
||||||
if ( $status ) {
|
if ( $status ) {
|
||||||
die( "Command '$command' exited with status: $status\n" );
|
die("Command '$command' exited with status: $status\n");
|
||||||
}
|
}
|
||||||
print( "\nDatabase successfully upgraded to version $version.\n" );
|
print("\nDatabase successfully upgraded to version $version.\n");
|
||||||
|
} # end sub patchDB
|
||||||
}
|
|
||||||
|
|
||||||
sub migratePasswords {
|
sub migratePasswords {
|
||||||
print ("Migratings passwords, if any...\n");
|
print ("Migratings passwords, if any...\n");
|
||||||
my $sql = "select * from Users";
|
my $sql = 'SELECT * FROM `Users`';
|
||||||
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());
|
||||||
while( my $user = $sth->fetchrow_hashref() ) {
|
while( my $user = $sth->fetchrow_hashref() ) {
|
||||||
my $scheme = substr($user->{Password}, 0, 1);
|
my $scheme = substr($user->{Password}, 0, 1);
|
||||||
if ($scheme eq "*") {
|
if ($scheme eq '*') {
|
||||||
print ("-->".$user->{Username}. " password will be migrated\n");
|
print ('-->'.$user->{Username}." password will be migrated\n");
|
||||||
my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8));
|
my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8));
|
||||||
my $settings = '$2a$10$'.$salt;
|
my $settings = '$2a$10$'.$salt;
|
||||||
my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings);
|
my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings);
|
||||||
my $new_pass_hash = "-ZM-".$pass_hash;
|
my $new_pass_hash = '-ZM-'.$pass_hash;
|
||||||
$sql = "UPDATE Users SET PASSWORD=? WHERE Username=?";
|
$sql = 'UPDATE Users SET `Password`=? WHERE `Username`=?';
|
||||||
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($new_pass_hash, $user->{Username}) or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute($new_pass_hash, $user->{Username}) or die("Can't execute: ".$sth->errstr());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} # end sub migratePasswords
|
||||||
|
|
||||||
sub migratePaths {
|
sub migratePaths {
|
||||||
|
|
||||||
|
|
1144
scripts/zmx10.pl.in
1144
scripts/zmx10.pl.in
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,81 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_crypt.h"
|
#include "zm_crypt.h"
|
||||||
#include "BCrypt.hpp"
|
#include "BCrypt.hpp"
|
||||||
#include "jwt.h"
|
#if HAVE_LIBJWT
|
||||||
|
#include <jwt.h>
|
||||||
|
#else
|
||||||
|
#include "jwt_cpp.h"
|
||||||
|
#endif
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#if HAVE_LIBCRYPTO
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
#elif HAVE_GNUTLS_GNUTLS_H
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/crypto.h>
|
||||||
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// returns username if valid, "" if not
|
// returns username if valid, "" if not
|
||||||
|
#if HAVE_LIBJWT
|
||||||
|
std::pair <std::string, unsigned int> verifyToken(std::string jwt_token_str, std::string key) {
|
||||||
|
std::string username = "";
|
||||||
|
unsigned int token_issued_at = 0;
|
||||||
|
int err = 0;
|
||||||
|
jwt_t *jwt = nullptr;
|
||||||
|
|
||||||
|
err = jwt_new(&jwt);
|
||||||
|
if( err ) {
|
||||||
|
Error("Unable to Allocate JWT object");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jwt_set_alg(jwt, JWT_ALG_HS256, (const unsigned char*)key.c_str(), key.length());
|
||||||
|
if( err ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("Error setting Algorithm for JWT decode");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jwt_decode(&jwt, jwt_token_str.c_str(), nullptr, 0);
|
||||||
|
if( err ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("Could not decode JWT");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *c_type = jwt_get_grant(jwt, (const char*)"type");
|
||||||
|
if ( !c_type ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("Missing token type. This should not happen");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
} else if ( std::string(c_type) != "access" ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("Only access tokens are allowed. Please do not use refresh tokens");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *c_username = jwt_get_grant(jwt, (const char*)"user");
|
||||||
|
if( !c_username ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("User not found in claim");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
username = std::string(c_username);
|
||||||
|
Debug(1, "Got %s as user claim from token", username.c_str());
|
||||||
|
|
||||||
|
token_issued_at = (unsigned int)jwt_get_grant_int(jwt, "iat");
|
||||||
|
if ( errno == ENOENT ) {
|
||||||
|
jwt_free(jwt);
|
||||||
|
Error("IAT not found in claim. This should not happen");
|
||||||
|
return std::make_pair("", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(1, "Got IAT token=%u", token_issued_at);
|
||||||
|
jwt_free(jwt);
|
||||||
|
return std::make_pair(username, token_issued_at);
|
||||||
|
}
|
||||||
|
#else // HAVE_LIBJWT
|
||||||
std::pair <std::string, unsigned int> verifyToken(std::string jwt_token_str, std::string key) {
|
std::pair <std::string, unsigned int> verifyToken(std::string jwt_token_str, std::string key) {
|
||||||
std::string username = "";
|
std::string username = "";
|
||||||
unsigned int token_issued_at = 0;
|
unsigned int token_issued_at = 0;
|
||||||
|
@ -58,6 +127,7 @@ std::pair <std::string, unsigned int> verifyToken(std::string jwt_token_str, std
|
||||||
}
|
}
|
||||||
return std::make_pair(username, token_issued_at);
|
return std::make_pair(username, token_issued_at);
|
||||||
}
|
}
|
||||||
|
#endif // HAVE_LIBJWT
|
||||||
|
|
||||||
bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash) {
|
bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash) {
|
||||||
bool password_correct = false;
|
bool password_correct = false;
|
||||||
|
@ -70,10 +140,16 @@ bool verifyPassword(const char *username, const char *input_password, const char
|
||||||
// MYSQL PASSWORD
|
// MYSQL PASSWORD
|
||||||
Debug(1, "%s is using an MD5 encoded password", username);
|
Debug(1, "%s is using an MD5 encoded password", username);
|
||||||
|
|
||||||
SHA_CTX ctx1, ctx2;
|
#ifndef SHA_DIGEST_LENGTH
|
||||||
|
#define SHA_DIGEST_LENGTH 20
|
||||||
|
#endif
|
||||||
|
|
||||||
unsigned char digest_interim[SHA_DIGEST_LENGTH];
|
unsigned char digest_interim[SHA_DIGEST_LENGTH];
|
||||||
unsigned char digest_final[SHA_DIGEST_LENGTH];
|
unsigned char digest_final[SHA_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
#if HAVE_LIBCRYPTO
|
||||||
|
SHA_CTX ctx1, ctx2;
|
||||||
|
|
||||||
//get first iteration
|
//get first iteration
|
||||||
SHA1_Init(&ctx1);
|
SHA1_Init(&ctx1);
|
||||||
SHA1_Update(&ctx1, input_password, strlen(input_password));
|
SHA1_Update(&ctx1, input_password, strlen(input_password));
|
||||||
|
@ -83,6 +159,15 @@ bool verifyPassword(const char *username, const char *input_password, const char
|
||||||
SHA1_Init(&ctx2);
|
SHA1_Init(&ctx2);
|
||||||
SHA1_Update(&ctx2, digest_interim,SHA_DIGEST_LENGTH);
|
SHA1_Update(&ctx2, digest_interim,SHA_DIGEST_LENGTH);
|
||||||
SHA1_Final (digest_final, &ctx2);
|
SHA1_Final (digest_final, &ctx2);
|
||||||
|
#elif HAVE_GNUTLS_GNUTLS_H
|
||||||
|
//get first iteration
|
||||||
|
gnutls_hash_fast(GNUTLS_DIG_SHA1, input_password, strlen(input_password), digest_interim);
|
||||||
|
//2nd iteration
|
||||||
|
gnutls_hash_fast(GNUTLS_DIG_SHA1, digest_interim, SHA_DIGEST_LENGTH, digest_final);
|
||||||
|
#else
|
||||||
|
Error("Authentication Error. ZoneMinder not built with GnuTLS or Openssl");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
char final_hash[SHA_DIGEST_LENGTH * 2 +2];
|
char final_hash[SHA_DIGEST_LENGTH * 2 +2];
|
||||||
final_hash[0] = '*';
|
final_hash[0] = '*';
|
||||||
|
|
|
@ -40,7 +40,7 @@ bool zmDbConnect() {
|
||||||
Error("Can't initialise database connection: %s", mysql_error(&dbconn));
|
Error("Can't initialise database connection: %s", mysql_error(&dbconn));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
my_bool reconnect = 1;
|
bool reconnect = 1;
|
||||||
if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) )
|
if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) )
|
||||||
Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn));
|
Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn));
|
||||||
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||||
|
|
|
@ -76,7 +76,7 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) {
|
||||||
curr_frame_id = 1; // curr_frame_id is 1-based
|
curr_frame_id = 1; // curr_frame_id is 1-based
|
||||||
if ( event_time >= event_data->start_time ) {
|
if ( event_time >= event_data->start_time ) {
|
||||||
Debug(2, "event time is after event start");
|
Debug(2, "event time is after event start");
|
||||||
for (unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
for ( unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
||||||
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
||||||
if ( event_data->frames[i].timestamp >= event_time ) {
|
if ( event_data->frames[i].timestamp >= event_time ) {
|
||||||
curr_frame_id = i+1;
|
curr_frame_id = i+1;
|
||||||
|
@ -100,6 +100,7 @@ bool EventStream::loadInitialEventData(uint64_t init_event_id, unsigned int init
|
||||||
if ( init_frame_id >= event_data->frame_count ) {
|
if ( init_frame_id >= event_data->frame_count ) {
|
||||||
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count);
|
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count);
|
||||||
curr_stream_time = event_data->start_time;
|
curr_stream_time = event_data->start_time;
|
||||||
|
curr_frame_id = 1;
|
||||||
} else {
|
} else {
|
||||||
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
||||||
curr_frame_id = init_frame_id;
|
curr_frame_id = init_frame_id;
|
||||||
|
@ -117,7 +118,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
||||||
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
|
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
|
||||||
"`DefaultVideo`, `Scheme`, `SaveJPEGs` FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
||||||
|
|
||||||
if ( mysql_query(&dbconn, sql) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
|
@ -160,9 +161,25 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
event_data->scheme = Storage::SHALLOW;
|
event_data->scheme = Storage::SHALLOW;
|
||||||
}
|
}
|
||||||
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
||||||
|
event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8]));
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
|
||||||
Storage * storage = new Storage(event_data->storage_id);
|
if ( !monitor ) {
|
||||||
|
monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY);
|
||||||
|
} else if ( monitor->Id() != event_data->monitor_id ) {
|
||||||
|
delete monitor;
|
||||||
|
monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY);
|
||||||
|
}
|
||||||
|
if ( !monitor ) {
|
||||||
|
Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !storage ) {
|
||||||
|
storage = new Storage(event_data->storage_id);
|
||||||
|
} else if ( storage->Id() != event_data->storage_id ) {
|
||||||
|
delete storage;
|
||||||
|
storage = new Storage(event_data->storage_id);
|
||||||
|
}
|
||||||
const char *storage_path = storage->Path();
|
const char *storage_path = storage->Path();
|
||||||
|
|
||||||
if ( event_data->scheme == Storage::DEEP ) {
|
if ( event_data->scheme == Storage::DEEP ) {
|
||||||
|
@ -204,7 +221,6 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||||
event_data->event_id);
|
event_data->event_id);
|
||||||
}
|
}
|
||||||
delete storage; storage = NULL;
|
|
||||||
|
|
||||||
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
||||||
Debug(3, "fps set by frame_count(%d)/duration(%f)",
|
Debug(3, "fps set by frame_count(%d)/duration(%f)",
|
||||||
|
@ -275,7 +291,10 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
|
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
|
||||||
if ( event_data->video_file[0] ) {
|
if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) {
|
||||||
|
if ( !event_data->video_file[0] ) {
|
||||||
|
snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4");
|
||||||
|
}
|
||||||
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
|
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
|
||||||
//char filepath[PATH_MAX];
|
//char filepath[PATH_MAX];
|
||||||
//snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
|
//snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
|
||||||
|
@ -376,12 +395,16 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
paused = true;
|
paused = true;
|
||||||
replay_rate = ZM_RATE_BASE;
|
replay_rate = ZM_RATE_BASE;
|
||||||
step = 1;
|
step = 1;
|
||||||
|
if ( (unsigned int)curr_frame_id < event_data->frame_count )
|
||||||
|
curr_frame_id += 1;
|
||||||
break;
|
break;
|
||||||
case CMD_SLOWREV :
|
case CMD_SLOWREV :
|
||||||
Debug(1, "Got SLOW REV command");
|
Debug(1, "Got SLOW REV command");
|
||||||
paused = true;
|
paused = true;
|
||||||
replay_rate = ZM_RATE_BASE;
|
replay_rate = ZM_RATE_BASE;
|
||||||
step = -1;
|
step = -1;
|
||||||
|
curr_frame_id -= 1;
|
||||||
|
if ( curr_frame_id < 1 ) curr_frame_id = 1;
|
||||||
break;
|
break;
|
||||||
case CMD_FASTREV :
|
case CMD_FASTREV :
|
||||||
Debug(1, "Got FAST REV command");
|
Debug(1, "Got FAST REV command");
|
||||||
|
@ -697,9 +720,10 @@ Debug(1, "Loading image");
|
||||||
} else if ( ffmpeg_input ) {
|
} else if ( ffmpeg_input ) {
|
||||||
// Get the frame from the mp4 input
|
// Get the frame from the mp4 input
|
||||||
Debug(1,"Getting frame from ffmpeg");
|
Debug(1,"Getting frame from ffmpeg");
|
||||||
AVFrame *frame;
|
|
||||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset );
|
AVFrame *frame = ffmpeg_input->get_frame(
|
||||||
|
ffmpeg_input->get_video_stream_id(),
|
||||||
|
frame_data->offset);
|
||||||
if ( frame ) {
|
if ( frame ) {
|
||||||
image = new Image(frame);
|
image = new Image(frame);
|
||||||
//av_frame_free(&frame);
|
//av_frame_free(&frame);
|
||||||
|
@ -707,6 +731,34 @@ Debug(1, "Loading image");
|
||||||
Error("Failed getting a frame.");
|
Error("Failed getting a frame.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when stored as an mp4, we just have the rotation as a flag in the headers
|
||||||
|
// so we need to rotate it before outputting
|
||||||
|
if (
|
||||||
|
(monitor->GetOptVideoWriter() == Monitor::H264PASSTHROUGH)
|
||||||
|
and
|
||||||
|
(event_data->Orientation != Monitor::ROTATE_0)
|
||||||
|
) {
|
||||||
|
Debug(2, "Rotating image %d", event_data->Orientation);
|
||||||
|
switch ( event_data->Orientation ) {
|
||||||
|
case Monitor::ROTATE_0 :
|
||||||
|
// No action required
|
||||||
|
break;
|
||||||
|
case Monitor::ROTATE_90 :
|
||||||
|
case Monitor::ROTATE_180 :
|
||||||
|
case Monitor::ROTATE_270 :
|
||||||
|
image->Rotate((event_data->Orientation-1)*90);
|
||||||
|
break;
|
||||||
|
case Monitor::FLIP_HORI :
|
||||||
|
case Monitor::FLIP_VERT :
|
||||||
|
image->Flip(event_data->Orientation==Monitor::FLIP_HORI);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Error("Invalid Orientation: %d", event_data->Orientation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(2, "Not Rotating image %d", event_data->Orientation);
|
||||||
|
} // end if have rotation
|
||||||
} else {
|
} else {
|
||||||
Error("Unable to get a frame");
|
Error("Unable to get a frame");
|
||||||
return false;
|
return false;
|
||||||
|
@ -736,6 +788,10 @@ Debug(1, "Loading image");
|
||||||
Fatal("Unexpected frame type %d", type);
|
Fatal("Unexpected frame type %d", type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ( send_image != image ) {
|
||||||
|
delete send_image;
|
||||||
|
send_image = NULL;
|
||||||
|
}
|
||||||
delete image;
|
delete image;
|
||||||
image = NULL;
|
image = NULL;
|
||||||
} // end if send_raw or not
|
} // end if send_raw or not
|
||||||
|
@ -824,17 +880,13 @@ void EventStream::runStream() {
|
||||||
// commands may set send_frame to true
|
// commands may set send_frame to true
|
||||||
while ( checkCommandQueue() && !zm_terminate ) {
|
while ( checkCommandQueue() && !zm_terminate ) {
|
||||||
// The idea is to loop here processing all commands before proceeding.
|
// The idea is to loop here processing all commands before proceeding.
|
||||||
Debug(1, "Have command queue");
|
|
||||||
}
|
}
|
||||||
Debug(2, "Done command queue");
|
|
||||||
|
|
||||||
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
||||||
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
||||||
touch(sock_path_lock);
|
touch(sock_path_lock);
|
||||||
last_comm_update = now;
|
last_comm_update = now;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Debug(2, "Not checking command queue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current frame data
|
// Get current frame data
|
||||||
|
@ -851,7 +903,7 @@ void EventStream::runStream() {
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
}
|
}
|
||||||
} else if ( step != 0 ) {
|
} else if ( step != 0 ) {
|
||||||
Debug(2, "Paused with step");
|
Debug(2, "Paused with step %d", step);
|
||||||
// We are paused and are just stepping forward or backward one frame
|
// We are paused and are just stepping forward or backward one frame
|
||||||
step = 0;
|
step = 0;
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
|
@ -990,30 +1042,30 @@ void EventStream::runStream() {
|
||||||
//if ( step != 0 )// Adding 0 is cheaper than an if 0
|
//if ( step != 0 )// Adding 0 is cheaper than an if 0
|
||||||
// curr_frame_id starts at 1 though, so we might skip the first frame?
|
// curr_frame_id starts at 1 though, so we might skip the first frame?
|
||||||
curr_frame_id += step;
|
curr_frame_id += step;
|
||||||
} // end if !paused
|
|
||||||
|
|
||||||
// Detects when we hit end of event and will load the next event or previous event
|
// Detects when we hit end of event and will load the next event or previous event
|
||||||
if ( checkEventLoaded() ) {
|
if ( checkEventLoaded() ) {
|
||||||
// Have change of event
|
// Have change of event
|
||||||
|
|
||||||
// This next bit is to determine if we are in the current event time wise
|
// This next bit is to determine if we are in the current event time wise
|
||||||
// and whether to show an image saying how long until the next event.
|
// and whether to show an image saying how long until the next event.
|
||||||
if ( replay_rate > 0 ) {
|
if ( replay_rate > 0 ) {
|
||||||
// This doesn't make sense unless we have hit the end of the event.
|
// This doesn't make sense unless we have hit the end of the event.
|
||||||
time_to_event = event_data->frames[0].timestamp - curr_stream_time;
|
time_to_event = event_data->frames[0].timestamp - curr_stream_time;
|
||||||
Debug(1, "replay rate(%d) time_to_event(%f)=frame timestamp:%f - curr_stream_time(%f)",
|
Debug(1, "replay rate(%d) time_to_event(%f)=frame timestamp:%f - curr_stream_time(%f)",
|
||||||
replay_rate, time_to_event,
|
replay_rate, time_to_event,
|
||||||
event_data->frames[0].timestamp,
|
event_data->frames[0].timestamp,
|
||||||
curr_stream_time);
|
curr_stream_time);
|
||||||
|
|
||||||
} else if ( replay_rate < 0 ) {
|
} else if ( replay_rate < 0 ) {
|
||||||
time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp;
|
time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp;
|
||||||
Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f",
|
Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f",
|
||||||
replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp);
|
replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp);
|
||||||
}
|
} // end if forward or reverse
|
||||||
|
|
||||||
}
|
} // end if checkEventLoaded
|
||||||
} // end while ! zm_terminate
|
} // end if !paused
|
||||||
|
} // end while ! zm_terminate
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
if ( type == STREAM_MPEG )
|
if ( type == STREAM_MPEG )
|
||||||
delete vid_stream;
|
delete vid_stream;
|
||||||
|
@ -1022,18 +1074,12 @@ void EventStream::runStream() {
|
||||||
closeComms();
|
closeComms();
|
||||||
} // void EventStream::runStream()
|
} // void EventStream::runStream()
|
||||||
|
|
||||||
void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) {
|
void EventStream::setStreamStart(
|
||||||
|
uint64_t init_event_id,
|
||||||
|
unsigned int init_frame_id=0) {
|
||||||
loadInitialEventData(init_event_id, init_frame_id);
|
loadInitialEventData(init_event_id, init_frame_id);
|
||||||
if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) {
|
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
|
||||||
Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventStream::setStreamStart(int monitor_id, time_t event_time) {
|
void EventStream::setStreamStart(int monitor_id, time_t event_time) {
|
||||||
loadInitialEventData(monitor_id, event_time);
|
loadInitialEventData(monitor_id, event_time);
|
||||||
if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) {
|
|
||||||
Fatal("Unable to load monitor id %d for streaming", monitor_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ class EventStream : public StreamBase {
|
||||||
char video_file[PATH_MAX];
|
char video_file[PATH_MAX];
|
||||||
Storage::Schemes scheme;
|
Storage::Schemes scheme;
|
||||||
int SaveJPEGs;
|
int SaveJPEGs;
|
||||||
|
Monitor::Orientation Orientation;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -82,6 +83,7 @@ class EventStream : public StreamBase {
|
||||||
struct timeval start; // clock time when started the event
|
struct timeval start; // clock time when started the event
|
||||||
|
|
||||||
EventData *event_data;
|
EventData *event_data;
|
||||||
|
Storage *storage;
|
||||||
FFmpeg_Input *ffmpeg_input;
|
FFmpeg_Input *ffmpeg_input;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -96,7 +98,7 @@ class EventStream : public StreamBase {
|
||||||
public:
|
public:
|
||||||
EventStream() {
|
EventStream() {
|
||||||
mode = DEFAULT_MODE;
|
mode = DEFAULT_MODE;
|
||||||
replay_rate = DEFAULT_RATE;
|
replay_rate = DEFAULT_RATE;
|
||||||
|
|
||||||
forceEventChange = false;
|
forceEventChange = false;
|
||||||
|
|
||||||
|
@ -104,13 +106,32 @@ class EventStream : public StreamBase {
|
||||||
curr_stream_time = 0.0;
|
curr_stream_time = 0.0;
|
||||||
send_frame = false;
|
send_frame = false;
|
||||||
|
|
||||||
event_data = 0;
|
event_data = NULL;
|
||||||
|
|
||||||
// Used when loading frames from an mp4
|
// Used when loading frames from an mp4
|
||||||
input_codec_context = 0;
|
input_codec_context = 0;
|
||||||
input_codec = 0;
|
input_codec = 0;
|
||||||
|
|
||||||
ffmpeg_input = NULL;
|
ffmpeg_input = NULL;
|
||||||
|
storage = NULL;
|
||||||
|
}
|
||||||
|
~EventStream() {
|
||||||
|
if ( event_data ) {
|
||||||
|
delete event_data;
|
||||||
|
event_data = NULL;
|
||||||
|
}
|
||||||
|
if ( monitor ) {
|
||||||
|
delete monitor;
|
||||||
|
monitor = NULL;
|
||||||
|
}
|
||||||
|
if ( storage ) {
|
||||||
|
delete storage;
|
||||||
|
storage = NULL;
|
||||||
|
}
|
||||||
|
if ( ffmpeg_input ) {
|
||||||
|
delete ffmpeg_input;
|
||||||
|
ffmpeg_input = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
|
void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
|
||||||
void setStreamStart( int monitor_id, time_t event_time );
|
void setStreamStart( int monitor_id, time_t event_time );
|
||||||
|
|
|
@ -81,7 +81,7 @@ void FFMPEGInit() {
|
||||||
av_log_set_callback(log_libav_callback);
|
av_log_set_callback(log_libav_callback);
|
||||||
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
|
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
|
||||||
} else {
|
} else {
|
||||||
Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor not part of your debug targets");
|
Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets");
|
||||||
av_log_set_level(AV_LOG_QUIET);
|
av_log_set_level(AV_LOG_QUIET);
|
||||||
}
|
}
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
||||||
|
@ -291,17 +291,18 @@ static void zm_log_fps(double d, const char *postfix) {
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
||||||
Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d %s) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)",
|
Debug(1, "Dumping codecpar codec_type(%d %s) codec_id(%d %s) codec_tag(%" PRIu32 ") width(%d) height(%d) bit_rate(%" PRIu64 ") format(%d %s)",
|
||||||
par->codec_type,
|
par->codec_type,
|
||||||
par->codec_id,
|
av_get_media_type_string(par->codec_type),
|
||||||
avcodec_get_name(par->codec_id),
|
par->codec_id,
|
||||||
par->codec_tag,
|
avcodec_get_name(par->codec_id),
|
||||||
par->width,
|
par->codec_tag,
|
||||||
par->height,
|
par->width,
|
||||||
par->bit_rate,
|
par->height,
|
||||||
par->format,
|
par->bit_rate,
|
||||||
((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format))
|
par->format,
|
||||||
);
|
(((AVPixelFormat)par->format == AV_PIX_FMT_NONE) ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -517,19 +517,19 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
Debug(1, "Selected hw_pix_fmt %d %s",
|
Debug(1, "Selected hw_pix_fmt %d %s",
|
||||||
hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt));
|
hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt));
|
||||||
|
|
||||||
mVideoCodecContext->get_format = get_hw_format;
|
|
||||||
|
|
||||||
ret = av_hwdevice_ctx_create(&hw_device_ctx, type,
|
ret = av_hwdevice_ctx_create(&hw_device_ctx, type,
|
||||||
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0);
|
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0);
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
Error("Failed to create hwaccel device.");
|
Error("Failed to create hwaccel device. %s",av_make_error_string(ret).c_str());
|
||||||
return -1;
|
hw_pix_fmt = AV_PIX_FMT_NONE;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Created hwdevice for %s", hwaccel_device.c_str());
|
||||||
|
mVideoCodecContext->get_format = get_hw_format;
|
||||||
|
mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx);
|
||||||
|
hwFrame = zm_av_frame_alloc();
|
||||||
}
|
}
|
||||||
Debug(1, "Created hwdevice for %s", hwaccel_device.c_str());
|
|
||||||
mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx);
|
|
||||||
hwFrame = zm_av_frame_alloc();
|
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Failed to setup hwaccel.");
|
Debug(1, "Failed to find suitable hw_pix_fmt.");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
Debug(1, "AVCodec not new enough for hwaccel");
|
Debug(1, "AVCodec not new enough for hwaccel");
|
||||||
|
@ -537,7 +537,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
#else
|
#else
|
||||||
Warning("HWAccel support not compiled in.");
|
Warning("HWAccel support not compiled in.");
|
||||||
#endif
|
#endif
|
||||||
} // end if hwacel_name
|
} // end if hwaccel_name
|
||||||
|
|
||||||
// Open the codec
|
// Open the codec
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
|
@ -603,6 +603,8 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
Error("Unable to allocate frame for %s", mPath.c_str());
|
Error("Unable to allocate frame for %s", mPath.c_str());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
mFrame->width = width;
|
||||||
|
mFrame->height = height;
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
||||||
|
@ -630,20 +632,6 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if 0
|
|
||||||
// Must get a frame first to find out the actual format returned by decoding
|
|
||||||
mConvertContext = sws_getContext(
|
|
||||||
mVideoCodecContext->width,
|
|
||||||
mVideoCodecContext->height,
|
|
||||||
mVideoCodecContext->pix_fmt,
|
|
||||||
width, height,
|
|
||||||
imagePixFormat, SWS_BICUBIC, NULL,
|
|
||||||
NULL, NULL);
|
|
||||||
if ( mConvertContext == NULL ) {
|
|
||||||
Error("Unable to create conversion context for %s", mPath.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal("You must compile ffmpeg with the --enable-swscale "
|
Fatal("You must compile ffmpeg with the --enable-swscale "
|
||||||
"option to use ffmpeg cameras");
|
"option to use ffmpeg cameras");
|
||||||
|
@ -1087,9 +1075,12 @@ int FfmpegCamera::transfer_to_image(
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
// From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too.
|
||||||
int size = av_image_fill_arrays(
|
int size = av_image_fill_arrays(
|
||||||
output_frame->data, output_frame->linesize,
|
output_frame->data, output_frame->linesize,
|
||||||
directbuffer, imagePixFormat, width, height, 32);
|
directbuffer, imagePixFormat, width, height,
|
||||||
|
(AV_PIX_FMT_RGBA == imagePixFormat ? 32 : 1)
|
||||||
|
);
|
||||||
if ( size < 0 ) {
|
if ( size < 0 ) {
|
||||||
Error("Problem setting up data pointers into image %s",
|
Error("Problem setting up data pointers into image %s",
|
||||||
av_make_error_string(size).c_str());
|
av_make_error_string(size).c_str());
|
||||||
|
@ -1128,19 +1119,31 @@ int FfmpegCamera::transfer_to_image(
|
||||||
mConvertContext, input_frame->data, input_frame->linesize,
|
mConvertContext, input_frame->data, input_frame->linesize,
|
||||||
0, mVideoCodecContext->height,
|
0, mVideoCodecContext->height,
|
||||||
output_frame->data, output_frame->linesize);
|
output_frame->data, output_frame->linesize);
|
||||||
if ( ret <= 0 ) {
|
if ( ret < 0 ) {
|
||||||
Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s : code: %d",
|
Error("Unable to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s lines %d: code: %d",
|
||||||
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
|
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
|
||||||
input_frame->linesize, mVideoCodecContext->height,
|
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
|
||||||
imagePixFormat,
|
imagePixFormat,
|
||||||
av_get_pix_fmt_name(imagePixFormat),
|
av_get_pix_fmt_name(imagePixFormat),
|
||||||
output_frame->linesize,
|
output_frame->linesize[0], output_frame->linesize[1],
|
||||||
frameCount,
|
frameCount,
|
||||||
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
||||||
|
mVideoCodecContext->height,
|
||||||
ret
|
ret
|
||||||
);
|
);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Debug(4, "Able to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s %dx%d ",
|
||||||
|
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
|
||||||
|
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
|
||||||
|
imagePixFormat,
|
||||||
|
av_get_pix_fmt_name(imagePixFormat),
|
||||||
|
output_frame->linesize[0], output_frame->linesize[1],
|
||||||
|
frameCount,
|
||||||
|
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
||||||
|
output_frame->width,
|
||||||
|
output_frame->height
|
||||||
|
);
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal("You must compile ffmpeg with the --enable-swscale "
|
Fatal("You must compile ffmpeg with the --enable-swscale "
|
||||||
"option to use ffmpeg cameras");
|
"option to use ffmpeg cameras");
|
||||||
|
|
|
@ -10,14 +10,31 @@ FFmpeg_Input::FFmpeg_Input() {
|
||||||
FFMPEGInit();
|
FFMPEGInit();
|
||||||
streams = NULL;
|
streams = NULL;
|
||||||
frame = NULL;
|
frame = NULL;
|
||||||
|
last_seek_request = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpeg_Input::~FFmpeg_Input() {
|
FFmpeg_Input::~FFmpeg_Input() {
|
||||||
if ( streams ) {
|
if ( streams ) {
|
||||||
|
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
||||||
|
avcodec_close(streams[i].context);
|
||||||
|
streams[i].context = NULL;
|
||||||
|
}
|
||||||
delete streams;
|
delete streams;
|
||||||
streams = NULL;
|
streams = NULL;
|
||||||
}
|
}
|
||||||
}
|
if ( frame ) {
|
||||||
|
av_frame_free(&frame);
|
||||||
|
frame = NULL;
|
||||||
|
}
|
||||||
|
if ( input_format_context ) {
|
||||||
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
|
||||||
|
av_close_input_file(input_format_context);
|
||||||
|
#else
|
||||||
|
avformat_close_input(&input_format_context);
|
||||||
|
#endif
|
||||||
|
input_format_context = NULL;
|
||||||
|
}
|
||||||
|
} // end ~FFmpeg_Input()
|
||||||
|
|
||||||
int FFmpeg_Input::Open(const char *filepath) {
|
int FFmpeg_Input::Open(const char *filepath) {
|
||||||
|
|
||||||
|
@ -89,6 +106,7 @@ int FFmpeg_Input::Open(const char *filepath) {
|
||||||
avcodec_free_context(&streams[i].context);
|
avcodec_free_context(&streams[i].context);
|
||||||
#endif
|
#endif
|
||||||
avformat_close_input(&input_format_context);
|
avformat_close_input(&input_format_context);
|
||||||
|
input_format_context = NULL;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
} // end foreach stream
|
} // end foreach stream
|
||||||
|
@ -102,8 +120,6 @@ int FFmpeg_Input::Open(const char *filepath) {
|
||||||
} // end int FFmpeg_Input::Open( const char * filepath )
|
} // end int FFmpeg_Input::Open( const char * filepath )
|
||||||
|
|
||||||
AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
||||||
Debug(4, "Getting frame from stream %d", stream_id);
|
|
||||||
|
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
av_init_packet(&packet);
|
av_init_packet(&packet);
|
||||||
|
@ -144,6 +160,12 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
||||||
zm_av_packet_unref(&packet);
|
zm_av_packet_unref(&packet);
|
||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
if ( is_video_stream(input_format_context->streams[packet.stream_index]) ) {
|
||||||
|
zm_dump_video_frame(frame, "resulting video frame");
|
||||||
|
} else {
|
||||||
|
zm_dump_frame(frame, "resulting frame");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frameComplete = 1;
|
frameComplete = 1;
|
||||||
|
@ -175,11 +197,20 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
||||||
}
|
}
|
||||||
// Have to grab a frame to update our current frame to know where we are
|
// Have to grab a frame to update our current frame to know where we are
|
||||||
get_frame(stream_id);
|
get_frame(stream_id);
|
||||||
zm_dump_frame(frame, "Got first frame, returning it");
|
} // end if ! frame
|
||||||
return frame;
|
|
||||||
} // end if !frame
|
|
||||||
|
|
||||||
if ( frame->pts > seek_target ) {
|
if ( !frame ) {
|
||||||
|
Warning("Unable to get frame.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(last_seek_request >= 0)
|
||||||
|
&&
|
||||||
|
(last_seek_request > seek_target )
|
||||||
|
&&
|
||||||
|
(frame->pts > seek_target)
|
||||||
|
) {
|
||||||
zm_dump_frame(frame, "frame->pts > seek_target, seek backwards");
|
zm_dump_frame(frame, "frame->pts > seek_target, seek backwards");
|
||||||
// our frame must be beyond our seek target. so go backwards to before it
|
// our frame must be beyond our seek target. so go backwards to before it
|
||||||
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target,
|
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target,
|
||||||
|
@ -193,6 +224,8 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
||||||
zm_dump_frame(frame, "frame->pts > seek_target, got");
|
zm_dump_frame(frame, "frame->pts > seek_target, got");
|
||||||
} // end if frame->pts > seek_target
|
} // end if frame->pts > seek_target
|
||||||
|
|
||||||
|
last_seek_request = seek_target;
|
||||||
|
|
||||||
// Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want.
|
// Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want.
|
||||||
if ( frame->pts <= seek_target ) {
|
if ( frame->pts <= seek_target ) {
|
||||||
zm_dump_frame(frame, "pts <= seek_target");
|
zm_dump_frame(frame, "pts <= seek_target");
|
||||||
|
@ -207,5 +240,4 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_frame(stream_id);
|
return get_frame(stream_id);
|
||||||
|
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
|
||||||
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ class FFmpeg_Input {
|
||||||
int audio_stream_id;
|
int audio_stream_id;
|
||||||
AVFormatContext *input_format_context;
|
AVFormatContext *input_format_context;
|
||||||
AVFrame *frame;
|
AVFrame *frame;
|
||||||
|
int64_t last_seek_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -48,6 +48,8 @@ static short *g_v_table;
|
||||||
static short *g_u_table;
|
static short *g_u_table;
|
||||||
static short *b_u_table;
|
static short *b_u_table;
|
||||||
|
|
||||||
|
struct SwsContext *sws_convert_context = NULL;
|
||||||
|
|
||||||
jpeg_compress_struct *Image::writejpg_ccinfo[101] = { 0 };
|
jpeg_compress_struct *Image::writejpg_ccinfo[101] = { 0 };
|
||||||
jpeg_compress_struct *Image::encodejpg_ccinfo[101] = { 0 };
|
jpeg_compress_struct *Image::encodejpg_ccinfo[101] = { 0 };
|
||||||
jpeg_decompress_struct *Image::readjpg_dcinfo = 0;
|
jpeg_decompress_struct *Image::readjpg_dcinfo = 0;
|
||||||
|
@ -160,7 +162,7 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin
|
||||||
update_function_pointers();
|
update_function_pointers();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image( const AVFrame *frame ) {
|
Image::Image(const AVFrame *frame) {
|
||||||
AVFrame *dest_frame = zm_av_frame_alloc();
|
AVFrame *dest_frame = zm_av_frame_alloc();
|
||||||
text[0] = '\0';
|
text[0] = '\0';
|
||||||
|
|
||||||
|
@ -183,26 +185,28 @@ Image::Image( const AVFrame *frame ) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext = sws_getContext(
|
sws_convert_context = sws_getCachedContext(
|
||||||
|
sws_convert_context,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
(AVPixelFormat)frame->format,
|
(AVPixelFormat)frame->format,
|
||||||
width, height,
|
width, height,
|
||||||
AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL,
|
AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
if ( mConvertContext == NULL )
|
if ( sws_convert_context == NULL )
|
||||||
Fatal( "Unable to create conversion context" );
|
Fatal("Unable to create conversion context");
|
||||||
|
|
||||||
if ( sws_scale(mConvertContext, frame->data, frame->linesize, 0, frame->height, dest_frame->data, dest_frame->linesize) < 0 )
|
if ( sws_scale(sws_convert_context, frame->data, frame->linesize, 0, frame->height,
|
||||||
|
dest_frame->data, dest_frame->linesize) < 0 )
|
||||||
Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA);
|
Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA);
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
av_frame_free( &dest_frame );
|
av_frame_free(&dest_frame);
|
||||||
update_function_pointers();
|
update_function_pointers();
|
||||||
}
|
} // end Image::Image(const AVFrame *frame)
|
||||||
|
|
||||||
Image::Image( const Image &p_image ) {
|
Image::Image(const Image &p_image) {
|
||||||
if ( !initialised )
|
if ( !initialised )
|
||||||
Initialise();
|
Initialise();
|
||||||
width = p_image.width;
|
width = p_image.width;
|
||||||
|
@ -215,7 +219,7 @@ Image::Image( const Image &p_image ) {
|
||||||
holdbuffer = 0;
|
holdbuffer = 0;
|
||||||
AllocImgBuffer(size);
|
AllocImgBuffer(size);
|
||||||
(*fptr_imgbufcpy)(buffer, p_image.buffer, size);
|
(*fptr_imgbufcpy)(buffer, p_image.buffer, size);
|
||||||
strncpy( text, p_image.text, sizeof(text) );
|
strncpy(text, p_image.text, sizeof(text));
|
||||||
update_function_pointers();
|
update_function_pointers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,35 +229,39 @@ Image::~Image() {
|
||||||
|
|
||||||
/* Should be called as part of program shutdown to free everything */
|
/* Should be called as part of program shutdown to free everything */
|
||||||
void Image::Deinitialise() {
|
void Image::Deinitialise() {
|
||||||
if ( initialised ) {
|
if ( !initialised ) return;
|
||||||
/*
|
/*
|
||||||
delete[] y_table;
|
delete[] y_table;
|
||||||
delete[] uv_table;
|
delete[] uv_table;
|
||||||
delete[] r_v_table;
|
delete[] r_v_table;
|
||||||
delete[] g_v_table;
|
delete[] g_v_table;
|
||||||
delete[] g_u_table;
|
delete[] g_u_table;
|
||||||
delete[] b_u_table;
|
delete[] b_u_table;
|
||||||
*/
|
*/
|
||||||
initialised = false;
|
initialised = false;
|
||||||
if ( readjpg_dcinfo ) {
|
if ( readjpg_dcinfo ) {
|
||||||
jpeg_destroy_decompress( readjpg_dcinfo );
|
jpeg_destroy_decompress( readjpg_dcinfo );
|
||||||
delete readjpg_dcinfo;
|
delete readjpg_dcinfo;
|
||||||
readjpg_dcinfo = 0;
|
readjpg_dcinfo = 0;
|
||||||
}
|
|
||||||
if ( decodejpg_dcinfo ) {
|
|
||||||
jpeg_destroy_decompress( decodejpg_dcinfo );
|
|
||||||
delete decodejpg_dcinfo;
|
|
||||||
decodejpg_dcinfo = 0;
|
|
||||||
}
|
|
||||||
for ( unsigned int quality=0; quality <= 100; quality += 1 ) {
|
|
||||||
if ( writejpg_ccinfo[quality] ) {
|
|
||||||
jpeg_destroy_compress( writejpg_ccinfo[quality] );
|
|
||||||
delete writejpg_ccinfo[quality];
|
|
||||||
writejpg_ccinfo[quality] = NULL;
|
|
||||||
}
|
|
||||||
} // end foreach quality
|
|
||||||
}
|
}
|
||||||
}
|
if ( decodejpg_dcinfo ) {
|
||||||
|
jpeg_destroy_decompress( decodejpg_dcinfo );
|
||||||
|
delete decodejpg_dcinfo;
|
||||||
|
decodejpg_dcinfo = 0;
|
||||||
|
}
|
||||||
|
for ( unsigned int quality=0; quality <= 100; quality += 1 ) {
|
||||||
|
if ( writejpg_ccinfo[quality] ) {
|
||||||
|
jpeg_destroy_compress( writejpg_ccinfo[quality] );
|
||||||
|
delete writejpg_ccinfo[quality];
|
||||||
|
writejpg_ccinfo[quality] = NULL;
|
||||||
|
}
|
||||||
|
} // end foreach quality
|
||||||
|
|
||||||
|
if ( sws_convert_context ) {
|
||||||
|
sws_freeContext(sws_convert_context);
|
||||||
|
sws_convert_context = NULL;
|
||||||
|
}
|
||||||
|
} // end void Image::Deinitialise()
|
||||||
|
|
||||||
void Image::Initialise() {
|
void Image::Initialise() {
|
||||||
/* Assign the blend pointer to function */
|
/* Assign the blend pointer to function */
|
||||||
|
@ -655,15 +663,16 @@ void Image::Assign( const Image &image ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) {
|
if ( !buffer || image.width != width || image.height != height
|
||||||
|
|| image.colours != colours || image.subpixelorder != subpixelorder) {
|
||||||
|
|
||||||
if (holdbuffer && buffer) {
|
if ( holdbuffer && buffer ) {
|
||||||
if (new_size > allocation) {
|
if ( new_size > allocation ) {
|
||||||
Error("Held buffer is undersized for assigned buffer");
|
Error("Held buffer is undersized for assigned buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(new_size > allocation || !buffer) {
|
if ( new_size > allocation || !buffer) {
|
||||||
// DumpImgBuffer(); This is also done in AllocImgBuffer
|
// DumpImgBuffer(); This is also done in AllocImgBuffer
|
||||||
AllocImgBuffer(new_size);
|
AllocImgBuffer(new_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void DumpImgBuffer() {
|
inline void DumpImgBuffer() {
|
||||||
DumpBuffer(buffer,buffertype);
|
DumpBuffer(buffer, buffertype);
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
allocation = 0;
|
allocation = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
|
||||||
if ( options.mTerminalLevel != NOOPT )
|
if ( options.mTerminalLevel != NOOPT )
|
||||||
tempTerminalLevel = options.mTerminalLevel;
|
tempTerminalLevel = options.mTerminalLevel;
|
||||||
|
|
||||||
|
// DEBUG1 == 1. So >= DEBUG1, we set to DEBUG9?! Why?
|
||||||
if ( options.mDatabaseLevel != NOOPT )
|
if ( options.mDatabaseLevel != NOOPT )
|
||||||
tempDatabaseLevel = options.mDatabaseLevel;
|
tempDatabaseLevel = options.mDatabaseLevel;
|
||||||
else
|
else
|
||||||
|
@ -359,7 +360,7 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) {
|
||||||
if ( databaseLevel > NOOPT ) {
|
if ( databaseLevel > NOOPT ) {
|
||||||
databaseLevel = limit(databaseLevel);
|
databaseLevel = limit(databaseLevel);
|
||||||
if ( mDatabaseLevel != databaseLevel ) {
|
if ( mDatabaseLevel != databaseLevel ) {
|
||||||
if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) {
|
if ( (databaseLevel > NOLOG) && (mDatabaseLevel <= NOLOG) ) { // <= NOLOG would be NOOPT
|
||||||
if ( !zmDbConnect() ) {
|
if ( !zmDbConnect() ) {
|
||||||
databaseLevel = NOLOG;
|
databaseLevel = NOLOG;
|
||||||
}
|
}
|
||||||
|
@ -535,8 +536,11 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
if ( level <= mFileLevel ) {
|
if ( level <= mFileLevel ) {
|
||||||
if ( !mLogFileFP )
|
if ( !mLogFileFP ) {
|
||||||
|
log_mutex.unlock();
|
||||||
openFile();
|
openFile();
|
||||||
|
log_mutex.lock();
|
||||||
|
}
|
||||||
if ( mLogFileFP ) {
|
if ( mLogFileFP ) {
|
||||||
fputs(logString, mLogFileFP);
|
fputs(logString, mLogFileFP);
|
||||||
if ( mFlush )
|
if ( mFlush )
|
||||||
|
|
|
@ -413,14 +413,14 @@ Monitor::Monitor(
|
||||||
|
|
||||||
auto_resume_time = 0;
|
auto_resume_time = 0;
|
||||||
|
|
||||||
if ( strcmp( config.event_close_mode, "time" ) == 0 )
|
if ( strcmp(config.event_close_mode, "time") == 0 )
|
||||||
event_close_mode = CLOSE_TIME;
|
event_close_mode = CLOSE_TIME;
|
||||||
else if ( strcmp( config.event_close_mode, "alarm" ) == 0 )
|
else if ( strcmp(config.event_close_mode, "alarm") == 0 )
|
||||||
event_close_mode = CLOSE_ALARM;
|
event_close_mode = CLOSE_ALARM;
|
||||||
else
|
else
|
||||||
event_close_mode = CLOSE_IDLE;
|
event_close_mode = CLOSE_IDLE;
|
||||||
|
|
||||||
Debug( 1, "monitor purpose=%d", purpose );
|
Debug(1, "monitor purpose=%d", purpose);
|
||||||
|
|
||||||
mem_size = sizeof(SharedData)
|
mem_size = sizeof(SharedData)
|
||||||
+ sizeof(TriggerData)
|
+ sizeof(TriggerData)
|
||||||
|
@ -440,17 +440,15 @@ Monitor::Monitor(
|
||||||
storage = new Storage(storage_id);
|
storage = new Storage(storage_id);
|
||||||
Debug(1, "Storage path: %s", storage->Path());
|
Debug(1, "Storage path: %s", storage->Path());
|
||||||
// Should maybe store this for later use
|
// Should maybe store this for later use
|
||||||
char monitor_dir[PATH_MAX] = "";
|
char monitor_dir[PATH_MAX];
|
||||||
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
|
snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id);
|
||||||
|
|
||||||
if ( purpose == CAPTURE ) {
|
if ( purpose == CAPTURE ) {
|
||||||
if ( mkdir(monitor_dir, 0755) ) {
|
if ( mkdir(monitor_dir, 0755) && ( errno != EEXIST ) ) {
|
||||||
if ( errno != EEXIST ) {
|
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
|
||||||
Error("Can't mkdir %s: %s", monitor_dir, strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! this->connect() ) {
|
if ( !this->connect() ) {
|
||||||
Error("unable to connect, but doing capture");
|
Error("unable to connect, but doing capture");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
@ -486,20 +484,16 @@ Monitor::Monitor(
|
||||||
video_store_data->size = sizeof(VideoStoreData);
|
video_store_data->size = sizeof(VideoStoreData);
|
||||||
//video_store_data->frameNumber = 0;
|
//video_store_data->frameNumber = 0;
|
||||||
} else if ( purpose == ANALYSIS ) {
|
} else if ( purpose == ANALYSIS ) {
|
||||||
if ( ! ( this->connect() && mem_ptr ) ) exit(-1);
|
if ( ! (this->connect() && mem_ptr && shared_data->valid) ) {
|
||||||
|
Error("Shared data not initialised by capture daemon for monitor %s", name);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
shared_data->state = IDLE;
|
shared_data->state = IDLE;
|
||||||
shared_data->last_read_time = 0;
|
shared_data->last_read_time = 0;
|
||||||
shared_data->alarm_x = -1;
|
shared_data->alarm_x = -1;
|
||||||
shared_data->alarm_y = -1;
|
shared_data->alarm_y = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( ! mem_ptr ) || ! shared_data->valid ) {
|
|
||||||
if ( purpose != QUERY ) {
|
|
||||||
Error("Shared data not initialised by capture daemon for monitor %s", name);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start_time = last_fps_time = time( 0 );
|
start_time = last_fps_time = time( 0 );
|
||||||
|
|
||||||
event = 0;
|
event = 0;
|
||||||
|
@ -545,9 +539,8 @@ Monitor::Monitor(
|
||||||
FifoStream::fifo_create_if_missing(diag_path_d.c_str());
|
FifoStream::fifo_create_if_missing(diag_path_d.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // end if purpose == ANALYSIS
|
||||||
} // end if purpose == ANALYSIS
|
} // Monitor::Monitor
|
||||||
} // Monitor::Monitor
|
|
||||||
|
|
||||||
bool Monitor::connect() {
|
bool Monitor::connect() {
|
||||||
Debug(3, "Connecting to monitor. Purpose is %d", purpose );
|
Debug(3, "Connecting to monitor. Purpose is %d", purpose );
|
||||||
|
@ -1815,7 +1808,7 @@ void Monitor::Reload() {
|
||||||
"`AlarmFrameCount`, `SectionLength`, `MinSectionLength`, `FrameSkip`, "
|
"`AlarmFrameCount`, `SectionLength`, `MinSectionLength`, `FrameSkip`, "
|
||||||
"`MotionFrameSkip`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`, "
|
"`MotionFrameSkip`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`, "
|
||||||
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, "
|
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, "
|
||||||
"`SignalCheckColour` FROM `Monitors` WHERE `Id` = '%d'", id);
|
"`SignalCheckPoints`, `SignalCheckColour` FROM `Monitors` WHERE `Id` = '%d'", id);
|
||||||
|
|
||||||
zmDbRow *row = zmDbFetchOne(sql);
|
zmDbRow *row = zmDbFetchOne(sql);
|
||||||
if ( !row ) {
|
if ( !row ) {
|
||||||
|
@ -1861,13 +1854,8 @@ void Monitor::Reload() {
|
||||||
alarm_ref_blend_perc = atoi(dbrow[index++]);
|
alarm_ref_blend_perc = atoi(dbrow[index++]);
|
||||||
track_motion = atoi(dbrow[index++]);
|
track_motion = atoi(dbrow[index++]);
|
||||||
|
|
||||||
signal_check_points = dbrow[index]?atoi(dbrow[index]):0;index++;
|
signal_check_points = dbrow[index]?atoi(dbrow[index]):0; index++;
|
||||||
|
signal_check_colour = strtol(dbrow[index][0]=='#'?dbrow[index]+1:dbrow[index], 0, 16); index++;
|
||||||
if ( dbrow[index][0] == '#' )
|
|
||||||
signal_check_colour = strtol(dbrow[index]+1,0,16);
|
|
||||||
else
|
|
||||||
signal_check_colour = strtol(dbrow[index],0,16);
|
|
||||||
index++;
|
|
||||||
|
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
shared_data->alarm_x = shared_data->alarm_y = -1;
|
shared_data->alarm_x = shared_data->alarm_y = -1;
|
||||||
|
@ -1880,7 +1868,7 @@ void Monitor::Reload() {
|
||||||
} // end if row
|
} // end if row
|
||||||
|
|
||||||
ReloadZones();
|
ReloadZones();
|
||||||
} // end void Monitor::Reload()
|
} // end void Monitor::Reload()
|
||||||
|
|
||||||
void Monitor::ReloadZones() {
|
void Monitor::ReloadZones() {
|
||||||
Debug(1, "Reloading zones for monitor %s", name);
|
Debug(1, "Reloading zones for monitor %s", name);
|
||||||
|
@ -2059,20 +2047,21 @@ int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose p
|
||||||
} // end int Monitor::LoadFfmpegMonitors
|
} // end int Monitor::LoadFfmpegMonitors
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
/*
|
/* For reference
|
||||||
std::string load_monitor_sql =
|
std::string load_monitor_sql =
|
||||||
"SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, "
|
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, "
|
||||||
"AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
|
"`AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
|
||||||
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
|
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
|
||||||
"Protocol, Method, Options, User, Pass, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, "
|
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
|
||||||
"SaveJPEGs, VideoWriter, EncoderParameters,
|
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
|
||||||
"OutputCodec, Encoder, OutputContainer,"
|
"`SaveJPEGs`, `VideoWriter`, `EncoderParameters`, "
|
||||||
" RecordAudio, "
|
//" OutputCodec, Encoder, OutputContainer, "
|
||||||
"Brightness, Contrast, Hue, Colour, "
|
"`RecordAudio`, "
|
||||||
"EventPrefix, LabelFormat, LabelX, LabelY, LabelSize,"
|
"`Brightness`, `Contrast`, `Hue`, `Colour`, "
|
||||||
"ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, "
|
"`EventPrefix`, `LabelFormat`, `LabelX`, `LabelY`, `LabelSize`,"
|
||||||
"SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, "
|
"`ImageBufferCount`, `WarmupCount`, `PreEventCount`, `PostEventCount`, `StreamReplayBuffer`, `AlarmFrameCount`, "
|
||||||
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, SignalCheckColour FROM Monitors";
|
"`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
|
||||||
|
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`, `SignalCheckPoints`, `SignalCheckColour` FROM `Monitors`";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
|
Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
|
||||||
|
@ -2092,8 +2081,6 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
|
||||||
|
|
||||||
double capture_max_fps = dbrow[col] ? atof(dbrow[col]) : 0.0; col++;
|
double capture_max_fps = dbrow[col] ? atof(dbrow[col]) : 0.0; col++;
|
||||||
double capture_delay = ( capture_max_fps > 0.0 ) ? int(DT_PREC_3/capture_max_fps) : 0;
|
double capture_delay = ( capture_max_fps > 0.0 ) ? int(DT_PREC_3/capture_max_fps) : 0;
|
||||||
|
|
||||||
Debug(1,"Capture Delay!? %.3f", capture_delay);
|
|
||||||
unsigned int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
|
unsigned int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
|
||||||
|
|
||||||
const char *device = dbrow[col]; col++;
|
const char *device = dbrow[col]; col++;
|
||||||
|
@ -2166,9 +2153,9 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) {
|
||||||
int ref_blend_perc = atoi(dbrow[col]); col++;
|
int ref_blend_perc = atoi(dbrow[col]); col++;
|
||||||
int alarm_ref_blend_perc = atoi(dbrow[col]); col++;
|
int alarm_ref_blend_perc = atoi(dbrow[col]); col++;
|
||||||
int track_motion = atoi(dbrow[col]); col++;
|
int track_motion = atoi(dbrow[col]); col++;
|
||||||
|
bool embed_exif = (*dbrow[col] != '0'); col++;
|
||||||
int signal_check_points = dbrow[col] ? atoi(dbrow[col]) : 0;col++;
|
int signal_check_points = dbrow[col] ? atoi(dbrow[col]) : 0;col++;
|
||||||
int signal_check_color = strtol(dbrow[col][0] == '#' ? dbrow[col]+1 : dbrow[col], 0, 16); col++;
|
int signal_check_color = strtol(dbrow[col][0] == '#' ? dbrow[col]+1 : dbrow[col], 0, 16); col++;
|
||||||
bool embed_exif = (*dbrow[col] != '0'); col++;
|
|
||||||
|
|
||||||
Camera *camera = 0;
|
Camera *camera = 0;
|
||||||
if ( type == "Local" ) {
|
if ( type == "Local" ) {
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
//
|
//
|
||||||
// ZoneMinder Monitor Class Implementation, $Date$, $Revision$
|
// ZoneMinder Monitor Class Implementation, $Date$, $Revision$
|
||||||
// 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
|
||||||
// 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
|
||||||
// of the License, or (at your option) any later version.
|
// of the License, or (at your option) any later version.
|
||||||
//
|
//
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_db.h"
|
#include "zm_db.h"
|
||||||
|
@ -73,7 +73,7 @@ bool MonitorStream::checkSwapPath(const char *path, bool create_path) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path )
|
} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path )
|
||||||
|
|
||||||
void MonitorStream::processCommand(const CmdMsg *msg) {
|
void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
||||||
|
@ -265,7 +265,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
//status_data.enabled = monitor->shared_data->active;
|
//status_data.enabled = monitor->shared_data->active;
|
||||||
status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF;
|
status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF;
|
||||||
status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON;
|
status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON;
|
||||||
Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
|
Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
|
||||||
status_data.buffer_level,
|
status_data.buffer_level,
|
||||||
status_data.delayed,
|
status_data.delayed,
|
||||||
status_data.paused,
|
status_data.paused,
|
||||||
|
@ -327,7 +327,7 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) {
|
||||||
// Calculate how long it takes to actually send the frame
|
// Calculate how long it takes to actually send the frame
|
||||||
struct timeval frameStartTime;
|
struct timeval frameStartTime;
|
||||||
gettimeofday(&frameStartTime, NULL);
|
gettimeofday(&frameStartTime, NULL);
|
||||||
|
|
||||||
fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout);
|
fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout);
|
||||||
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
||||||
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
||||||
|
@ -383,7 +383,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
||||||
// Calculate how long it takes to actually send the frame
|
// Calculate how long it takes to actually send the frame
|
||||||
struct timeval frameStartTime;
|
struct timeval frameStartTime;
|
||||||
gettimeofday(&frameStartTime, NULL);
|
gettimeofday(&frameStartTime, NULL);
|
||||||
|
|
||||||
fputs("--ZoneMinderFrame\r\n", stdout);
|
fputs("--ZoneMinderFrame\r\n", stdout);
|
||||||
switch( type ) {
|
switch( type ) {
|
||||||
case STREAM_JPEG :
|
case STREAM_JPEG :
|
||||||
|
@ -412,7 +412,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
||||||
}
|
}
|
||||||
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
||||||
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
||||||
if ( !zm_terminate ) {
|
if ( !zm_terminate ) {
|
||||||
// If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate
|
// If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate
|
||||||
Warning("Unable to send stream frame: %s", strerror(errno));
|
Warning("Unable to send stream frame: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
@ -430,7 +430,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
||||||
Warning("Frame send time %d msec too slow, throttling maxfps to %.2f",
|
Warning("Frame send time %d msec too slow, throttling maxfps to %.2f",
|
||||||
frameSendTime, maxfps);
|
frameSendTime, maxfps);
|
||||||
}
|
}
|
||||||
}
|
} // Not mpeg
|
||||||
last_frame_sent = TV_2_FLOAT(now);
|
last_frame_sent = TV_2_FLOAT(now);
|
||||||
return true;
|
return true;
|
||||||
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
||||||
|
@ -455,7 +455,7 @@ void MonitorStream::runStream() {
|
||||||
fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);
|
fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);
|
||||||
|
|
||||||
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count
|
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count
|
||||||
unsigned int last_read_index = monitor->image_buffer_count;
|
unsigned int last_read_index = monitor->image_buffer_count;
|
||||||
|
|
||||||
time_t stream_start_time;
|
time_t stream_start_time;
|
||||||
time(&stream_start_time);
|
time(&stream_start_time);
|
||||||
|
@ -474,22 +474,21 @@ void MonitorStream::runStream() {
|
||||||
Image *paused_image = NULL;
|
Image *paused_image = NULL;
|
||||||
struct timeval paused_timestamp;
|
struct timeval paused_timestamp;
|
||||||
|
|
||||||
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
|
||||||
const int max_swap_len_suffix = 15;
|
|
||||||
|
|
||||||
int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator
|
|
||||||
int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1;
|
|
||||||
int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
|
|
||||||
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
|
||||||
|
|
||||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||||
|
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
||||||
|
const int max_swap_len_suffix = 15;
|
||||||
|
|
||||||
|
int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator
|
||||||
|
int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1;
|
||||||
|
int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
|
||||||
|
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
||||||
|
|
||||||
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
|
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
|
||||||
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
||||||
} else {
|
} else {
|
||||||
swap_path = staticConfig.PATH_SWAP;
|
swap_path = staticConfig.PATH_SWAP;
|
||||||
|
|
||||||
Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
|
Debug(3, "Checking swap path folder: %s", swap_path.c_str());
|
||||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||||
swap_path += stringtf("/zmswap-m%d", monitor->Id());
|
swap_path += stringtf("/zmswap-m%d", monitor->Id());
|
||||||
|
|
||||||
|
@ -509,8 +508,8 @@ void MonitorStream::runStream() {
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Assigning temporary buffer");
|
Debug(2, "Assigning temporary buffer");
|
||||||
temp_image_buffer = new SwapImage[temp_image_buffer_count];
|
temp_image_buffer = new SwapImage[temp_image_buffer_count];
|
||||||
memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count );
|
memset(temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count);
|
||||||
Debug( 2, "Assigned temporary buffer" );
|
Debug(2, "Assigned temporary buffer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -525,7 +524,7 @@ void MonitorStream::runStream() {
|
||||||
Debug(1, "Using %.3f for fps instead of current fps %.3f", capture_max_fps, capture_fps);
|
Debug(1, "Using %.3f for fps instead of current fps %.3f", capture_max_fps, capture_fps);
|
||||||
capture_fps = capture_max_fps;
|
capture_fps = capture_max_fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( capture_fps < 1 ) {
|
if ( capture_fps < 1 ) {
|
||||||
max_secs_since_last_sent_frame = 10/capture_fps;
|
max_secs_since_last_sent_frame = 10/capture_fps;
|
||||||
Debug(1, "Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f",
|
Debug(1, "Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f",
|
||||||
|
@ -562,7 +561,7 @@ void MonitorStream::runStream() {
|
||||||
touch(sock_path_lock);
|
touch(sock_path_lock);
|
||||||
last_comm_update = now;
|
last_comm_update = now;
|
||||||
}
|
}
|
||||||
} // end if connkey
|
} // end if connkey
|
||||||
|
|
||||||
if ( paused ) {
|
if ( paused ) {
|
||||||
if ( !was_paused ) {
|
if ( !was_paused ) {
|
||||||
|
@ -589,7 +588,7 @@ void MonitorStream::runStream() {
|
||||||
} else {
|
} else {
|
||||||
if ( !paused ) {
|
if ( !paused ) {
|
||||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||||
//Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
|
// Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
|
||||||
SwapImage *swap_image = &temp_image_buffer[temp_index];
|
SwapImage *swap_image = &temp_image_buffer[temp_index];
|
||||||
|
|
||||||
if ( !swap_image->valid ) {
|
if ( !swap_image->valid ) {
|
||||||
|
@ -597,51 +596,61 @@ void MonitorStream::runStream() {
|
||||||
delayed = true;
|
delayed = true;
|
||||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
||||||
} else {
|
} else {
|
||||||
//Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
|
// Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
|
||||||
double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate;
|
double expected_delta_time = ((TV_2_FLOAT(swap_image->timestamp) - TV_2_FLOAT(last_frame_timestamp)) * ZM_RATE_BASE)/replay_rate;
|
||||||
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||||
|
|
||||||
//Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) );
|
// Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) );
|
||||||
// If the next frame is due
|
// If the next frame is due
|
||||||
if ( actual_delta_time > expected_delta_time ) {
|
if ( actual_delta_time > expected_delta_time ) {
|
||||||
//Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
|
// Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
|
||||||
if ( temp_index%frame_mod == 0 ) {
|
if ( temp_index%frame_mod == 0 ) {
|
||||||
Debug( 2, "Sending delayed frame %d", temp_index );
|
Debug(2, "Sending delayed frame %d", temp_index);
|
||||||
// Send the next frame
|
// Send the next frame
|
||||||
if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) )
|
if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) {
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
|
}
|
||||||
memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
|
memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
|
||||||
//frame_sent = true;
|
// frame_sent = true;
|
||||||
}
|
}
|
||||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
|
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( step != 0 ) {
|
} else if ( step != 0 ) {
|
||||||
temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count );
|
temp_read_index = MOD_ADD(temp_read_index, (step>0?1:-1), temp_image_buffer_count);
|
||||||
|
|
||||||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||||
|
|
||||||
// Send the next frame
|
// Send the next frame
|
||||||
if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) )
|
if ( !sendFrame(
|
||||||
|
temp_image_buffer[temp_read_index].file_name,
|
||||||
|
&temp_image_buffer[temp_read_index].timestamp
|
||||||
|
) ) {
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) );
|
}
|
||||||
//frame_sent = true;
|
memcpy(
|
||||||
|
&last_frame_timestamp,
|
||||||
|
&(swap_image->timestamp),
|
||||||
|
sizeof(last_frame_timestamp)
|
||||||
|
);
|
||||||
|
// frame_sent = true;
|
||||||
step = 0;
|
step = 0;
|
||||||
} else {
|
} else {
|
||||||
//paused?
|
//paused?
|
||||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||||
|
|
||||||
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||||
if ( got_command || actual_delta_time > 5 ) {
|
if ( got_command || (actual_delta_time > 5) ) {
|
||||||
// Send keepalive
|
// Send keepalive
|
||||||
Debug(2, "Sending keepalive frame %d", temp_index);
|
Debug(2, "Sending keepalive frame %d", temp_index);
|
||||||
// Send the next frame
|
// Send the next frame
|
||||||
if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) )
|
if ( !sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) {
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
//frame_sent = true;
|
}
|
||||||
|
// frame_sent = true;
|
||||||
}
|
}
|
||||||
} // end if (!paused) or step or paused
|
} // end if (!paused) or step or paused
|
||||||
} // end if have exceeded buffer or not
|
} // end if have exceeded buffer or not
|
||||||
|
|
||||||
if ( temp_read_index == temp_write_index ) {
|
if ( temp_read_index == temp_write_index ) {
|
||||||
// Go back to live viewing
|
// Go back to live viewing
|
||||||
|
@ -652,16 +661,16 @@ void MonitorStream::runStream() {
|
||||||
delayed = false;
|
delayed = false;
|
||||||
replay_rate = ZM_RATE_BASE;
|
replay_rate = ZM_RATE_BASE;
|
||||||
}
|
}
|
||||||
} // end if ( buffered_playback && delayed )
|
} // end if ( buffered_playback && delayed )
|
||||||
|
|
||||||
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
||||||
// have a new image to send
|
// have a new image to send
|
||||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
||||||
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
||||||
if ( !paused && !delayed ) {
|
if ( !paused && !delayed ) {
|
||||||
last_read_index = monitor->shared_data->last_write_index;
|
last_read_index = monitor->shared_data->last_write_index;
|
||||||
Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
||||||
index, frame_mod, frame_count, paused, delayed );
|
index, frame_mod, frame_count, paused, delayed);
|
||||||
// Send the next frame
|
// Send the next frame
|
||||||
Monitor::Snapshot *snap = &monitor->image_buffer[index];
|
Monitor::Snapshot *snap = &monitor->image_buffer[index];
|
||||||
|
|
||||||
|
@ -670,9 +679,13 @@ void MonitorStream::runStream() {
|
||||||
Debug(2, "sendFrame failed, quiting.");
|
Debug(2, "sendFrame failed, quiting.");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
}
|
}
|
||||||
// Perhaps we should use NOW instead.
|
// Perhaps we should use NOW instead.
|
||||||
memcpy(&last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp));
|
memcpy(
|
||||||
//frame_sent = true;
|
&last_frame_timestamp,
|
||||||
|
snap->timestamp,
|
||||||
|
sizeof(last_frame_timestamp)
|
||||||
|
);
|
||||||
|
// frame_sent = true;
|
||||||
|
|
||||||
temp_read_index = temp_write_index;
|
temp_read_index = temp_write_index;
|
||||||
} else {
|
} else {
|
||||||
|
@ -697,7 +710,7 @@ void MonitorStream::runStream() {
|
||||||
if ( !sendFrame(paused_image, &paused_timestamp) )
|
if ( !sendFrame(paused_image, &paused_timestamp) )
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Would have sent keepalive frame, but had no paused_image ");
|
Debug(2, "Would have sent keepalive frame, but had no paused_image");
|
||||||
}
|
}
|
||||||
} // end if actual_delta_time > 5
|
} // end if actual_delta_time > 5
|
||||||
} // end if change in zoom
|
} // end if change in zoom
|
||||||
|
@ -718,27 +731,27 @@ void MonitorStream::runStream() {
|
||||||
temp_index);
|
temp_index);
|
||||||
temp_image_buffer[temp_index].valid = true;
|
temp_image_buffer[temp_index].valid = true;
|
||||||
}
|
}
|
||||||
memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) );
|
memcpy(&(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp));
|
||||||
monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality );
|
monitor->image_buffer[index].image->WriteJpeg(temp_image_buffer[temp_index].file_name, config.jpeg_file_quality);
|
||||||
temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count );
|
temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count);
|
||||||
if ( temp_write_index == temp_read_index ) {
|
if ( temp_write_index == temp_read_index ) {
|
||||||
// Go back to live viewing
|
// Go back to live viewing
|
||||||
Warning( "Exceeded temporary buffer, resuming live play" );
|
Warning("Exceeded temporary buffer, resuming live play");
|
||||||
paused = false;
|
paused = false;
|
||||||
delayed = false;
|
delayed = false;
|
||||||
replay_rate = ZM_RATE_BASE;
|
replay_rate = ZM_RATE_BASE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Warning( "Unable to store frame as timestamp invalid" );
|
Warning("Unable to store frame as timestamp invalid");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Warning( "Unable to store frame as shared memory invalid" );
|
Warning("Unable to store frame as shared memory invalid");
|
||||||
}
|
}
|
||||||
} // end if buffered playback
|
} // end if buffered playback
|
||||||
frame_count++;
|
frame_count++;
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
|
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
|
||||||
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
||||||
|
|
||||||
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
|
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
|
||||||
Debug(3, "Sleeping for (%d)", sleep_time);
|
Debug(3, "Sleeping for (%d)", sleep_time);
|
||||||
|
@ -755,10 +768,10 @@ void MonitorStream::runStream() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( ! last_frame_sent ) {
|
if ( !last_frame_sent ) {
|
||||||
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
|
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
|
||||||
last_frame_sent = now.tv_sec;
|
last_frame_sent = now.tv_sec;
|
||||||
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d) ",
|
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
|
||||||
frame_mod, frame_count);
|
frame_mod, frame_count);
|
||||||
} else if (
|
} else if (
|
||||||
(!paused)
|
(!paused)
|
||||||
|
@ -799,9 +812,9 @@ void MonitorStream::runStream() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
globfree( &pglob );
|
globfree(&pglob);
|
||||||
if ( rmdir(swap_path.c_str()) < 0 ) {
|
if ( rmdir(swap_path.c_str()) < 0 ) {
|
||||||
Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) );
|
Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno));
|
||||||
}
|
}
|
||||||
} // end if checking for swap_path
|
} // end if checking for swap_path
|
||||||
} // end if buffered_playback
|
} // end if buffered_playback
|
||||||
|
@ -809,7 +822,7 @@ void MonitorStream::runStream() {
|
||||||
closeComms();
|
closeComms();
|
||||||
} // end MonitorStream::runStream
|
} // end MonitorStream::runStream
|
||||||
|
|
||||||
void MonitorStream::SingleImage( int scale ) {
|
void MonitorStream::SingleImage(int scale) {
|
||||||
int img_buffer_size = 0;
|
int img_buffer_size = 0;
|
||||||
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
|
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
Image scaled_image;
|
Image scaled_image;
|
||||||
|
@ -817,42 +830,45 @@ void MonitorStream::SingleImage( int scale ) {
|
||||||
Image *snap_image = snap->image;
|
Image *snap_image = snap->image;
|
||||||
|
|
||||||
if ( scale != ZM_SCALE_BASE ) {
|
if ( scale != ZM_SCALE_BASE ) {
|
||||||
scaled_image.Assign( *snap_image );
|
scaled_image.Assign(*snap_image);
|
||||||
scaled_image.Scale( scale );
|
scaled_image.Scale(scale);
|
||||||
snap_image = &scaled_image;
|
snap_image = &scaled_image;
|
||||||
}
|
}
|
||||||
if ( !config.timestamp_on_capture ) {
|
if ( !config.timestamp_on_capture ) {
|
||||||
monitor->TimestampImage( snap_image, snap->timestamp );
|
monitor->TimestampImage(snap_image, snap->timestamp);
|
||||||
}
|
}
|
||||||
snap_image->EncodeJpeg( img_buffer, &img_buffer_size );
|
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
"Content-Length: %d\r\n"
|
||||||
fwrite( img_buffer, img_buffer_size, 1, stdout );
|
"Content-Type: image/jpeg\r\n\r\n",
|
||||||
|
img_buffer_size);
|
||||||
|
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MonitorStream::SingleImageRaw( int scale ) {
|
void MonitorStream::SingleImageRaw(int scale) {
|
||||||
Image scaled_image;
|
Image scaled_image;
|
||||||
Monitor::Snapshot *snap = monitor->getSnapshot();
|
Monitor::Snapshot *snap = monitor->getSnapshot();
|
||||||
Image *snap_image = snap->image;
|
Image *snap_image = snap->image;
|
||||||
|
|
||||||
if ( scale != ZM_SCALE_BASE ) {
|
if ( scale != ZM_SCALE_BASE ) {
|
||||||
scaled_image.Assign( *snap_image );
|
scaled_image.Assign(*snap_image);
|
||||||
scaled_image.Scale( scale );
|
scaled_image.Scale(scale);
|
||||||
snap_image = &scaled_image;
|
snap_image = &scaled_image;
|
||||||
}
|
}
|
||||||
if ( !config.timestamp_on_capture ) {
|
if ( !config.timestamp_on_capture ) {
|
||||||
monitor->TimestampImage( snap_image, snap->timestamp );
|
monitor->TimestampImage(snap_image, snap->timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" );
|
"Content-Length: %d\r\n"
|
||||||
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
|
"Content-Type: image/x-rgb\r\n\r\n",
|
||||||
|
snap_image->Size());
|
||||||
|
fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB_H
|
#ifdef HAVE_ZLIB_H
|
||||||
void MonitorStream::SingleImageZip( int scale ) {
|
void MonitorStream::SingleImageZip(int scale) {
|
||||||
unsigned long img_buffer_size = 0;
|
unsigned long img_buffer_size = 0;
|
||||||
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
|
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
Image scaled_image;
|
Image scaled_image;
|
||||||
|
@ -861,17 +877,19 @@ void MonitorStream::SingleImageZip( int scale ) {
|
||||||
Image *snap_image = snap->image;
|
Image *snap_image = snap->image;
|
||||||
|
|
||||||
if ( scale != ZM_SCALE_BASE ) {
|
if ( scale != ZM_SCALE_BASE ) {
|
||||||
scaled_image.Assign( *snap_image );
|
scaled_image.Assign(*snap_image);
|
||||||
scaled_image.Scale( scale );
|
scaled_image.Scale(scale);
|
||||||
snap_image = &scaled_image;
|
snap_image = &scaled_image;
|
||||||
}
|
}
|
||||||
if ( !config.timestamp_on_capture ) {
|
if ( !config.timestamp_on_capture ) {
|
||||||
monitor->TimestampImage( snap_image, snap->timestamp );
|
monitor->TimestampImage(snap_image, snap->timestamp);
|
||||||
}
|
}
|
||||||
snap_image->Zip( img_buffer, &img_buffer_size );
|
snap_image->Zip(img_buffer, &img_buffer_size);
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
"Content-Length: %ld\r\n"
|
||||||
fwrite( img_buffer, img_buffer_size, 1, stdout );
|
"Content-Type: image/x-rgbz\r\n\r\n",
|
||||||
|
img_buffer_size);
|
||||||
|
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||||
}
|
}
|
||||||
#endif // HAVE_ZLIB_H
|
#endif // HAVE_ZLIB_H
|
||||||
|
|
|
@ -1060,7 +1060,6 @@ int RemoteCameraHttp::PreCapture() {
|
||||||
if ( sd < 0 ) {
|
if ( sd < 0 ) {
|
||||||
Connect();
|
Connect();
|
||||||
if ( sd < 0 ) {
|
if ( sd < 0 ) {
|
||||||
Error("Unable to connect to camera");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
mode = SINGLE_IMAGE;
|
mode = SINGLE_IMAGE;
|
||||||
|
|
|
@ -144,7 +144,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), (unsigned int)ha1Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ ) {
|
for ( unsigned int j = 0; j < md5len; j++ ) {
|
||||||
|
@ -159,7 +159,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), (unsigned int)ha2Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ ) {
|
for ( unsigned int j = 0; j < md5len; j++ ) {
|
||||||
|
@ -180,7 +180,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), (unsigned int)digestData.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ ) {
|
for ( unsigned int j = 0; j < md5len; j++ ) {
|
||||||
|
|
|
@ -19,9 +19,6 @@
|
||||||
#ifndef ZM_RTSP_AUTH_H
|
#ifndef ZM_RTSP_AUTH_H
|
||||||
#define ZM_RTSP_AUTH_H
|
#define ZM_RTSP_AUTH_H
|
||||||
|
|
||||||
#if HAVE_GNUTLS_OPENSSL_H
|
|
||||||
#include <gnutls/openssl.h>
|
|
||||||
#endif
|
|
||||||
#if HAVE_GNUTLS_GNUTLS_H
|
#if HAVE_GNUTLS_GNUTLS_H
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,7 +44,12 @@ bool StreamBase::loadMonitor(int monitor_id) {
|
||||||
Error("Unable to load monitor id %d for streaming", monitor_id);
|
Error("Unable to load monitor id %d for streaming", monitor_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( ! monitor->connect() ) {
|
if ( monitor->GetFunction() == Monitor::NONE ) {
|
||||||
|
Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !monitor->connect() ) {
|
||||||
Error("Unable to connect to monitor id %d for streaming", monitor_id);
|
Error("Unable to connect to monitor id %d for streaming", monitor_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +114,7 @@ bool StreamBase::checkCommandQueue() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *StreamBase::prepareImage( Image *image ) {
|
Image *StreamBase::prepareImage(Image *image) {
|
||||||
|
|
||||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||||
|
@ -119,50 +124,51 @@ Image *StreamBase::prepareImage( Image *image ) {
|
||||||
|
|
||||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
|
||||||
|
|
||||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
|
||||||
|
|
||||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
|
||||||
|
|
||||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
|
||||||
|
|
||||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
|
||||||
|
|
||||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
|
||||||
|
|
||||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
|
||||||
|
|
||||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
|
||||||
|
|
||||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
|
||||||
|
|
||||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
|
||||||
|
|
||||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
|
||||||
|
|
||||||
if ( mag != ZM_SCALE_BASE ) {
|
Debug(3,
|
||||||
if ( act_mag != ZM_SCALE_BASE ) {
|
"Scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
||||||
Debug(3, "Magnifying by %d", mag);
|
"Last scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
||||||
if ( !image_copied ) {
|
"Base image width = %d, height = %d\n"
|
||||||
static Image copy_image;
|
"Virtual image width = %d, height = %d\n"
|
||||||
copy_image.Assign(*image);
|
"Last virtual image width = %d, height = %d\n"
|
||||||
image = ©_image;
|
"Actual image width = %d, height = %d\n"
|
||||||
image_copied = true;
|
"Last actual image width = %d, height = %d\n"
|
||||||
}
|
"Display image width = %d, height = %d\n"
|
||||||
image->Scale(mag);
|
"Last display image width = %d, height = %d\n"
|
||||||
}
|
"Send image width = %d, height = %d\n"
|
||||||
|
"Last send image width = %d, height = %d\n",
|
||||||
|
scale, zoom, mag, act_mag,
|
||||||
|
last_scale, last_zoom, last_mag, last_act_mag,
|
||||||
|
base_image_width, base_image_height,
|
||||||
|
virt_image_width, virt_image_height,
|
||||||
|
last_virt_image_width, last_virt_image_height,
|
||||||
|
act_image_width, act_image_height,
|
||||||
|
last_act_image_width, last_act_image_height,
|
||||||
|
disp_image_width, disp_image_height,
|
||||||
|
last_disp_image_width, last_disp_image_height,
|
||||||
|
send_image_width, send_image_height,
|
||||||
|
last_send_image_width, last_send_image_height
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) {
|
||||||
|
Debug(3, "Magnifying by %d", mag);
|
||||||
|
static Image copy_image;
|
||||||
|
copy_image.Assign(*image);
|
||||||
|
image = ©_image;
|
||||||
|
image_copied = true;
|
||||||
|
image->Scale(mag);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(3, "Real image width = %d, height = %d", image->Width(), image->Height());
|
Debug(3, "Real image width = %d, height = %d", image->Width(), image->Height());
|
||||||
|
@ -171,26 +177,22 @@ Image *StreamBase::prepareImage( Image *image ) {
|
||||||
static Box last_crop;
|
static Box last_crop;
|
||||||
|
|
||||||
if ( mag != last_mag || x != last_x || y != last_y ) {
|
if ( mag != last_mag || x != last_x || y != last_y ) {
|
||||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
Debug(3, "Got click at %d,%d x %d", x, y, mag);
|
||||||
|
|
||||||
//if ( !last_mag )
|
|
||||||
//last_mag = mag;
|
|
||||||
|
|
||||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||||
last_crop = Box();
|
last_crop = Box();
|
||||||
|
|
||||||
Debug( 3, "Recalculating crop" );
|
|
||||||
// Recalculate crop parameters, as %ges
|
// Recalculate crop parameters, as %ges
|
||||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||||
click_x += ( x * 100 ) / last_virt_image_width;
|
click_x += ( x * 100 ) / last_virt_image_width;
|
||||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||||
click_y += ( y * 100 ) / last_virt_image_height;
|
click_y += ( y * 100 ) / last_virt_image_height;
|
||||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
Debug(3, "Got adjusted click at %d%%,%d%%", click_x, click_y);
|
||||||
|
|
||||||
// Convert the click locations to the current image pixels
|
// Convert the click locations to the current image pixels
|
||||||
click_x = ( click_x * act_image_width ) / 100;
|
click_x = ( click_x * act_image_width ) / 100;
|
||||||
click_y = ( click_y * act_image_height ) / 100;
|
click_y = ( click_y * act_image_height ) / 100;
|
||||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
Debug(3, "Got readjusted click at %d,%d", click_x, click_y);
|
||||||
|
|
||||||
int lo_x = click_x - (send_image_width/2);
|
int lo_x = click_x - (send_image_width/2);
|
||||||
if ( lo_x < 0 )
|
if ( lo_x < 0 )
|
||||||
|
@ -209,23 +211,25 @@ Image *StreamBase::prepareImage( Image *image ) {
|
||||||
lo_y = hi_y - (send_image_height - 1);
|
lo_y = hi_y - (send_image_height - 1);
|
||||||
}
|
}
|
||||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||||
}
|
} // end if ( mag != last_mag || x != last_x || y != last_y )
|
||||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
|
||||||
|
Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY());
|
||||||
if ( !image_copied ) {
|
if ( !image_copied ) {
|
||||||
static Image copy_image;
|
static Image copy_image;
|
||||||
copy_image.Assign( *image );
|
copy_image.Assign(*image);
|
||||||
image = ©_image;
|
image = ©_image;
|
||||||
image_copied = true;
|
image_copied = true;
|
||||||
}
|
}
|
||||||
image->Crop( last_crop );
|
image->Crop(last_crop);
|
||||||
}
|
} // end if difference in image vs displayed dimensions
|
||||||
|
|
||||||
last_scale = scale;
|
last_scale = scale;
|
||||||
last_zoom = zoom;
|
last_zoom = zoom;
|
||||||
last_x = x;
|
last_x = x;
|
||||||
last_y = y;
|
last_y = y;
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
} // end Image *StreamBase::prepareImage(Image *image)
|
||||||
|
|
||||||
bool StreamBase::sendTextFrame(const char *frame_text) {
|
bool StreamBase::sendTextFrame(const char *frame_text) {
|
||||||
Debug(2, "Sending %dx%d * %d text frame '%s'",
|
Debug(2, "Sending %dx%d * %d text frame '%s'",
|
||||||
|
|
226
src/zm_user.cpp
226
src/zm_user.cpp
|
@ -27,9 +27,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#if HAVE_GNUTLS_OPENSSL_H
|
|
||||||
#include <gnutls/openssl.h>
|
|
||||||
#endif
|
|
||||||
#if HAVE_GNUTLS_GNUTLS_H
|
#if HAVE_GNUTLS_GNUTLS_H
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -112,12 +109,13 @@ User *zmLoadUser(const char *username, const char *password) {
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`"
|
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`"
|
||||||
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", safer_username);
|
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", safer_username);
|
||||||
|
delete safer_username;
|
||||||
|
safer_username = NULL;
|
||||||
|
|
||||||
if ( mysql_query(&dbconn, sql) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
exit(mysql_errno(&dbconn));
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
delete safer_username;
|
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result(&dbconn);
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||||
if ( !result ) {
|
if ( !result ) {
|
||||||
|
@ -125,35 +123,30 @@ User *zmLoadUser(const char *username, const char *password) {
|
||||||
exit(mysql_errno(&dbconn));
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mysql_num_rows(result) != 1 ) {
|
if ( mysql_num_rows(result) == 1 ) {
|
||||||
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||||
|
User *user = new User(dbrow);
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
Warning("Unable to authenticate user %s", username);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
if (
|
||||||
User *user = new User(dbrow);
|
(! password ) // relay type must be none
|
||||||
|
||
|
||||||
|
verifyPassword(username, password, user->getPassword()) ) {
|
||||||
|
Info("Authenticated user '%s'", user->getUsername());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
} // end if 1 result from db
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
|
||||||
if ( !password ) {
|
|
||||||
// relay type must be none
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( verifyPassword(username, password, user->getPassword()) ) {
|
|
||||||
Info("Authenticated user '%s'", user->getUsername());
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
Warning("Unable to authenticate user %s", username);
|
Warning("Unable to authenticate user %s", username);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
} // end User *zmLoadUser(const char *username, const char *password)
|
||||||
|
|
||||||
User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
|
User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) {
|
||||||
std::string key = config.auth_hash_secret;
|
std::string key = config.auth_hash_secret;
|
||||||
std::string remote_addr = "";
|
std::string remote_addr = "";
|
||||||
|
|
||||||
if (use_remote_addr) {
|
if ( use_remote_addr ) {
|
||||||
remote_addr = std::string(getenv( "REMOTE_ADDR" ));
|
remote_addr = std::string(getenv( "REMOTE_ADDR" ));
|
||||||
if ( remote_addr == "" ) {
|
if ( remote_addr == "" ) {
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning( "Can't determine remote address, using null" );
|
||||||
|
@ -162,125 +155,123 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
|
||||||
key += remote_addr;
|
key += remote_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug (1,"Inside zmLoadTokenUser, formed key=%s", key.c_str());
|
Debug(1, "Inside zmLoadTokenUser, formed key=%s", key.c_str());
|
||||||
|
|
||||||
std::pair<std::string, unsigned int> ans = verifyToken(jwt_token_str, key);
|
std::pair<std::string, unsigned int> ans = verifyToken(jwt_token_str, key);
|
||||||
std::string username = ans.first;
|
std::string username = ans.first;
|
||||||
unsigned int iat = ans.second;
|
unsigned int iat = ans.second;
|
||||||
Debug (1,"retrieved user '%s' from token", username.c_str());
|
Debug(1, "retrieved user '%s' from token", username.c_str());
|
||||||
|
|
||||||
if (username != "") {
|
if ( username == "" ) {
|
||||||
char sql[ZM_SQL_MED_BUFSIZ] = "";
|
|
||||||
snprintf(sql, sizeof(sql),
|
|
||||||
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`"
|
|
||||||
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str() );
|
|
||||||
|
|
||||||
if ( mysql_query(&dbconn, sql) ) {
|
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
|
||||||
exit(mysql_errno(&dbconn));
|
|
||||||
}
|
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result(&dbconn);
|
|
||||||
if ( !result ) {
|
|
||||||
Error("Can't use query result: %s", mysql_error(&dbconn));
|
|
||||||
exit(mysql_errno(&dbconn));
|
|
||||||
}
|
|
||||||
int n_users = mysql_num_rows(result);
|
|
||||||
|
|
||||||
if ( n_users != 1 ) {
|
|
||||||
mysql_free_result(result);
|
|
||||||
Error("Unable to authenticate user '%s'", username.c_str());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
|
||||||
User *user = new User(dbrow);
|
|
||||||
unsigned int stored_iat = strtoul(dbrow[10], NULL,0 );
|
|
||||||
|
|
||||||
if (stored_iat > iat ) { // admin revoked tokens
|
|
||||||
mysql_free_result(result);
|
|
||||||
Error("Token was revoked for '%s'", username.c_str());
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug (1,"Got stored expiry time of %u",stored_iat);
|
|
||||||
Debug (1,"Authenticated user '%s' via token", username.c_str());
|
|
||||||
mysql_free_result(result);
|
|
||||||
return user;
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
char sql[ZM_SQL_MED_BUFSIZ] = "";
|
||||||
|
snprintf(sql, sizeof(sql),
|
||||||
|
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0,"
|
||||||
|
" `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`"
|
||||||
|
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str());
|
||||||
|
|
||||||
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||||
|
if ( !result ) {
|
||||||
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n_users = mysql_num_rows(result);
|
||||||
|
if ( n_users != 1 ) {
|
||||||
|
mysql_free_result(result);
|
||||||
|
Error("Unable to authenticate user '%s'", username.c_str());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||||
|
User *user = new User(dbrow);
|
||||||
|
unsigned int stored_iat = strtoul(dbrow[10], NULL, 0);
|
||||||
|
|
||||||
|
if ( stored_iat > iat ) { // admin revoked tokens
|
||||||
|
mysql_free_result(result);
|
||||||
|
Error("Token was revoked for '%s'", username.c_str());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug (1,"Got last token revoke time of: %u",stored_iat);
|
||||||
|
Debug (1,"Authenticated user '%s' via token", username.c_str());
|
||||||
|
mysql_free_result(result);
|
||||||
|
return user;
|
||||||
|
} // User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr)
|
||||||
|
|
||||||
// Function to validate an authentication string
|
// Function to validate an authentication string
|
||||||
User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) ) {
|
if ( !gcry_check_version(GCRYPT_VERSION) ) {
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal( "Unable to initialise libgcrypt" );
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
const char *remote_addr = "";
|
const char *remote_addr = "";
|
||||||
if ( use_remote_addr ) {
|
if ( use_remote_addr ) {
|
||||||
remote_addr = getenv( "REMOTE_ADDR" );
|
remote_addr = getenv("REMOTE_ADDR");
|
||||||
if ( !remote_addr ) {
|
if ( !remote_addr ) {
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning("Can't determine remote address, using null");
|
||||||
remote_addr = "";
|
remote_addr = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
Debug(1, "Attempting to authenticate user from auth string '%s'", auth);
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
snprintf( sql, sizeof(sql), "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds` FROM `Users` WHERE `Enabled` = 1" );
|
snprintf(sql, sizeof(sql),
|
||||||
|
"SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0,"
|
||||||
|
" `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`"
|
||||||
|
" FROM `Users` WHERE `Enabled` = 1");
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result ) {
|
if ( !result ) {
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
return NULL;
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows(result);
|
||||||
|
|
||||||
if ( n_users < 1 ) {
|
if ( n_users < 1 ) {
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
Warning( "Unable to authenticate user" );
|
Warning("Unable to authenticate user");
|
||||||
return( 0 );
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) {
|
time_t now = time(0);
|
||||||
|
unsigned int hours = config.auth_hash_ttl;
|
||||||
|
if ( ! hours ) {
|
||||||
|
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
|
||||||
|
hours = 2;
|
||||||
|
} else {
|
||||||
|
Debug( 1, "AUTH_HASH_TTL is %d", hours );
|
||||||
|
}
|
||||||
|
char auth_key[512] = "";
|
||||||
|
char auth_md5[32+1] = "";
|
||||||
|
size_t md5len = 16;
|
||||||
|
unsigned char md5sum[md5len];
|
||||||
|
|
||||||
|
while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) {
|
||||||
const char *user = dbrow[1];
|
const char *user = dbrow[1];
|
||||||
const char *pass = dbrow[2];
|
const char *pass = dbrow[2];
|
||||||
|
|
||||||
char auth_key[512] = "";
|
|
||||||
char auth_md5[32+1] = "";
|
|
||||||
size_t md5len = 16;
|
|
||||||
unsigned char md5sum[md5len];
|
|
||||||
|
|
||||||
time_t now = time( 0 );
|
|
||||||
unsigned int hours = config.auth_hash_ttl;
|
|
||||||
|
|
||||||
if ( ! hours ) {
|
|
||||||
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
|
|
||||||
hours = 2;
|
|
||||||
} else {
|
|
||||||
Debug( 1, "AUTH_HASH_TTL is %d", hours );
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) {
|
for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) {
|
||||||
struct tm *now_tm = localtime( &now );
|
struct tm *now_tm = localtime(&now);
|
||||||
|
|
||||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
snprintf(auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||||
config.auth_hash_secret,
|
config.auth_hash_secret,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
|
@ -292,47 +283,48 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5len);
|
||||||
#endif
|
#endif
|
||||||
auth_md5[0] = '\0';
|
auth_md5[0] = '\0';
|
||||||
for ( unsigned int j = 0; j < md5len; j++ ) {
|
for ( unsigned int j = 0; j < md5len; j++ ) {
|
||||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
sprintf(&auth_md5[2*j], "%02x", md5sum[j]);
|
||||||
}
|
}
|
||||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth );
|
Debug(1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth);
|
||||||
|
|
||||||
if ( !strcmp( auth, auth_md5 ) ) {
|
if ( !strcmp( auth, auth_md5 ) ) {
|
||||||
// We have a match
|
// We have a match
|
||||||
User *user = new User( dbrow );
|
User *user = new User( dbrow );
|
||||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
return( user );
|
return user;
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "No match for %s", auth );
|
Debug(1, "No match for %s", auth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5
|
||||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
Error("You need to build with gnutls or openssl installed to use hash based authentication");
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5
|
||||||
Debug(1, "No user found for auth_key %s", auth );
|
Debug(1, "No user found for auth_key %s", auth);
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
} // end User *zmLoadAuthUser(const char *auth, bool use_remote_addr)
|
||||||
|
|
||||||
//Function to check Username length
|
//Function to check Username length
|
||||||
bool checkUser ( const char *username) {
|
bool checkUser(const char *username) {
|
||||||
if ( ! username )
|
if ( !username )
|
||||||
return false;
|
return false;
|
||||||
if ( strlen(username) > 32 )
|
if ( strlen(username) > 32 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Function to check password length
|
//Function to check password length
|
||||||
bool checkPass (const char *password) {
|
bool checkPass(const char *password) {
|
||||||
if ( !password )
|
if ( !password )
|
||||||
return false;
|
return false;
|
||||||
if ( strlen(password) > 64 )
|
if ( strlen(password) > 64 )
|
||||||
|
|
|
@ -393,12 +393,12 @@ void timespec_diff(struct timespec *start, struct timespec *end, struct timespec
|
||||||
char *timeval_to_string( struct timeval tv ) {
|
char *timeval_to_string( struct timeval tv ) {
|
||||||
time_t nowtime;
|
time_t nowtime;
|
||||||
struct tm *nowtm;
|
struct tm *nowtm;
|
||||||
static char tmbuf[64], buf[64];
|
static char tmbuf[20], buf[28];
|
||||||
|
|
||||||
nowtime = tv.tv_sec;
|
nowtime = tv.tv_sec;
|
||||||
nowtm = localtime(&nowtime);
|
nowtm = localtime(&nowtime);
|
||||||
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
|
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
|
||||||
snprintf(buf, sizeof buf, "%s.%06ld", tmbuf, tv.tv_usec);
|
snprintf(buf, sizeof buf-1, "%s.%06ld", tmbuf, tv.tv_usec);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -409,7 +409,7 @@ bool VideoStore::open() {
|
||||||
AVDictionary *opts = NULL;
|
AVDictionary *opts = NULL;
|
||||||
// av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
|
// av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
|
||||||
// Shiboleth reports that this may break seeking in mp4 before it downloads
|
// Shiboleth reports that this may break seeking in mp4 before it downloads
|
||||||
//av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
|
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
|
||||||
// av_dict_set(&opts, "movflags",
|
// av_dict_set(&opts, "movflags",
|
||||||
// "frag_keyframe+empty_moov+default_base_moof", 0);
|
// "frag_keyframe+empty_moov+default_base_moof", 0);
|
||||||
if ( (ret = avformat_write_header(oc, &opts)) < 0 ) {
|
if ( (ret = avformat_write_header(oc, &opts)) < 0 ) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ void Zone::Setup(
|
||||||
|
|
||||||
id = p_id;
|
id = p_id;
|
||||||
label = new char[strlen(p_label)+1];
|
label = new char[strlen(p_label)+1];
|
||||||
strcpy( label, p_label );
|
strcpy(label, p_label);
|
||||||
type = p_type;
|
type = p_type;
|
||||||
polygon = p_polygon;
|
polygon = p_polygon;
|
||||||
alarm_rgb = p_alarm_rgb;
|
alarm_rgb = p_alarm_rgb;
|
||||||
|
@ -89,10 +89,10 @@ void Zone::Setup(
|
||||||
overload_count = 0;
|
overload_count = 0;
|
||||||
extend_alarm_count = 0;
|
extend_alarm_count = 0;
|
||||||
|
|
||||||
pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE );
|
pg_image = new Image(monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE);
|
||||||
pg_image->Clear();
|
pg_image->Clear();
|
||||||
pg_image->Fill( 0xff, polygon );
|
pg_image->Fill(0xff, polygon);
|
||||||
pg_image->Outline( 0xff, polygon );
|
pg_image->Outline(0xff, polygon);
|
||||||
|
|
||||||
ranges = new Range[monitor->Height()];
|
ranges = new Range[monitor->Height()];
|
||||||
for ( unsigned int y = 0; y < monitor->Height(); y++ ) {
|
for ( unsigned int y = 0; y < monitor->Height(); y++ ) {
|
||||||
|
@ -113,8 +113,10 @@ void Zone::Setup(
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( config.record_diag_images ) {
|
if ( config.record_diag_images ) {
|
||||||
snprintf(diag_path, sizeof(diag_path), config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
|
snprintf(diag_path, sizeof(diag_path),
|
||||||
if (config.record_diag_images_fifo)
|
config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg",
|
||||||
|
monitor->getStorage()->Path(), id);
|
||||||
|
if ( config.record_diag_images_fifo )
|
||||||
FifoStream::fifo_create_if_missing(diag_path);
|
FifoStream::fifo_create_if_missing(diag_path);
|
||||||
pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
|
pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -350,7 +350,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
if ( result < 0 ) {
|
if ( result < 0 ) {
|
||||||
// Failure, try reconnecting
|
// Failure, try reconnecting
|
||||||
sleep(1);
|
sleep(5);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} // end while ! zm_terminate
|
} // end while ! zm_terminate
|
||||||
|
|
48
src/zms.cpp
48
src/zms.cpp
|
@ -95,7 +95,7 @@ int main(int argc, const char *argv[]) {
|
||||||
Debug(1, "Query: %s", query);
|
Debug(1, "Query: %s", query);
|
||||||
|
|
||||||
char temp_query[1024];
|
char temp_query[1024];
|
||||||
strncpy(temp_query, query, sizeof(temp_query));
|
strncpy(temp_query, query, sizeof(temp_query)-1);
|
||||||
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;
|
||||||
|
@ -106,9 +106,9 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
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 const *value = strtok(NULL, "=");
|
||||||
if ( !value )
|
if ( !value )
|
||||||
value = (char *)"";
|
value = "";
|
||||||
if ( !strcmp(name, "source") ) {
|
if ( !strcmp(name, "source") ) {
|
||||||
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
||||||
if ( !strcmp(value, "fifo") )
|
if ( !strcmp(value, "fifo") )
|
||||||
|
@ -127,10 +127,10 @@ int main(int argc, const char *argv[]) {
|
||||||
} 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, NULL, 10);
|
||||||
source = ZMS_EVENT;
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp(name, "frame") ) {
|
} else if ( !strcmp(name, "frame") ) {
|
||||||
frame_id = strtoull(value, (char **)NULL, 10);
|
frame_id = strtoull(value, NULL, 10);
|
||||||
source = ZMS_EVENT;
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp(name, "scale") ) {
|
} else if ( !strcmp(name, "scale") ) {
|
||||||
scale = atoi(value);
|
scale = atoi(value);
|
||||||
|
@ -159,7 +159,7 @@ int main(int argc, const char *argv[]) {
|
||||||
} else if ( !strcmp(name, "buffer") ) {
|
} else if ( !strcmp(name, "buffer") ) {
|
||||||
playback_buffer = atoi(value);
|
playback_buffer = atoi(value);
|
||||||
} else if ( !strcmp(name, "auth") ) {
|
} else if ( !strcmp(name, "auth") ) {
|
||||||
strncpy( auth, value, sizeof(auth)-1 );
|
strncpy(auth, value, sizeof(auth)-1);
|
||||||
} else if ( !strcmp(name, "token") ) {
|
} else if ( !strcmp(name, "token") ) {
|
||||||
jwt_token_str = value;
|
jwt_token_str = value;
|
||||||
Debug(1, "ZMS: JWT token found: %s", jwt_token_str.c_str());
|
Debug(1, "ZMS: JWT token found: %s", jwt_token_str.c_str());
|
||||||
|
@ -184,7 +184,7 @@ int main(int argc, const char *argv[]) {
|
||||||
logInit(log_id_string);
|
logInit(log_id_string);
|
||||||
|
|
||||||
if ( config.opt_use_auth ) {
|
if ( config.opt_use_auth ) {
|
||||||
User *user = 0;
|
User *user = NULL;
|
||||||
|
|
||||||
if ( jwt_token_str != "" ) {
|
if ( jwt_token_str != "" ) {
|
||||||
// user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips);
|
// user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips);
|
||||||
|
@ -195,19 +195,11 @@ int main(int argc, const char *argv[]) {
|
||||||
} else {
|
} else {
|
||||||
Error("Bad username");
|
Error("Bad username");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
if ( *auth ) {
|
||||||
{
|
user = zmLoadAuthUser(auth, config.auth_hash_ips);
|
||||||
if ( *auth ) {
|
} else if ( username.length() && password.length() ) {
|
||||||
user = zmLoadAuthUser(auth, config.auth_hash_ips);
|
user = zmLoadUser(username.c_str(), password.c_str());
|
||||||
}
|
|
||||||
}
|
|
||||||
// else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( username.length() && password.length() ) {
|
|
||||||
user = zmLoadUser(username.c_str(), password.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( !user ) {
|
if ( !user ) {
|
||||||
|
@ -218,11 +210,15 @@ int main(int argc, const char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ( !ValidateAccess(user, monitor_id) ) {
|
if ( !ValidateAccess(user, monitor_id) ) {
|
||||||
|
delete user;
|
||||||
|
user = NULL;
|
||||||
fputs("HTTP/1.0 403 Forbidden\r\n\r\n", stdout);
|
fputs("HTTP/1.0 403 Forbidden\r\n\r\n", stdout);
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
delete user;
|
||||||
|
user = NULL;
|
||||||
} // end if config.opt_use_auth
|
} // end if config.opt_use_auth
|
||||||
|
|
||||||
hwcaps_detect();
|
hwcaps_detect();
|
||||||
|
@ -237,11 +233,13 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
char date_string[64];
|
char date_string[64];
|
||||||
strftime(date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
strftime(date_string, sizeof(date_string)-1,
|
||||||
|
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||||
|
|
||||||
fprintf(stdout, "Last-Modified: %s\r\n", date_string);
|
fputs("Last-Modified: ", stdout);
|
||||||
|
fputs(date_string, stdout);
|
||||||
fputs(
|
fputs(
|
||||||
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
"\r\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||||
"Cache-Control: no-store, no-cache, must-revalidate\r\n"
|
"Cache-Control: no-store, no-cache, must-revalidate\r\n"
|
||||||
"Cache-Control: post-check=0, pre-check=0\r\n"
|
"Cache-Control: post-check=0, pre-check=0\r\n"
|
||||||
"Pragma: no-cache\r\n",
|
"Pragma: no-cache\r\n",
|
||||||
|
@ -279,7 +277,9 @@ int main(int argc, const char *argv[]) {
|
||||||
stream.setStreamType(MonitorStream::STREAM_MPEG);
|
stream.setStreamType(MonitorStream::STREAM_MPEG);
|
||||||
#else // HAVE_LIBAVCODEC
|
#else // HAVE_LIBAVCODEC
|
||||||
Error("MPEG streaming of '%s' attempted while disabled", query);
|
Error("MPEG streaming of '%s' attempted while disabled", query);
|
||||||
fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n");
|
fprintf(stderr, "MPEG streaming is disabled.\n"
|
||||||
|
"You should configure with the --with-ffmpeg"
|
||||||
|
" option and rebuild to use this functionality.\n");
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -332,5 +332,5 @@ int main(int argc, const char *argv[]) {
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
|
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
51
src/zmu.cpp
51
src/zmu.cpp
|
@ -430,7 +430,9 @@ int main(int argc, char *argv[]) {
|
||||||
User *user = 0;
|
User *user = 0;
|
||||||
|
|
||||||
if ( config.opt_use_auth ) {
|
if ( config.opt_use_auth ) {
|
||||||
if ( strcmp(config.auth_relay, "none") == 0 ) {
|
if ( jwt_token_str != "" ) {
|
||||||
|
user = zmLoadTokenUser(jwt_token_str, false);
|
||||||
|
} else if ( strcmp(config.auth_relay, "none") == 0 ) {
|
||||||
if ( !username ) {
|
if ( !username ) {
|
||||||
Error("Username must be supplied");
|
Error("Username must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
|
@ -444,13 +446,10 @@ int main(int argc, char *argv[]) {
|
||||||
user = zmLoadUser(username);
|
user = zmLoadUser(username);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if ( !(username && password) && !auth && (jwt_token_str=="")) {
|
if ( !(username && password) && !auth ) {
|
||||||
Error("Username and password or auth/token string must be supplied");
|
Error("Username and password or auth/token string must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
if (jwt_token_str != "") {
|
|
||||||
user = zmLoadTokenUser(jwt_token_str, false);
|
|
||||||
}
|
|
||||||
if ( auth ) {
|
if ( auth ) {
|
||||||
user = zmLoadAuthUser(auth, false);
|
user = zmLoadAuthUser(auth, false);
|
||||||
}
|
}
|
||||||
|
@ -477,14 +476,12 @@ int main(int argc, char *argv[]) {
|
||||||
} // end if auth
|
} // end if auth
|
||||||
|
|
||||||
if ( mon_id > 0 ) {
|
if ( mon_id > 0 ) {
|
||||||
//fprintf(stderr,"Monitor %d\n", mon_id);
|
|
||||||
Monitor *monitor = Monitor::Load(mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY);
|
Monitor *monitor = Monitor::Load(mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY);
|
||||||
if ( monitor ) {
|
if ( monitor ) {
|
||||||
//fprintf(stderr,"Monitor %d(%s)\n", monitor->Id(), monitor->Name());
|
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name());
|
printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name());
|
||||||
}
|
}
|
||||||
if ( ! monitor->connect() ) {
|
if ( !monitor->connect() ) {
|
||||||
Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name());
|
Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name());
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
|
@ -496,13 +493,13 @@ int main(int argc, char *argv[]) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
printf("Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"));
|
printf("Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"));
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%d", state);
|
printf("%d", state);
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_TIME ) {
|
if ( function & ZMU_TIME ) {
|
||||||
struct timeval timestamp = monitor->GetTimestamp( image_idx );
|
struct timeval timestamp = monitor->GetTimestamp(image_idx);
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
char timestamp_str[64] = "None";
|
char timestamp_str[64] = "None";
|
||||||
if ( timestamp.tv_sec )
|
if ( timestamp.tv_sec )
|
||||||
|
@ -512,7 +509,7 @@ int main(int argc, char *argv[]) {
|
||||||
else
|
else
|
||||||
printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000);
|
printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000);
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
|
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
|
@ -521,7 +518,7 @@ int main(int argc, char *argv[]) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf("Last read index: %d\n", monitor->GetLastReadIndex());
|
printf("Last read index: %d\n", monitor->GetLastReadIndex());
|
||||||
else {
|
else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%d", monitor->GetLastReadIndex());
|
printf("%d", monitor->GetLastReadIndex());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
|
@ -530,7 +527,7 @@ int main(int argc, char *argv[]) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
printf("Last write index: %d\n", monitor->GetLastWriteIndex());
|
printf("Last write index: %d\n", monitor->GetLastWriteIndex());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%d", monitor->GetLastWriteIndex());
|
printf("%d", monitor->GetLastWriteIndex());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
|
@ -539,16 +536,16 @@ int main(int argc, char *argv[]) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%" PRIu64, monitor->GetLastEventId());
|
printf("%" PRIu64, monitor->GetLastEventId());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_FPS ) {
|
if ( function & ZMU_FPS ) {
|
||||||
if ( verbose )
|
if ( verbose ) {
|
||||||
printf("Current capture rate: %.2f frames per second\n", monitor->GetFPS());
|
printf("Current capture rate: %.2f frames per second\n", monitor->GetFPS());
|
||||||
else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
printf("%.2f", monitor->GetFPS());
|
printf("%.2f", monitor->GetFPS());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
|
@ -574,10 +571,16 @@ int main(int argc, char *argv[]) {
|
||||||
if ( monitor->GetFunction() == Monitor::Function::MONITOR ) {
|
if ( monitor->GetFunction() == Monitor::Function::MONITOR ) {
|
||||||
printf("A Monitor in monitor mode cannot handle alarms. Please use NoDect\n");
|
printf("A Monitor in monitor mode cannot handle alarms. Please use NoDect\n");
|
||||||
} else {
|
} else {
|
||||||
if ( verbose )
|
Monitor::State state = monitor->GetState();
|
||||||
printf("Forcing alarm on\n");
|
|
||||||
|
if ( verbose ) {
|
||||||
|
printf("Forcing alarm on current state: %s, event %" PRIu64 "\n",
|
||||||
|
state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"),
|
||||||
|
monitor->GetLastEventId()
|
||||||
|
);
|
||||||
|
}
|
||||||
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
|
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
|
||||||
while ( (monitor->GetState() != Monitor::ALARM) && !zm_terminate ) {
|
while ( ((state = monitor->GetState()) != Monitor::ALARM) && !zm_terminate ) {
|
||||||
// Wait for monitor to notice.
|
// Wait for monitor to notice.
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
@ -631,7 +634,7 @@ int main(int argc, char *argv[]) {
|
||||||
else
|
else
|
||||||
printf("Current brightness: %d\n", monitor->actionBrightness());
|
printf("Current brightness: %d\n", monitor->actionBrightness());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
if ( brightness >= 0 )
|
if ( brightness >= 0 )
|
||||||
printf("%d", monitor->actionBrightness(brightness));
|
printf("%d", monitor->actionBrightness(brightness));
|
||||||
else
|
else
|
||||||
|
@ -646,7 +649,7 @@ int main(int argc, char *argv[]) {
|
||||||
else
|
else
|
||||||
printf("Current contrast: %d\n", monitor->actionContrast());
|
printf("Current contrast: %d\n", monitor->actionContrast());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
if ( contrast >= 0 )
|
if ( contrast >= 0 )
|
||||||
printf("%d", monitor->actionContrast(contrast));
|
printf("%d", monitor->actionContrast(contrast));
|
||||||
else
|
else
|
||||||
|
@ -661,7 +664,7 @@ int main(int argc, char *argv[]) {
|
||||||
else
|
else
|
||||||
printf("Current hue: %d\n", monitor->actionHue());
|
printf("Current hue: %d\n", monitor->actionHue());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
if ( hue >= 0 )
|
if ( hue >= 0 )
|
||||||
printf("%d", monitor->actionHue(hue));
|
printf("%d", monitor->actionHue(hue));
|
||||||
else
|
else
|
||||||
|
@ -676,7 +679,7 @@ int main(int argc, char *argv[]) {
|
||||||
else
|
else
|
||||||
printf("Current colour: %d\n", monitor->actionColour());
|
printf("Current colour: %d\n", monitor->actionColour());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf("%c", separator);
|
if ( have_output ) fputc(separator, stdout);
|
||||||
if ( colour >= 0 )
|
if ( colour >= 0 )
|
||||||
printf("%d", monitor->actionColour(colour));
|
printf("%d", monitor->actionColour(colour));
|
||||||
else
|
else
|
||||||
|
|
|
@ -80,7 +80,7 @@ fi;
|
||||||
|
|
||||||
if [ "$DISTROS" == "" ]; then
|
if [ "$DISTROS" == "" ]; then
|
||||||
if [ "$RELEASE" != "" ]; then
|
if [ "$RELEASE" != "" ]; then
|
||||||
DISTROS="xenial,bionic,disco,eoan,trusty"
|
DISTROS="xenial,bionic,disco,eoan,focal,trusty"
|
||||||
else
|
else
|
||||||
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
||||||
fi;
|
fi;
|
||||||
|
@ -128,14 +128,14 @@ else
|
||||||
fi;
|
fi;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||||
if [ "$PPA" == "" ]; then
|
if [ "$PPA" == "" ]; then
|
||||||
if [ "$RELEASE" != "" ]; then
|
if [ "$RELEASE" != "" ]; then
|
||||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
||||||
IFS='.' read -r -a VERSION <<< "$RELEASE"
|
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||||
if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then
|
|
||||||
PPA="ppa:iconnor/zoneminder-stable"
|
PPA="ppa:iconnor/zoneminder-stable"
|
||||||
else
|
else
|
||||||
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
|
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
|
||||||
fi;
|
fi;
|
||||||
else
|
else
|
||||||
if [ "$BRANCH" == "" ]; then
|
if [ "$BRANCH" == "" ]; then
|
||||||
|
@ -175,7 +175,7 @@ cd ../
|
||||||
|
|
||||||
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
||||||
|
|
||||||
if [ $VERSION == "" ]; then
|
if [ -z "$VERSION" ]; then
|
||||||
exit 1;
|
exit 1;
|
||||||
fi;
|
fi;
|
||||||
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
||||||
|
@ -316,7 +316,7 @@ EOF
|
||||||
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}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "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/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming/"
|
||||||
else
|
else
|
||||||
if [ "$BRANCH" == "" ]; then
|
if [ "$BRANCH" == "" ]; then
|
||||||
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/"
|
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/"
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# We don't deploy during eslint checks, so exit immediately
|
||||||
|
if [ "${DIST}" == "eslint" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Check to see if this script has access to all the commands it needs
|
# Check to see if this script has access to all the commands it needs
|
||||||
for CMD in sshfs rsync find fusermount mkdir; do
|
for CMD in sshfs rsync find fusermount mkdir; do
|
||||||
type $CMD 2>&1 > /dev/null
|
type $CMD 2>&1 > /dev/null
|
||||||
|
@ -12,53 +17,35 @@ for CMD in sshfs rsync find fusermount mkdir; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# We only want to deploy packages during cron events
|
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||||
# See https://docs.travis-ci.com/user/cron-jobs/
|
if [ "${RELEASE}" != "" ]; then
|
||||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||||
|
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||||
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
targetfolder="debian/release/mini-dinstall/incoming"
|
||||||
targetfolder="debian/master/mini-dinstall/incoming"
|
|
||||||
else
|
else
|
||||||
targetfolder="travis"
|
targetfolder="debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming"
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Target subfolder set to $targetfolder"
|
|
||||||
echo
|
|
||||||
if [ "${USE_SFTP}" == "yes" ]; then
|
|
||||||
echo "Running \$(rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
|
||||||
rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo
|
|
||||||
echo "Files copied successfully."
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!"
|
|
||||||
echo
|
|
||||||
exit 99
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
mkdir -p ./zmrepo
|
|
||||||
ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect 2>&1)"
|
|
||||||
|
|
||||||
if [ -z "$ssh_mntchk" ]; then
|
|
||||||
echo
|
|
||||||
echo "Remote filesystem mounted successfully."
|
|
||||||
echo "Begin transfering files..."
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Don't keep packages older than 5 days
|
|
||||||
find ./zmrepo/$targetfolder/ -maxdepth 1 -type f,l -mtime +5 -delete
|
|
||||||
rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/
|
|
||||||
fusermount -zu zmrepo
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo "ERROR: Attempt to mount zmrepo.zoneminder.com failed!"
|
|
||||||
echo "sshfs gave the following error message:"
|
|
||||||
echo \"$ssh_mntchk\"
|
|
||||||
echo
|
|
||||||
exit 99
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
targetfolder="debian/master/mini-dinstall/incoming"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
targetfolder="travis"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Target subfolder set to $targetfolder"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
||||||
|
rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1
|
||||||
|
if [ "$?" -eq 0 ]; then
|
||||||
|
echo
|
||||||
|
echo "Files copied successfully."
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!"
|
||||||
|
echo "See log output for details."
|
||||||
|
echo
|
||||||
|
exit 99
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# General sanity checks
|
# General sanity checks
|
||||||
checksanity () {
|
checksanity () {
|
||||||
# Check to see if this script has access to all the commands it needs
|
# Check to see if this script has access to all the commands it needs
|
||||||
for CMD in set echo curl git ln mkdir rmdir cat patch; do
|
for CMD in set echo curl git ln mkdir rmdir cat patch sed; do
|
||||||
type $CMD 2>&1 > /dev/null
|
type $CMD 2>&1 > /dev/null
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
@ -30,7 +30,7 @@ checksanity () {
|
||||||
ARCH="x86_64"
|
ARCH="x86_64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" ]]; then
|
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" && "${ARCH}" != "aarch64" ]]; then
|
||||||
echo
|
echo
|
||||||
echo "ERROR: Unsupported architecture specified \"${ARCH}\"."
|
echo "ERROR: Unsupported architecture specified \"${ARCH}\"."
|
||||||
echo
|
echo
|
||||||
|
@ -150,7 +150,7 @@ install_deb () {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install and test the zoneminder package (only) for Ubuntu Trusty
|
# Install and test the zoneminder package (only) for Ubuntu Xenial
|
||||||
pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb"
|
pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb"
|
||||||
|
|
||||||
if [ -e $pkgname ]; then
|
if [ -e $pkgname ]; then
|
||||||
|
@ -275,6 +275,8 @@ checkdeploytarget () {
|
||||||
echo "*** TRACEROUTE ***"
|
echo "*** TRACEROUTE ***"
|
||||||
echo
|
echo
|
||||||
traceroute -w 2 -m 15 ${DEPLOYTARGET}
|
traceroute -w 2 -m 15 ${DEPLOYTARGET}
|
||||||
|
|
||||||
|
exit 97
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,43 +293,43 @@ if [ "${TRAVIS}" == "true" ]; then
|
||||||
fi
|
fi
|
||||||
checksanity
|
checksanity
|
||||||
|
|
||||||
# We don't want to build packages for all supported distros after every commit
|
|
||||||
# Only build all packages when executed via cron
|
|
||||||
# See https://docs.travis-ci.com/user/cron-jobs/
|
|
||||||
|
|
||||||
# Steps common to Redhat distros
|
# Steps common to Redhat distros
|
||||||
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
||||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
commonprep
|
||||||
commonprep
|
echo "Begin Redhat build..."
|
||||||
echo "Begin Redhat build..."
|
|
||||||
|
|
||||||
setrpmpkgname
|
# Newer Redhat distros use dnf package manager rather than yum
|
||||||
|
if [ "${DIST}" -gt "7" ]; then
|
||||||
|
sed -i 's\yum\dnf\' utils/packpack/redhat_package.mk
|
||||||
|
fi
|
||||||
|
|
||||||
ln -sfT distros/redhat rpm
|
setrpmpkgname
|
||||||
|
|
||||||
# The rpm specfile requires the Crud submodule folder to be empty
|
ln -sfT distros/redhat rpm
|
||||||
rm -rf web/api/app/Plugin/Crud
|
|
||||||
mkdir web/api/app/Plugin/Crud
|
|
||||||
|
|
||||||
reporpm="rpmfusion-free-release"
|
# The rpm specfile requires the Crud submodule folder to be empty
|
||||||
dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm"
|
rm -rf web/api/app/Plugin/Crud
|
||||||
|
mkdir web/api/app/Plugin/Crud
|
||||||
|
|
||||||
# Give our downloaded repo rpm a common name so redhat_package.mk can find it
|
reporpm="rpmfusion-free-release"
|
||||||
if [ -n "$dlurl" ] && [ $? -eq 0 ]; then
|
dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm"
|
||||||
echo "Retrieving ${reporpm} repo rpm..."
|
|
||||||
curl $dlurl > build/external-repo.noarch.rpm
|
|
||||||
else
|
|
||||||
echo "ERROR: Failed to retrieve ${reporpm} repo rpm..."
|
|
||||||
echo "Download url was: $dlurl"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
setrpmchangelog
|
# Give our downloaded repo rpm a common name so redhat_package.mk can find it
|
||||||
|
if [ -n "$dlurl" ] && [ $? -eq 0 ]; then
|
||||||
|
echo "Retrieving ${reporpm} repo rpm..."
|
||||||
|
curl $dlurl > build/external-repo.noarch.rpm
|
||||||
|
else
|
||||||
|
echo "ERROR: Failed to retrieve ${reporpm} repo rpm..."
|
||||||
|
echo "Download url was: $dlurl"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Starting packpack..."
|
setrpmchangelog
|
||||||
execpackpack
|
|
||||||
fi;
|
echo "Starting packpack..."
|
||||||
# Steps common to Debian based distros
|
execpackpack
|
||||||
|
|
||||||
|
# Steps common to Debian based distros
|
||||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||||
commonprep
|
commonprep
|
||||||
echo "Begin ${OS} ${DIST} build..."
|
echo "Begin ${OS} ${DIST} build..."
|
||||||
|
@ -348,14 +350,27 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
execpackpack
|
execpackpack
|
||||||
|
|
||||||
# We were not triggered via cron so just build and test trusty
|
# Try to install and run the newly built zoneminder package
|
||||||
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ]; then
|
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then
|
||||||
# If we are running inside Travis then attempt to install the deb we just built
|
echo "Begin Deb package installation..."
|
||||||
if [ "${TRAVIS}" == "true" ]; then
|
|
||||||
install_deb
|
install_deb
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Steps common to eslint checks
|
||||||
|
elif [ "${OS}" == "eslint" ] || [ "${DIST}" == "eslint" ]; then
|
||||||
|
|
||||||
|
# Check we've got npm installed
|
||||||
|
type npm 2>&1 > /dev/null
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo
|
||||||
|
echo "ERROR: The script cannot find the required command \"npm\"."
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5
|
||||||
|
echo "Begin eslint checks..."
|
||||||
|
eslint --ext .php,.js .
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
|
|
|
@ -46,18 +46,18 @@ if ( 0 ) {
|
||||||
SOL_SOCKET, // socket level
|
SOL_SOCKET, // socket level
|
||||||
SO_SNDTIMEO, // timeout option
|
SO_SNDTIMEO, // timeout option
|
||||||
array(
|
array(
|
||||||
"sec"=>0, // Timeout in seconds
|
'sec'=>0, // Timeout in seconds
|
||||||
"usec"=>500 // I assume timeout in microseconds
|
'usec'=>500 // I assume timeout in microseconds
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$new_stream = null;
|
$new_stream = null;
|
||||||
Info("Testing connection to " . $url_bits['host'].':'.$port);
|
Info('Testing connection to '.$url_bits['host'].':'.$port);
|
||||||
if ( socket_connect( $socket, $url_bits['host'], $port ) ) {
|
if ( socket_connect( $socket, $url_bits['host'], $port ) ) {
|
||||||
$new_stream = $url_bits; // make a copy
|
$new_stream = $url_bits; // make a copy
|
||||||
$new_stream['port'] = $port;
|
$new_stream['port'] = $port;
|
||||||
} else {
|
} else {
|
||||||
socket_close($socket);
|
socket_close($socket);
|
||||||
ZM\Info("No connection to ".$url_bits['host'] . " on port $port");
|
ZM\Info('No connection to '.$url_bits['host'].' on port '.$port);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( $new_stream ) {
|
if ( $new_stream ) {
|
||||||
|
@ -92,10 +92,10 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
|
||||||
}
|
}
|
||||||
foreach ( $available_streams as &$stream ) {
|
foreach ( $available_streams as &$stream ) {
|
||||||
# check for existence in db.
|
# check for existence in db.
|
||||||
$stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') );
|
$stream['url'] = unparse_url($stream, array('path'=>'/','query'=>'action=stream'));
|
||||||
$monitors = ZM\Monitor::find( array('Path'=>$stream['url']) );
|
$monitors = ZM\Monitor::find(array('Path'=>$stream['url']));
|
||||||
if ( count($monitors) ) {
|
if ( count($monitors) ) {
|
||||||
ZM\Info("Found monitors matching " . $stream['url'] );
|
ZM\Info('Found monitors matching ' . $stream['url'] );
|
||||||
$stream['Monitor'] = $monitors[0];
|
$stream['Monitor'] = $monitors[0];
|
||||||
if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) {
|
if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) {
|
||||||
$stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n";
|
$stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n";
|
||||||
|
@ -106,11 +106,11 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
|
||||||
} else {
|
} else {
|
||||||
$stream['Monitor'] = clone $defaultMonitor;
|
$stream['Monitor'] = clone $defaultMonitor;
|
||||||
if ( isset($stream['Width']) ) {
|
if ( isset($stream['Width']) ) {
|
||||||
$stream['Monitor']->Width( $stream['Width'] );
|
$stream['Monitor']->Width($stream['Width']);
|
||||||
$stream['Monitor']->Height( $stream['Height'] );
|
$stream['Monitor']->Height($stream['Height']);
|
||||||
}
|
}
|
||||||
if ( isset($stream['Name']) ) {
|
if ( isset($stream['Name']) ) {
|
||||||
$stream['Monitor']->Name( $stream['Name'] );
|
$stream['Monitor']->Name($stream['Name']);
|
||||||
}
|
}
|
||||||
} // Monitor found or not
|
} // Monitor found or not
|
||||||
} // end foreach Stream
|
} // end foreach Stream
|
||||||
|
@ -121,16 +121,16 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
|
||||||
return $available_streams;
|
return $available_streams;
|
||||||
} // end function probe
|
} // end function probe
|
||||||
|
|
||||||
if ( canEdit( 'Monitors' ) ) {
|
if ( canEdit('Monitors') ) {
|
||||||
switch ( $_REQUEST['action'] ) {
|
switch ( $_REQUEST['action'] ) {
|
||||||
case 'probe' :
|
case 'probe' :
|
||||||
{
|
{
|
||||||
$available_streams = array();
|
$available_streams = array();
|
||||||
$url_bits = null;
|
$url_bits = null;
|
||||||
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url'] ) ) {
|
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url']) ) {
|
||||||
$url_bits = array( 'host'=>$_REQUEST['url'] );
|
$url_bits = array('host'=>$_REQUEST['url']);
|
||||||
} else {
|
} else {
|
||||||
$url_bits = parse_url( $_REQUEST['url'] );
|
$url_bits = parse_url($_REQUEST['url']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( 0 ) {
|
if ( 0 ) {
|
||||||
|
@ -147,13 +147,13 @@ if ( 0 ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $url_bits ) {
|
if ( ! $url_bits ) {
|
||||||
ajaxError("The given URL was too malformed to parse.");
|
ajaxError('The given URL was too malformed to parse.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$available_streams = probe( $url_bits );
|
$available_streams = probe($url_bits);
|
||||||
|
|
||||||
ajaxResponse( array('Streams'=>$available_streams) );
|
ajaxResponse(array('Streams'=>$available_streams));
|
||||||
return;
|
return;
|
||||||
} // end case url_probe
|
} // end case url_probe
|
||||||
case 'import':
|
case 'import':
|
||||||
|
@ -161,16 +161,16 @@ if ( 0 ) {
|
||||||
|
|
||||||
$file = $_FILES['import_file'];
|
$file = $_FILES['import_file'];
|
||||||
|
|
||||||
if ($file["error"] > 0) {
|
if ( $file['error'] > 0 ) {
|
||||||
ajaxError($file["error"]);
|
ajaxError($file['error']);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
$filename = $file["name"];
|
$filename = $file['name'];
|
||||||
|
|
||||||
$available_streams = array();
|
$available_streams = array();
|
||||||
$row = 1;
|
$row = 1;
|
||||||
if (($handle = fopen($file['tmp_name'], 'r')) !== FALSE) {
|
if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) {
|
||||||
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
|
while ( ($data = fgetcsv($handle, 1000, ',')) !== FALSE ) {
|
||||||
$name = $data[0];
|
$name = $data[0];
|
||||||
$url = $data[1];
|
$url = $data[1];
|
||||||
$group = $data[2];
|
$group = $data[2];
|
||||||
|
@ -178,16 +178,16 @@ if ( 0 ) {
|
||||||
|
|
||||||
$url_bits = null;
|
$url_bits = null;
|
||||||
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $url) ) {
|
if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $url) ) {
|
||||||
$url_bits = array( 'host'=>$url, 'scheme'=>'http' );
|
$url_bits = array('host'=>$url, 'scheme'=>'http');
|
||||||
} else {
|
} else {
|
||||||
$url_bits = parse_url( $url );
|
$url_bits = parse_url($url);
|
||||||
}
|
}
|
||||||
if ( ! $url_bits ) {
|
if ( ! $url_bits ) {
|
||||||
ZM\Info("Bad url, skipping line $name $url $group");
|
ZM\Info("Bad url, skipping line $name $url $group");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$available_streams += probe( $url_bits );
|
$available_streams += probe($url_bits);
|
||||||
|
|
||||||
//$url_bits['url'] = unparse_url( $url_bits );
|
//$url_bits['url'] = unparse_url( $url_bits );
|
||||||
//$url_bits['Monitor'] = $defaultMonitor;
|
//$url_bits['Monitor'] = $defaultMonitor;
|
||||||
|
@ -197,23 +197,19 @@ if ( 0 ) {
|
||||||
|
|
||||||
} // end while rows
|
} // end while rows
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
ajaxResponse( array('Streams'=>$available_streams) );
|
ajaxResponse(array('Streams'=>$available_streams));
|
||||||
} else {
|
} else {
|
||||||
ajaxError("Uploaded file does not exist");
|
ajaxError('Uploaded file does not exist');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} // end case import
|
} // end case import
|
||||||
default:
|
default:
|
||||||
{
|
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||||
ZM\Warning("unknown action " . $_REQUEST['action'] );
|
} // end switch action
|
||||||
} // end ddcase default
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ZM\Warning("Cannot edit monitors" );
|
ZM\Warning('Cannot edit monitors');
|
||||||
}
|
}
|
||||||
|
|
||||||
ajaxError( 'Unrecognised action or insufficient permissions' );
|
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,35 +1,33 @@
|
||||||
<?php
|
<?php
|
||||||
if ( canEdit('Monitors') ) {
|
if ( canEdit('Monitors') ) {
|
||||||
switch ( $_REQUEST['action'] ) {
|
switch ( $_REQUEST['action'] ) {
|
||||||
case 'sort' :
|
case 'sort' :
|
||||||
{
|
{
|
||||||
$monitor_ids = $_POST['monitor_ids'];
|
$monitor_ids = $_POST['monitor_ids'];
|
||||||
# Two concurrent sorts could generate odd sortings... so lock the table.
|
# Two concurrent sorts could generate odd sortings... so lock the table.
|
||||||
global $dbConn;
|
global $dbConn;
|
||||||
$dbConn->beginTransaction();
|
$dbConn->beginTransaction();
|
||||||
$dbConn->exec('LOCK TABLES Monitors WRITE');
|
$dbConn->exec('LOCK TABLES Monitors WRITE');
|
||||||
for ( $i = 0; $i < count($monitor_ids); $i += 1 ) {
|
for ( $i = 0; $i < count($monitor_ids); $i += 1 ) {
|
||||||
$monitor_id = $monitor_ids[$i];
|
$monitor_id = $monitor_ids[$i];
|
||||||
$monitor_id = preg_replace( '/^monitor_id-/', '', $monitor_id );
|
$monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id);
|
||||||
if ( ( ! $monitor_id ) or ! ( is_integer( $monitor_id ) or ctype_digit( $monitor_id ) ) ) {
|
if ( ( !$monitor_id ) or ! ( is_integer($monitor_id) or ctype_digit($monitor_id) ) ) {
|
||||||
Warning("Got $monitor_id from " . $monitor_ids[$i]);
|
Warning('Got '.$monitor_id.' from '.$monitor_ids[$i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
|
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
|
||||||
} // end for each monitor_id
|
} // end for each monitor_id
|
||||||
$dbConn->commit();
|
$dbConn->commit();
|
||||||
$dbConn->exec('UNLOCK TABLES');
|
$dbConn->exec('UNLOCK TABLES');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} // end case sort
|
} // end case sort
|
||||||
default:
|
default:
|
||||||
{
|
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||||
ZM\Warning('unknown action ' . $_REQUEST['action']);
|
}
|
||||||
} // end ddcase default
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ZM\Warning('Cannot edit monitors');
|
ZM\Warning('Cannot edit monitors');
|
||||||
}
|
}
|
||||||
|
|
||||||
ajaxError('Unrecognised action or insufficient permissions');
|
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
error_reporting(E_ERROR);
|
ini_set('display_errors','0');
|
||||||
|
|
||||||
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
||||||
ajaxError('No event id(s) supplied');
|
ajaxError('No event id(s) supplied');
|
||||||
|
@ -155,5 +155,5 @@ if ( canEdit('Events') ) {
|
||||||
} // end switch action
|
} // end switch action
|
||||||
} // end if canEdit('Events')
|
} // end if canEdit('Events')
|
||||||
|
|
||||||
ajaxError('Unrecognised action or insufficient permissions');
|
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user '.$user['Username']);
|
||||||
?>
|
?>
|
||||||
|
|
154
web/ajax/log.php
154
web/ajax/log.php
|
@ -1,14 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
|
||||||
# Moved up here because it is used in several spots.
|
# Moved up here because it is used in several spots.
|
||||||
# These are the valid columns that you can filter on.
|
# These are the valid columns that you can filter on.
|
||||||
$filterFields = array( 'Component', 'ServerId', 'Pid', 'Level', 'File', 'Line' );
|
$filterFields = array('Component', 'ServerId', 'Pid', 'Level', 'File', 'Line');
|
||||||
|
|
||||||
function buildLogQuery($action) {
|
function buildLogQuery($action) {
|
||||||
global $filterFields;
|
global $filterFields;
|
||||||
|
|
||||||
$minTime = isset($_REQUEST['minTime'])?$_REQUEST['minTime']:NULL;
|
$minTime = isset($_REQUEST['minTime']) ? $_REQUEST['minTime'] : NULL;
|
||||||
$maxTime = isset($_REQUEST['maxTime'])?$_REQUEST['maxTime']:NULL;
|
$maxTime = isset($_REQUEST['maxTime']) ? $_REQUEST['maxTime'] : NULL;
|
||||||
|
|
||||||
$limit = 100;
|
$limit = 100;
|
||||||
if ( isset($_REQUEST['limit']) ) {
|
if ( isset($_REQUEST['limit']) ) {
|
||||||
|
@ -41,11 +42,11 @@ function buildLogQuery($action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $filter as $field=>$value ) {
|
foreach ( $filter as $field=>$value ) {
|
||||||
if ( ! in_array($field, $filterFields) ) {
|
if ( !in_array($field, $filterFields) ) {
|
||||||
ZM\Error("'$field' is not in valid filter fields " . print_r($filterField,true));
|
ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( $field == 'Level' ){
|
if ( $field == 'Level' ) {
|
||||||
$where[] = $field.' <= ?';
|
$where[] = $field.' <= ?';
|
||||||
$values[] = $value;
|
$values[] = $value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,7 +59,7 @@ function buildLogQuery($action) {
|
||||||
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
|
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
|
||||||
|
|
||||||
return array('sql'=>$sql, 'values'=>$values);
|
return array('sql'=>$sql, 'values'=>$values);
|
||||||
}
|
} # function buildLogQuery($action)
|
||||||
|
|
||||||
switch ( $_REQUEST['task'] ) {
|
switch ( $_REQUEST['task'] ) {
|
||||||
case 'create' :
|
case 'create' :
|
||||||
|
@ -69,17 +70,21 @@ switch ( $_REQUEST['task'] ) {
|
||||||
|
|
||||||
$string = $_POST['message'];
|
$string = $_POST['message'];
|
||||||
|
|
||||||
$file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : '';
|
$file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : '';
|
||||||
if ( !empty( $_POST['line'] ) )
|
if ( !empty($_POST['line']) ) {
|
||||||
$line = validInt($_POST['line']);
|
$line = validInt($_POST['line']);
|
||||||
else
|
} else {
|
||||||
$line = NULL;
|
$line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
$levels = array_flip(ZM\Logger::$codes);
|
$levels = array_flip(ZM\Logger::$codes);
|
||||||
if ( !isset($levels[$_POST['level']]) )
|
if ( !isset($levels[$_POST['level']]) ) {
|
||||||
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
||||||
|
}
|
||||||
$level = $levels[$_POST['level']];
|
$level = $levels[$_POST['level']];
|
||||||
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
||||||
|
} else {
|
||||||
|
ZM\Error('Invalid log create: '.print_r($_POST, true));
|
||||||
}
|
}
|
||||||
ajaxResponse();
|
ajaxResponse();
|
||||||
break;
|
break;
|
||||||
|
@ -91,12 +96,7 @@ switch ( $_REQUEST['task'] ) {
|
||||||
|
|
||||||
$query = buildLogQuery('DELETE');
|
$query = buildLogQuery('DELETE');
|
||||||
$result = dbQuery($query['sql'], $query['values']);
|
$result = dbQuery($query['sql'], $query['values']);
|
||||||
ajaxResponse( array(
|
ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount()));
|
||||||
'result'=>'Ok',
|
|
||||||
'deleted'=>$result->rowCount(),
|
|
||||||
) );
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
case 'query' :
|
case 'query' :
|
||||||
{
|
{
|
||||||
|
@ -105,10 +105,12 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
|
$total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total');
|
||||||
$query = buildLogQuery('SELECT *');
|
$query = buildLogQuery('SELECT *');
|
||||||
|
|
||||||
$servers = ZM\Server::find();
|
global $Servers;
|
||||||
|
if ( !$Servers )
|
||||||
|
$Servers = ZM\Server::find();
|
||||||
$servers_by_Id = array();
|
$servers_by_Id = array();
|
||||||
# There is probably a better way to do this.
|
# There is probably a better way to do this.
|
||||||
foreach ( $servers as $server ) {
|
foreach ( $Servers as $server ) {
|
||||||
$servers_by_Id[$server->Id()] = $server;
|
$servers_by_Id[$server->Id()] = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +120,9 @@ switch ( $_REQUEST['task'] ) {
|
||||||
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) {
|
foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) {
|
||||||
|
|
||||||
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
|
$log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey']));
|
||||||
#Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']);
|
|
||||||
#$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']);
|
$log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']);
|
||||||
foreach( $filterFields as $field ) {
|
foreach ( $filterFields as $field ) {
|
||||||
if ( !isset($options[$field]) )
|
if ( !isset($options[$field]) )
|
||||||
$options[$field] = array();
|
$options[$field] = array();
|
||||||
$value = $log[$field];
|
$value = $log[$field];
|
||||||
|
@ -139,17 +139,21 @@ switch ( $_REQUEST['task'] ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$logs[] = $log;
|
$logs[] = $log;
|
||||||
|
} # end foreach log db row
|
||||||
|
|
||||||
|
foreach ( $options as $field => $values ) {
|
||||||
|
asort($options[$field]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$available = count($logs);
|
$available = count($logs);
|
||||||
ajaxResponse( array(
|
ajaxResponse(array(
|
||||||
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG),
|
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG),
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'available' => isset($available) ? $available : $total,
|
'available' => isset($available) ? $available : $total,
|
||||||
'logs' => $logs,
|
'logs' => $logs,
|
||||||
'state' => logState(),
|
'state' => logState(),
|
||||||
'options' => $options,
|
'options' => $options,
|
||||||
) );
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'export' :
|
case 'export' :
|
||||||
|
@ -157,9 +161,9 @@ switch ( $_REQUEST['task'] ) {
|
||||||
if ( !canView('System') )
|
if ( !canView('System') )
|
||||||
ajaxError('Insufficient permissions to export logs');
|
ajaxError('Insufficient permissions to export logs');
|
||||||
|
|
||||||
$minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL;
|
$minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL;
|
||||||
$maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL;
|
$maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL;
|
||||||
if ( !is_null($minTime) && !is_null($maxTime) && $minTime > $maxTime ) {
|
if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) {
|
||||||
$tempTime = $minTime;
|
$tempTime = $minTime;
|
||||||
$minTime = $maxTime;
|
$minTime = $maxTime;
|
||||||
$maxTime = $tempTime;
|
$maxTime = $tempTime;
|
||||||
|
@ -168,15 +172,17 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$filter = isset($_POST['filter'])?$_POST['filter']:array();
|
$filter = isset($_POST['filter'])?$_POST['filter']:array();
|
||||||
$sortField = 'TimeKey';
|
$sortField = 'TimeKey';
|
||||||
if ( isset($_POST['sortField']) ) {
|
if ( isset($_POST['sortField']) ) {
|
||||||
if ( ! in_array( $_POST['sortField'], $filterFields ) and ( $_POST['sortField'] != 'TimeKey' ) ) {
|
if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) {
|
||||||
ZM\Error("Invalid sort field " . $_POST['sortField'] );
|
ZM\Error('Invalid sort field '.$_POST['sortField']);
|
||||||
} else {
|
} else {
|
||||||
$sortField = $_POST['sortField'];
|
$sortField = $_POST['sortField'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc';
|
$sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc';
|
||||||
|
|
||||||
$servers = ZM\Server::find();
|
global $Servers;
|
||||||
|
if ( !$Servers )
|
||||||
|
$Servers = ZM\Server::find();
|
||||||
$servers_by_Id = array();
|
$servers_by_Id = array();
|
||||||
# There is probably a better way to do this.
|
# There is probably a better way to do this.
|
||||||
foreach ( $servers as $server ) {
|
foreach ( $servers as $server ) {
|
||||||
|
@ -187,11 +193,9 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$where = array();
|
$where = array();
|
||||||
$values = array();
|
$values = array();
|
||||||
if ( $minTime ) {
|
if ( $minTime ) {
|
||||||
ZM\Logger::Debug("MinTime: $minTime");
|
|
||||||
if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) {
|
if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) {
|
||||||
# This handles sub second precision
|
# This handles sub second precision
|
||||||
$minTime = strtotime($matches[1]).$matches[2];
|
$minTime = strtotime($matches[1]).$matches[2];
|
||||||
ZM\Logger::Debug("MinTime: $minTime");
|
|
||||||
} else {
|
} else {
|
||||||
$minTime = strtotime($minTime);
|
$minTime = strtotime($minTime);
|
||||||
}
|
}
|
||||||
|
@ -219,11 +223,11 @@ switch ( $_REQUEST['task'] ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( count($where) )
|
if ( count($where) )
|
||||||
$sql.= ' WHERE '.join( ' AND ', $where );
|
$sql.= ' WHERE '.join(' AND ', $where);
|
||||||
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder;
|
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder;
|
||||||
//$sql .= " limit ".dbEscape($limit);
|
//$sql .= " limit ".dbEscape($limit);
|
||||||
$format = isset($_POST['format'])?$_POST['format']:'text';
|
$format = isset($_POST['format']) ? $_POST['format'] : 'text';
|
||||||
switch( $format ) {
|
switch ( $format ) {
|
||||||
case 'text' :
|
case 'text' :
|
||||||
$exportExt = 'txt';
|
$exportExt = 'txt';
|
||||||
break;
|
break;
|
||||||
|
@ -239,43 +243,40 @@ switch ( $_REQUEST['task'] ) {
|
||||||
default :
|
default :
|
||||||
ZM\Fatal("Unrecognised log export format '$format'");
|
ZM\Fatal("Unrecognised log export format '$format'");
|
||||||
}
|
}
|
||||||
$exportKey = substr(md5(rand()),0,8);
|
$exportKey = substr(md5(rand()), 0, 8);
|
||||||
$exportFile = "zm-log.$exportExt";
|
$exportFile = 'zm-log.'.$exportExt;
|
||||||
if ( ! file_exists(ZM_DIR_EXPORTS) ) {
|
if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) {
|
||||||
ZM\Logger::Debug('Creating ' . ZM_DIR_EXPORTS);
|
ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\'');
|
||||||
if ( ! mkdir(ZM_DIR_EXPORTS) ) {
|
|
||||||
ZM\Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt";
|
$exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt;
|
||||||
ZM\Logger::Debug("Exporting to $exportPath");
|
ZM\Logger::Debug("Exporting to $exportPath");
|
||||||
if ( !($exportFP = fopen($exportPath, 'w')) )
|
if ( !($exportFP = fopen($exportPath, 'w')) )
|
||||||
ZM\Fatal("Unable to open log export file $exportPath");
|
ZM\Fatal("Unable to open log export file $exportPath");
|
||||||
$logs = array();
|
$logs = array();
|
||||||
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() : '';
|
||||||
$logs[] = $log;
|
$logs[] = $log;
|
||||||
}
|
}
|
||||||
ZM\Logger::Debug(count($logs)." lines being exported by $sql " . implode(',',$values));
|
ZM\Logger::Debug(count($logs).' lines being exported by '.$sql.implode(',', $values));
|
||||||
|
|
||||||
switch( $format ) {
|
switch( $format ) {
|
||||||
case 'text' :
|
case 'text' :
|
||||||
{
|
{
|
||||||
foreach ( $logs as $log ) {
|
foreach ( $logs as $log ) {
|
||||||
if ( $log['Line'] )
|
if ( $log['Line'] )
|
||||||
fprintf( $exportFP, "%s %s[%d].%s-%s/%d [%s]\n",
|
fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n",
|
||||||
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message'] );
|
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']);
|
||||||
else
|
else
|
||||||
fprintf( $exportFP, "%s %s[%d].%s-%s [%s]\n",
|
fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n",
|
||||||
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message'] );
|
$log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'tsv' :
|
case 'tsv' :
|
||||||
{
|
{
|
||||||
# This line doesn't need fprintf, it could use fwrite
|
# This line doesn't need fprintf, it could use fwrite
|
||||||
fprintf( $exportFP, join( "\t",
|
fprintf($exportFP, join("\t",
|
||||||
translate('DateTime'),
|
translate('DateTime'),
|
||||||
translate('Component'),
|
translate('Component'),
|
||||||
translate('Server'),
|
translate('Server'),
|
||||||
|
@ -284,17 +285,17 @@ switch ( $_REQUEST['task'] ) {
|
||||||
translate('Message'),
|
translate('Message'),
|
||||||
translate('File'),
|
translate('File'),
|
||||||
translate('Line')
|
translate('Line')
|
||||||
)."\n" );
|
)."\n");
|
||||||
foreach ( $logs as $log ) {
|
foreach ( $logs as $log ) {
|
||||||
fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
|
fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n",
|
||||||
|
$log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'html' :
|
case 'html' :
|
||||||
{
|
{
|
||||||
fwrite( $exportFP,
|
fwrite($exportFP,
|
||||||
'
|
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>'.translate('ZoneMinderLog').'</title>
|
<title>'.translate('ZoneMinderLog').'</title>
|
||||||
|
@ -333,34 +334,36 @@ switch ( $_REQUEST['task'] ) {
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h3>'.translate('ZoneMinderLog').'</h3>
|
<h3>'.translate('ZoneMinderLog').'</h3>
|
||||||
<p>'.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'</p>
|
<p>'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'</p>
|
||||||
<p>'.count($logs).' '.translate('Logs').'</p>
|
<p>'.count($logs).' '.translate('Logs').'</p>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><th>'.translate('DateTime').'</th><th>'.translate('Component').'</th><th>'.translate('Server').'</th><th>'.translate('Pid').'</th><th>'.translate('Level').'</th><th>'.translate('Message').'</th><th>'.translate('File').'</th><th>'.translate('Line').'</th></tr>
|
<tr><th>'.translate('DateTime').'</th><th>'.translate('Component').'</th><th>'.translate('Server').'</th><th>'.translate('Pid').'</th><th>'.translate('Level').'</th><th>'.translate('Message').'</th><th>'.translate('File').'</th><th>'.translate('Line').'</th></tr>
|
||||||
' );
|
');
|
||||||
foreach ( $logs as $log ) {
|
foreach ( $logs as $log ) {
|
||||||
$classLevel = $log['Level'];
|
$classLevel = $log['Level'];
|
||||||
if ( $classLevel < ZM\Logger::FATAL )
|
if ( $classLevel < ZM\Logger::FATAL ) {
|
||||||
$classLevel = ZM\Logger::FATAL;
|
$classLevel = ZM\Logger::FATAL;
|
||||||
elseif ( $classLevel > ZM\Logger::DEBUG )
|
} else if ( $classLevel > ZM\Logger::DEBUG ) {
|
||||||
$classLevel = ZM\Logger::DEBUG;
|
$classLevel = ZM\Logger::DEBUG;
|
||||||
|
}
|
||||||
$logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]);
|
$logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]);
|
||||||
fprintf( $exportFP, " <tr class=\"%s\"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
|
fprintf($exportFP, ' <tr class="%s"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>
|
||||||
|
', $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']);
|
||||||
}
|
}
|
||||||
fwrite( $exportFP,
|
fwrite($exportFP,
|
||||||
' </tbody>
|
' </tbody>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>' );
|
</html>');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'xml' :
|
case 'xml' :
|
||||||
{
|
{
|
||||||
fwrite( $exportFP,
|
fwrite($exportFP,
|
||||||
'<?xml version="1.0" encoding="utf-8"?>
|
'<?xml version="1.0" encoding="utf-8"?>
|
||||||
<logexport title="'.translate('ZoneMinderLog').'" date="'.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'">
|
<logexport title="'.translate('ZoneMinderLog').'" date="'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'">
|
||||||
<selector>'.$_POST['selector'].'</selector>' );
|
<selector>'.$_POST['selector'].'</selector>');
|
||||||
foreach ( $filter as $field=>$value )
|
foreach ( $filter as $field=>$value )
|
||||||
if ( $value != '' )
|
if ( $value != '' )
|
||||||
fwrite( $exportFP,
|
fwrite( $exportFP,
|
||||||
|
@ -375,7 +378,7 @@ switch ( $_REQUEST['task'] ) {
|
||||||
' );
|
' );
|
||||||
foreach ( $logs as $log ) {
|
foreach ( $logs as $log ) {
|
||||||
fprintf( $exportFP,
|
fprintf( $exportFP,
|
||||||
" <log>
|
' <log>
|
||||||
<datetime>%s</datetime>
|
<datetime>%s</datetime>
|
||||||
<component>%s</component>
|
<component>%s</component>
|
||||||
<server>%s</server>
|
<server>%s</server>
|
||||||
|
@ -384,7 +387,8 @@ switch ( $_REQUEST['task'] ) {
|
||||||
<message><![CDATA[%s]]></message>
|
<message><![CDATA[%s]]></message>
|
||||||
<file>%s</file>
|
<file>%s</file>
|
||||||
<line>%d</line>
|
<line>%d</line>
|
||||||
</log>\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] );
|
</log>
|
||||||
|
', $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] );
|
||||||
}
|
}
|
||||||
fwrite( $exportFP,
|
fwrite( $exportFP,
|
||||||
' </logs>
|
' </logs>
|
||||||
|
@ -413,7 +417,7 @@ switch ( $_REQUEST['task'] ) {
|
||||||
ZM\Fatal('No log export format given');
|
ZM\Fatal('No log export format given');
|
||||||
$format = $_REQUEST['format'];
|
$format = $_REQUEST['format'];
|
||||||
|
|
||||||
switch( $format ) {
|
switch ( $format ) {
|
||||||
case 'text' :
|
case 'text' :
|
||||||
$exportExt = 'txt';
|
$exportExt = 'txt';
|
||||||
break;
|
break;
|
||||||
|
@ -430,15 +434,15 @@ switch ( $_REQUEST['task'] ) {
|
||||||
ZM\Fatal("Unrecognised log export format '$format'");
|
ZM\Fatal("Unrecognised log export format '$format'");
|
||||||
}
|
}
|
||||||
|
|
||||||
$exportFile = "zm-log.$exportExt";
|
$exportFile = 'zm-log.'.$exportExt;
|
||||||
$exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt";
|
$exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt;
|
||||||
|
|
||||||
header('Pragma: public');
|
header('Pragma: public');
|
||||||
header('Expires: 0');
|
header('Expires: 0');
|
||||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||||
header('Cache-Control: private', false ); // required by certain browsers
|
header('Cache-Control: private', false); // required by certain browsers
|
||||||
header('Content-Description: File Transfer');
|
header('Content-Description: File Transfer');
|
||||||
header('Content-Disposition: attachment; filename="'.$exportFile.'"' );
|
header('Content-Disposition: attachment; filename="'.$exportFile.'"');
|
||||||
header('Content-Transfer-Encoding: binary');
|
header('Content-Transfer-Encoding: binary');
|
||||||
header('Content-Type: application/force-download');
|
header('Content-Type: application/force-download');
|
||||||
header('Content-Length: '.filesize($exportPath));
|
header('Content-Length: '.filesize($exportPath));
|
||||||
|
@ -446,6 +450,6 @@ switch ( $_REQUEST['task'] ) {
|
||||||
exit(0);
|
exit(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} // end switch ( $_REQUEST['task'] )
|
||||||
ajaxError('Unrecognised action or insufficient permissions');
|
ajaxError('Unrecognised action or insufficient permissions');
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
if ($_REQUEST['entity'] == 'navBar') {
|
if ( $_REQUEST['entity'] == 'navBar' ) {
|
||||||
$data = array();
|
$data = array();
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$data['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS );
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
|
$data['auth'] = $auth_hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$data['message'] = getNavBarHtml('reload');
|
$data['message'] = getNavBarHtml('reload');
|
||||||
ajaxResponse($data);
|
ajaxResponse($data);
|
||||||
|
@ -83,8 +86,8 @@ $statusData = array(
|
||||||
'MinEventId' => array( 'sql' => '(SELECT min(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
'MinEventId' => array( 'sql' => '(SELECT min(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||||
'MaxEventId' => array( 'sql' => '(SELECT max(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
'MaxEventId' => array( 'sql' => '(SELECT max(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||||
'TotalEvents' => array( 'sql' => '(SELECT count(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
'TotalEvents' => array( 'sql' => '(SELECT count(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||||
'Status' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ),
|
'Status' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ):null),
|
||||||
'FrameRate' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ),
|
'FrameRate' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ):null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'events' => array(
|
'events' => array(
|
||||||
|
@ -93,6 +96,7 @@ $statusData = array(
|
||||||
'selector' => 'Events.MonitorId',
|
'selector' => 'Events.MonitorId',
|
||||||
'elements' => array(
|
'elements' => array(
|
||||||
'Id' => true,
|
'Id' => true,
|
||||||
|
'MonitorId' => true,
|
||||||
'Name' => true,
|
'Name' => true,
|
||||||
'Cause' => true,
|
'Cause' => true,
|
||||||
'Notes' => true,
|
'Notes' => true,
|
||||||
|
@ -204,6 +208,7 @@ function collectData() {
|
||||||
$fieldSql = array();
|
$fieldSql = array();
|
||||||
$joinSql = array();
|
$joinSql = array();
|
||||||
$groupSql = array();
|
$groupSql = array();
|
||||||
|
$values = array();
|
||||||
|
|
||||||
$elements = &$entitySpec['elements'];
|
$elements = &$entitySpec['elements'];
|
||||||
$lc_elements = array_change_key_case( $elements );
|
$lc_elements = array_change_key_case( $elements );
|
||||||
|
@ -258,7 +263,6 @@ function collectData() {
|
||||||
if ( $id && !empty($entitySpec['selector']) ) {
|
if ( $id && !empty($entitySpec['selector']) ) {
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$where = array();
|
$where = array();
|
||||||
$values = array();
|
|
||||||
foreach( $entitySpec['selector'] as $selIndex => $selector ) {
|
foreach( $entitySpec['selector'] as $selIndex => $selector ) {
|
||||||
$selectorParamName = ':selector' . $selIndex;
|
$selectorParamName = ':selector' . $selIndex;
|
||||||
if ( is_array( $selector ) ) {
|
if ( is_array( $selector ) ) {
|
||||||
|
|
|
@ -86,10 +86,8 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
$numSockets = socket_select($rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000);
|
$numSockets = socket_select($rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000);
|
||||||
|
|
||||||
if ( $numSockets === false ) {
|
if ( $numSockets === false ) {
|
||||||
ZM\Error('socket_select failed: ' . socket_strerror(socket_last_error()));
|
|
||||||
ajaxError('socket_select failed: '.socket_strerror(socket_last_error()));
|
ajaxError('socket_select failed: '.socket_strerror(socket_last_error()));
|
||||||
} else if ( $numSockets < 0 ) {
|
} else if ( $numSockets < 0 ) {
|
||||||
ZM\Error("Socket closed $remSockFile");
|
|
||||||
ajaxError("Socket closed $remSockFile");
|
ajaxError("Socket closed $remSockFile");
|
||||||
} else if ( $numSockets == 0 ) {
|
} else if ( $numSockets == 0 ) {
|
||||||
ZM\Error("Timed out waiting for msg $remSockFile");
|
ZM\Error("Timed out waiting for msg $remSockFile");
|
||||||
|
@ -97,7 +95,6 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
#ajaxError("Timed out waiting for msg $remSockFile");
|
#ajaxError("Timed out waiting for msg $remSockFile");
|
||||||
} else if ( $numSockets > 0 ) {
|
} else if ( $numSockets > 0 ) {
|
||||||
if ( count($rSockets) != 1 ) {
|
if ( count($rSockets) != 1 ) {
|
||||||
ZM\Error('Bogus return from select, '.count($rSockets).' sockets available');
|
|
||||||
ajaxError('Bogus return from select, '.count($rSockets).' sockets available');
|
ajaxError('Bogus return from select, '.count($rSockets).' sockets available');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,17 +116,17 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
switch ( $data['type'] ) {
|
switch ( $data['type'] ) {
|
||||||
case MSG_DATA_WATCH :
|
case MSG_DATA_WATCH :
|
||||||
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
||||||
ZM\Logger::Debug('FPS: ' . $data['fps']);
|
|
||||||
$data['fps'] = round( $data['fps'], 2 );
|
$data['fps'] = round( $data['fps'], 2 );
|
||||||
ZM\Logger::Debug('FPS: ' . $data['fps'] );
|
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['delay'] = round( $data['delay'], 2 );
|
$data['delay'] = round( $data['delay'], 2 );
|
||||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$time = time();
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
// Regenerate auth hash after half the lifetime of the hash
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
$data['auth'] = $auth_hash;
|
||||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
ZM\Logger::Debug("including nw auth hash " . $data['auth']);
|
||||||
|
} else {
|
||||||
|
ZM\Logger::Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ajaxResponse(array('status'=>$data));
|
ajaxResponse(array('status'=>$data));
|
||||||
|
@ -143,12 +140,11 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
$data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg);
|
$data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg);
|
||||||
}
|
}
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
$data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$time = time();
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
// Regenerate auth hash after half the lifetime of the hash
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
$data['auth'] = $auth_hash;
|
||||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ajaxResponse(array('status'=>$data));
|
ajaxResponse(array('status'=>$data));
|
||||||
|
|
|
@ -149,6 +149,10 @@ class EventsController extends AppController {
|
||||||
));
|
));
|
||||||
$event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id'];
|
$event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id'];
|
||||||
$event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id'];
|
$event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id'];
|
||||||
|
|
||||||
|
$this->loadModel('Frame');
|
||||||
|
$event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'FrameId',array('Score'=>'desc','FrameId'=>'asc'))['Frame']['FrameId'];
|
||||||
|
$event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['FrameId'];
|
||||||
|
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
'event' => $event,
|
'event' => $event,
|
||||||
|
|
|
@ -77,16 +77,24 @@ class GroupsController extends AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->Group->create();
|
$this->Group->create();
|
||||||
if ( $this->Group->save($this->request->data) ) {
|
|
||||||
|
if ( $this->request->data['Group']['MonitorIds'] and ! isset($this->request->data['Monitor']) ) {
|
||||||
|
$this->request->data['Monitor'] = explode(',', $this->request->data['Group']['MonitorIds']);
|
||||||
|
unset($this->request->data['Group']['MonitorIds']);
|
||||||
|
}
|
||||||
|
if ( $this->Group->saveAssociated($this->request->data, array('atomic'=>true)) ) {
|
||||||
return $this->flash(
|
return $this->flash(
|
||||||
__('The group has been saved.'),
|
__('The group has been saved.'),
|
||||||
array('action' => 'index')
|
array('action' => 'index')
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
}
|
ZM\Error("Failed to save Group");
|
||||||
$monitors = $this->Group->Monitor->find('list');
|
debug($this->Group->invalidFields());
|
||||||
|
}
|
||||||
|
} # end if post
|
||||||
|
$monitors = $this->Group->Monitor->find('list');
|
||||||
$this->set(compact('monitors'));
|
$this->set(compact('monitors'));
|
||||||
}
|
} # end add
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* edit method
|
* edit method
|
||||||
|
|
|
@ -50,8 +50,10 @@ class HostController extends AppController {
|
||||||
$cred_depr = [];
|
$cred_depr = [];
|
||||||
|
|
||||||
if ( $username && $password ) {
|
if ( $username && $password ) {
|
||||||
|
ZM\Logger::Debug('Username and password provided, generating access and refresh tokens');
|
||||||
$cred = $this->_getCredentials(true, '', $username); // generate refresh
|
$cred = $this->_getCredentials(true, '', $username); // generate refresh
|
||||||
} else {
|
} else {
|
||||||
|
ZM\Logger::Debug('Only generating access token');
|
||||||
$cred = $this->_getCredentials(false, $token); // don't generate refresh
|
$cred = $this->_getCredentials(false, $token); // don't generate refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +71,8 @@ class HostController extends AppController {
|
||||||
$cred_depr = $this->_getCredentialsDeprecated();
|
$cred_depr = $this->_getCredentialsDeprecated();
|
||||||
$login_array['credentials'] = $cred_depr[0];
|
$login_array['credentials'] = $cred_depr[0];
|
||||||
$login_array['append_password'] = $cred_depr[1];
|
$login_array['append_password'] = $cred_depr[1];
|
||||||
|
} else {
|
||||||
|
ZM\Logger::Debug('Legacy Auth is disabled, not generating auth= credentials');
|
||||||
}
|
}
|
||||||
|
|
||||||
$login_array['version'] = $ver[0];
|
$login_array['version'] = $ver[0];
|
||||||
|
@ -108,8 +112,11 @@ class HostController extends AppController {
|
||||||
|
|
||||||
private function _getCredentials($generate_refresh_token=false, $token='', $username='') {
|
private function _getCredentials($generate_refresh_token=false, $token='', $username='') {
|
||||||
|
|
||||||
if ( !ZM_OPT_USE_AUTH )
|
if ( !ZM_OPT_USE_AUTH ) {
|
||||||
|
ZM\Error('OPT_USE_AUTH is turned off. Tokens will be null');
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( !ZM_AUTH_HASH_SECRET )
|
if ( !ZM_AUTH_HASH_SECRET )
|
||||||
throw new ForbiddenException(__('Please create a valid AUTH_HASH_SECRET in ZoneMinder'));
|
throw new ForbiddenException(__('Please create a valid AUTH_HASH_SECRET in ZoneMinder'));
|
||||||
|
@ -136,7 +143,7 @@ class HostController extends AppController {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
$access_issued_at = time();
|
$access_issued_at = time();
|
||||||
$access_ttl = (ZM_AUTH_HASH_TTL || 2) * 3600;
|
$access_ttl = max(ZM_AUTH_HASH_TTL,1) * 3600;
|
||||||
|
|
||||||
// by default access token will expire in 2 hrs
|
// by default access token will expire in 2 hrs
|
||||||
// you can change it by changing the value of ZM_AUTH_HASH_TLL
|
// you can change it by changing the value of ZM_AUTH_HASH_TLL
|
||||||
|
|
|
@ -44,33 +44,26 @@ class MonitorsController extends AppController {
|
||||||
} else {
|
} else {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
global $user;
|
global $user;
|
||||||
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
|
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
|
||||||
if ( $allowedMonitors ) {
|
if ( $allowedMonitors ) {
|
||||||
$conditions['Monitor.Id' ] = $allowedMonitors;
|
$conditions['Monitor.Id' ] = $allowedMonitors;
|
||||||
}
|
}
|
||||||
$find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
|
|
||||||
|
|
||||||
if ( isset($conditions['GroupId']) ) {
|
$find_array = array(
|
||||||
$find_array['joins'] = array(
|
'conditions' => &$conditions,
|
||||||
|
'contain' => array('Group'),
|
||||||
|
'joins' => array(
|
||||||
array(
|
array(
|
||||||
'table' => 'Groups_Monitors',
|
'table' => 'Groups_Monitors',
|
||||||
'type' => 'inner',
|
'type' => 'left',
|
||||||
'conditions' => array(
|
'conditions' => array(
|
||||||
'Groups_Monitors.MonitorId = Monitor.Id'
|
'Groups_Monitors.MonitorId = Monitor.Id',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
//array(
|
),
|
||||||
//'table' => 'Groups',
|
'group' => '`Monitor`.`Id`',
|
||||||
//'type' => 'inner',
|
);
|
||||||
//'conditions' => array(
|
|
||||||
//'Groups.Id = Groups_Monitors.GroupId',
|
|
||||||
//'Groups.Id' => $this->request->params['GroupId'],
|
|
||||||
//),
|
|
||||||
//)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$monitors = $this->Monitor->find('all',$find_array);
|
$monitors = $this->Monitor->find('all',$find_array);
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
'monitors' => $monitors,
|
'monitors' => $monitors,
|
||||||
|
@ -386,6 +379,8 @@ class MonitorsController extends AppController {
|
||||||
$args = '';
|
$args = '';
|
||||||
if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) {
|
if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) {
|
||||||
$args = '-d ' . $monitor['Device'];
|
$args = '-d ' . $monitor['Device'];
|
||||||
|
} else if ( $daemon == 'zmcontrol.pl' ) {
|
||||||
|
$args = '--id '.$id;
|
||||||
} else {
|
} else {
|
||||||
$args = '-m ' . $id;
|
$args = '-m ' . $id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,17 @@ class ServersController extends AppController {
|
||||||
|
|
||||||
public function beforeFilter() {
|
public function beforeFilter() {
|
||||||
parent::beforeFilter();
|
parent::beforeFilter();
|
||||||
|
/*
|
||||||
|
* A user needs the server data to calculate how to view a monitor, and there really isn't anything sensitive in this data.
|
||||||
|
* So it has been decided for now to just let everyone read it.
|
||||||
|
|
||||||
global $user;
|
global $user;
|
||||||
$canView = (!$user) || ($user['System'] != 'None');
|
$canView = (!$user) || ($user['System'] != 'None');
|
||||||
if ( !$canView ) {
|
if ( !$canView ) {
|
||||||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +39,7 @@ class ServersController extends AppController {
|
||||||
$this->Server->recursive = 0;
|
$this->Server->recursive = 0;
|
||||||
|
|
||||||
$options = '';
|
$options = '';
|
||||||
$servers = $this->Server->find('all',$options);
|
$servers = $this->Server->find('all', $options);
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
'servers' => $servers,
|
'servers' => $servers,
|
||||||
'_serialize' => array('servers')
|
'_serialize' => array('servers')
|
||||||
|
@ -50,13 +55,13 @@ class ServersController extends AppController {
|
||||||
*/
|
*/
|
||||||
public function view($id = null) {
|
public function view($id = null) {
|
||||||
$this->Server->recursive = 0;
|
$this->Server->recursive = 0;
|
||||||
if (!$this->Server->exists($id)) {
|
if ( !$this->Server->exists($id) ) {
|
||||||
throw new NotFoundException(__('Invalid server'));
|
throw new NotFoundException(__('Invalid server'));
|
||||||
}
|
}
|
||||||
$restricted = '';
|
$restricted = '';
|
||||||
|
|
||||||
$options = array('conditions' => array(
|
$options = array('conditions' => array(
|
||||||
array('Server.' . $this->Server->primaryKey => $id),
|
array('Server.'.$this->Server->primaryKey => $id),
|
||||||
$restricted
|
$restricted
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Group extends AppModel {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $hasMany = array(
|
public $hasAndBelongsToMany = array(
|
||||||
'Monitor' => array(
|
'Monitor' => array(
|
||||||
'className' => 'Monitor',
|
'className' => 'Monitor',
|
||||||
'joinTable' => 'Groups_Monitors',
|
'joinTable' => 'Groups_Monitors',
|
||||||
|
@ -77,4 +77,5 @@ class Group extends AppModel {
|
||||||
'counterQuery' => ''
|
'counterQuery' => ''
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
var $actsAs = array( 'Containable' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,12 +128,16 @@ class Control extends ZM_Object {
|
||||||
$cmds['PresetHome'] = 'presetHome';
|
$cmds['PresetHome'] = 'presetHome';
|
||||||
|
|
||||||
if ( $this->CanZoom() ) {
|
if ( $this->CanZoom() ) {
|
||||||
if ( $this->CanZoomCon() )
|
if ( $this->CanZoomCon() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomCon';
|
$cmds['ZoomRoot'] = 'zoomCon';
|
||||||
elseif ( $this->CanZoomRel() )
|
} else if ( $this->CanZoomRel() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomRel';
|
$cmds['ZoomRoot'] = 'zoomRel';
|
||||||
elseif ( $this->CanZoomAbs() )
|
} else if ( $this->CanZoomAbs() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomAbs';
|
$cmds['ZoomRoot'] = 'zoomAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['ZoomRoot'] = '';
|
||||||
|
Error('No zoom type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele';
|
$cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele';
|
||||||
$cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide';
|
$cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide';
|
||||||
$cmds['ZoomStop'] = 'zoomStop';
|
$cmds['ZoomStop'] = 'zoomStop';
|
||||||
|
@ -142,12 +146,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanFocus() ) {
|
if ( $this->CanFocus() ) {
|
||||||
if ( $this->CanFocusCon() )
|
if ( $this->CanFocusCon() ) {
|
||||||
$cmds['FocusRoot'] = 'focusCon';
|
$cmds['FocusRoot'] = 'focusCon';
|
||||||
elseif ( $this->CanFocusRel() )
|
} else if ( $this->CanFocusRel() ) {
|
||||||
$cmds['FocusRoot'] = 'focusRel';
|
$cmds['FocusRoot'] = 'focusRel';
|
||||||
elseif ( $this->CanFocusAbs() )
|
} else if ( $this->CanFocusAbs() ) {
|
||||||
$cmds['FocusRoot'] = 'focusAbs';
|
$cmds['FocusRoot'] = 'focusAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['FocusRoot'] = '';
|
||||||
|
Error('No focus type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['FocusFar'] = $cmds['FocusRoot'].'Far';
|
$cmds['FocusFar'] = $cmds['FocusRoot'].'Far';
|
||||||
$cmds['FocusNear'] = $cmds['FocusRoot'].'Near';
|
$cmds['FocusNear'] = $cmds['FocusRoot'].'Near';
|
||||||
$cmds['FocusStop'] = 'focusStop';
|
$cmds['FocusStop'] = 'focusStop';
|
||||||
|
@ -156,12 +164,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanIris() ) {
|
if ( $this->CanIris() ) {
|
||||||
if ( $this->CanIrisCon() )
|
if ( $this->CanIrisCon() ) {
|
||||||
$cmds['IrisRoot'] = 'irisCon';
|
$cmds['IrisRoot'] = 'irisCon';
|
||||||
elseif ( $this->CanIrisRel() )
|
} else if ( $this->CanIrisRel() ) {
|
||||||
$cmds['IrisRoot'] = 'irisRel';
|
$cmds['IrisRoot'] = 'irisRel';
|
||||||
elseif ( $this->CanIrisAbs() )
|
} else if ( $this->CanIrisAbs() ) {
|
||||||
$cmds['IrisRoot'] = 'irisAbs';
|
$cmds['IrisRoot'] = 'irisAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['IrisRoot'] = '';
|
||||||
|
Error('No iris type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['IrisOpen'] = $cmds['IrisRoot'].'Open';
|
$cmds['IrisOpen'] = $cmds['IrisRoot'].'Open';
|
||||||
$cmds['IrisClose'] = $cmds['IrisRoot'].'Close';
|
$cmds['IrisClose'] = $cmds['IrisRoot'].'Close';
|
||||||
$cmds['IrisStop'] = 'irisStop';
|
$cmds['IrisStop'] = 'irisStop';
|
||||||
|
@ -170,12 +182,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanWhite() ) {
|
if ( $this->CanWhite() ) {
|
||||||
if ( $this->CanWhiteCon() )
|
if ( $this->CanWhiteCon() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteCon';
|
$cmds['WhiteRoot'] = 'whiteCon';
|
||||||
elseif ( $this->CanWhiteRel() )
|
} else if ( $this->CanWhiteRel() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteRel';
|
$cmds['WhiteRoot'] = 'whiteRel';
|
||||||
elseif ( $this->CanWhiteAbs() )
|
} else if ( $this->CanWhiteAbs() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteAbs';
|
$cmds['WhiteRoot'] = 'whiteAbs';
|
||||||
|
} else {
|
||||||
|
Error('No White type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
$cmds['WhiteRoot'] = '';
|
||||||
|
}
|
||||||
$cmds['WhiteIn'] = $cmds['WhiteRoot'].'In';
|
$cmds['WhiteIn'] = $cmds['WhiteRoot'].'In';
|
||||||
$cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out';
|
$cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out';
|
||||||
$cmds['WhiteAuto'] = 'whiteAuto';
|
$cmds['WhiteAuto'] = 'whiteAuto';
|
||||||
|
@ -183,12 +199,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanGain() ) {
|
if ( $this->CanGain() ) {
|
||||||
if ( $this->CanGainCon() )
|
if ( $this->CanGainCon() ) {
|
||||||
$cmds['GainRoot'] = 'gainCon';
|
$cmds['GainRoot'] = 'gainCon';
|
||||||
elseif ( $this->CanGainRel() )
|
} else if ( $this->CanGainRel() ) {
|
||||||
$cmds['GainRoot'] = 'gainRel';
|
$cmds['GainRoot'] = 'gainRel';
|
||||||
elseif ( $this->CanGainAbs() )
|
} else if ( $this->CanGainAbs() ) {
|
||||||
$cmds['GainRoot'] = 'gainAbs';
|
$cmds['GainRoot'] = 'gainAbs';
|
||||||
|
} else {
|
||||||
|
Error('No Gain type selected');
|
||||||
|
$cmds['GainRoot'] = '';
|
||||||
|
}
|
||||||
$cmds['GainUp'] = $cmds['GainRoot'].'Up';
|
$cmds['GainUp'] = $cmds['GainRoot'].'Up';
|
||||||
$cmds['GainDown'] = $cmds['GainRoot'].'Down';
|
$cmds['GainDown'] = $cmds['GainRoot'].'Down';
|
||||||
$cmds['GainAuto'] = 'gainAuto';
|
$cmds['GainAuto'] = 'gainAuto';
|
||||||
|
@ -207,6 +227,7 @@ class Control extends ZM_Object {
|
||||||
$cmds['Center'] = $cmds['PresetHome'];
|
$cmds['Center'] = $cmds['PresetHome'];
|
||||||
} else {
|
} else {
|
||||||
$cmds['MoveRoot'] = '';
|
$cmds['MoveRoot'] = '';
|
||||||
|
Error('No move type selected. Please select Continuous, Relative, Absolute');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cmds['MoveUp'] = $cmds['MoveRoot'].'Up';
|
$cmds['MoveUp'] = $cmds['MoveRoot'].'Up';
|
||||||
|
|
|
@ -47,14 +47,18 @@ class Event extends ZM_Object {
|
||||||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function clear_cache() {
|
||||||
|
return ZM_Object::_clear_cache(get_class());
|
||||||
|
}
|
||||||
|
|
||||||
public function Storage( $new = null ) {
|
public function Storage( $new = null ) {
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'Storage'} = $new;
|
$this->{'Storage'} = $new;
|
||||||
}
|
}
|
||||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
|
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) {
|
||||||
if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
|
if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
|
||||||
$this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'}));
|
$this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'}));
|
||||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) )
|
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) )
|
||||||
$this->{'Storage'} = new Storage(NULL);
|
$this->{'Storage'} = new Storage(NULL);
|
||||||
}
|
}
|
||||||
return $this->{'Storage'};
|
return $this->{'Storage'};
|
||||||
|
@ -64,10 +68,10 @@ class Event extends ZM_Object {
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'SecondaryStorage'} = $new;
|
$this->{'SecondaryStorage'} = $new;
|
||||||
}
|
}
|
||||||
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) ) {
|
if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) ) {
|
||||||
if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} )
|
if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} )
|
||||||
$this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$this->{'SecondaryStorageId'}));
|
$this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$this->{'SecondaryStorageId'}));
|
||||||
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) )
|
if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) )
|
||||||
$this->{'SecondaryStorage'} = new Storage(NULL);
|
$this->{'SecondaryStorage'} = new Storage(NULL);
|
||||||
}
|
}
|
||||||
return $this->{'SecondaryStorage'};
|
return $this->{'SecondaryStorage'};
|
||||||
|
@ -262,7 +266,7 @@ class Event extends ZM_Object {
|
||||||
if ( is_null($new) or ( $new != '' ) ) {
|
if ( is_null($new) or ( $new != '' ) ) {
|
||||||
$this->{'DiskSpace'} = $new;
|
$this->{'DiskSpace'} = $new;
|
||||||
}
|
}
|
||||||
if ( (!array_key_exists('DiskSpace',$this)) or (null === $this->{'DiskSpace'}) ) {
|
if ( (!property_exists($this, 'DiskSpace')) or (null === $this->{'DiskSpace'}) ) {
|
||||||
$this->{'DiskSpace'} = folder_size($this->Path());
|
$this->{'DiskSpace'} = folder_size($this->Path());
|
||||||
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
|
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
|
||||||
}
|
}
|
||||||
|
@ -298,7 +302,7 @@ class Event extends ZM_Object {
|
||||||
} // end function createListThumbnail
|
} // end function createListThumbnail
|
||||||
|
|
||||||
function ThumbnailWidth( ) {
|
function ThumbnailWidth( ) {
|
||||||
if ( ! ( array_key_exists('ThumbnailWidth', $this) ) ) {
|
if ( ! ( property_exists($this, 'ThumbnailWidth') ) ) {
|
||||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
||||||
|
@ -315,7 +319,7 @@ class Event extends ZM_Object {
|
||||||
} // end function ThumbnailWidth
|
} // end function ThumbnailWidth
|
||||||
|
|
||||||
function ThumbnailHeight( ) {
|
function ThumbnailHeight( ) {
|
||||||
if ( ! ( array_key_exists('ThumbnailHeight', $this) ) ) {
|
if ( ! ( property_exists($this, 'ThumbnailHeight') ) ) {
|
||||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
||||||
|
@ -413,31 +417,27 @@ class Event extends ZM_Object {
|
||||||
}
|
}
|
||||||
} // end if capture file exists
|
} // end if capture file exists
|
||||||
} // end if analyze file exists
|
} // end if analyze file exists
|
||||||
}
|
} // end if frame or snapshot
|
||||||
|
|
||||||
$captPath = $eventPath.'/'.$captImage;
|
$captPath = $eventPath.'/'.$captImage;
|
||||||
if ( ! file_exists($captPath) ) {
|
if ( ! file_exists($captPath) ) {
|
||||||
Error("Capture file does not exist at $captPath");
|
Error("Capture file does not exist at $captPath");
|
||||||
}
|
}
|
||||||
|
|
||||||
//echo "CI:$captImage, CP:$captPath, TCP:$captPath<br>";
|
|
||||||
|
|
||||||
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']);
|
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']);
|
||||||
$analPath = $eventPath.'/'.$analImage;
|
$analPath = $eventPath.'/'.$analImage;
|
||||||
|
|
||||||
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
|
||||||
|
|
||||||
$alarmFrame = $frame['Type']=='Alarm';
|
$alarmFrame = $frame['Type']=='Alarm';
|
||||||
|
|
||||||
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
||||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
$isAnalImage = $hasAnalImage && !$captureOnly;
|
||||||
|
|
||||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists('imagecreatefromjpeg') ) {
|
if ( !ZM_WEB_SCALE_THUMBS || ($scale >= SCALE_BASE) || !function_exists('imagecreatefromjpeg') ) {
|
||||||
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
||||||
$imageFile = $imagePath;
|
$imageFile = $imagePath;
|
||||||
$thumbFile = $thumbPath;
|
$thumbFile = $thumbPath;
|
||||||
} else {
|
} else {
|
||||||
if ( version_compare( phpversion(), '4.3.10', '>=') )
|
if ( version_compare(phpversion(), '4.3.10', '>=') )
|
||||||
$fraction = sprintf('%.3F', $scale/SCALE_BASE);
|
$fraction = sprintf('%.3F', $scale/SCALE_BASE);
|
||||||
else
|
else
|
||||||
$fraction = sprintf('%.3f', $scale/SCALE_BASE);
|
$fraction = sprintf('%.3f', $scale/SCALE_BASE);
|
||||||
|
@ -455,19 +455,19 @@ class Event extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
$thumbFile = $thumbPath;
|
$thumbFile = $thumbPath;
|
||||||
if ( $overwrite || ! file_exists( $thumbFile ) || ! filesize( $thumbFile ) ) {
|
if ( $overwrite || ! file_exists($thumbFile) || ! filesize($thumbFile) ) {
|
||||||
// Get new dimensions
|
// Get new dimensions
|
||||||
list( $imageWidth, $imageHeight ) = getimagesize( $imagePath );
|
list( $imageWidth, $imageHeight ) = getimagesize($imagePath);
|
||||||
$thumbWidth = $imageWidth * $fraction;
|
$thumbWidth = $imageWidth * $fraction;
|
||||||
$thumbHeight = $imageHeight * $fraction;
|
$thumbHeight = $imageHeight * $fraction;
|
||||||
|
|
||||||
// Resample
|
// Resample
|
||||||
$thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
|
$thumbImage = imagecreatetruecolor($thumbWidth, $thumbHeight);
|
||||||
$image = imagecreatefromjpeg( $imagePath );
|
$image = imagecreatefromjpeg($imagePath);
|
||||||
imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
|
imagecopyresampled($thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight);
|
||||||
|
|
||||||
if ( !imagejpeg( $thumbImage, $thumbPath ) )
|
if ( !imagejpeg($thumbImage, $thumbPath) )
|
||||||
Error( "Can't create thumbnail '$thumbPath'" );
|
Error("Can't create thumbnail '$thumbPath'");
|
||||||
}
|
}
|
||||||
} # Create thumbnails
|
} # Create thumbnails
|
||||||
|
|
||||||
|
@ -507,11 +507,12 @@ class Event extends ZM_Object {
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -550,15 +551,16 @@ class Event extends ZM_Object {
|
||||||
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
||||||
if ( $Server->Id() != ZM_SERVER_ID ) {
|
if ( $Server->Id() != ZM_SERVER_ID ) {
|
||||||
|
|
||||||
$url = $Server->UrlToApi() . '/events/'.$this->{'Id'}.'.json';
|
$url = $Server->UrlToApi().'/events/'.$this->{'Id'}.'.json';
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
|
|
@ -39,8 +39,8 @@ class Filter extends ZM_Object {
|
||||||
$this->{'Query'} = func_get_arg(0);;
|
$this->{'Query'} = func_get_arg(0);;
|
||||||
$this->{'Query_json'} = jsonEncode($this->{'Query'});
|
$this->{'Query_json'} = jsonEncode($this->{'Query'});
|
||||||
}
|
}
|
||||||
if ( !array_key_exists('Query', $this) ) {
|
if ( !property_exists($this, 'Query') ) {
|
||||||
if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) {
|
if ( property_exists($this, 'Query_json') and $this->{'Query_json'} ) {
|
||||||
$this->{'Query'} = jsonDecode($this->{'Query_json'});
|
$this->{'Query'} = jsonDecode($this->{'Query_json'});
|
||||||
} else {
|
} else {
|
||||||
$this->{'Query'} = array();
|
$this->{'Query'} = array();
|
||||||
|
@ -115,13 +115,17 @@ class Filter extends ZM_Object {
|
||||||
|
|
||||||
public function control($command, $server_id=null) {
|
public function control($command, $server_id=null) {
|
||||||
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running'));
|
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running'));
|
||||||
if ( !count($Servers) and !$server_id ) {
|
if ( !count($Servers) ) {
|
||||||
# This will be the non-multi-server case
|
if ( !$server_id ) {
|
||||||
$Servers = array(new Server());
|
# This will be the non-multi-server case
|
||||||
|
$Servers = array(new Server());
|
||||||
|
} else {
|
||||||
|
Warning("Server not found for id $server_id");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach ( $Servers as $Server ) {
|
foreach ( $Servers as $Server ) {
|
||||||
|
|
||||||
if ( !defined('ZM_SERVER_ID') or !$Server->Id() or ZM_SERVER_ID==$Server->Id() ) {
|
if ( (!defined('ZM_SERVER_ID')) or (!$Server->Id()) or (ZM_SERVER_ID==$Server->Id()) ) {
|
||||||
# Local
|
# Local
|
||||||
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
|
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
|
||||||
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
|
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
|
||||||
|
@ -139,7 +143,7 @@ class Filter extends ZM_Object {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url = '?user='.$_SESSION['username'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$url .= '&view=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
|
$url .= '&view=filter&object=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
$data = array();
|
$data = array();
|
||||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Group extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
if ( array_key_exists('Id', $this) ) {
|
if ( property_exists($this, 'Id') ) {
|
||||||
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}));
|
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}));
|
||||||
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'}));
|
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'}));
|
||||||
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'}));
|
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'}));
|
||||||
|
@ -35,7 +35,7 @@ class Group extends ZM_Object {
|
||||||
if ( isset($new) ) {
|
if ( isset($new) ) {
|
||||||
$this->{'depth'} = $new;
|
$this->{'depth'} = $new;
|
||||||
}
|
}
|
||||||
if ( !array_key_exists('depth', $this) or ($this->{'depth'} === null) ) {
|
if ( !property_exists($this, 'depth') or ($this->{'depth'} === null) ) {
|
||||||
$this->{'depth'} = 0;
|
$this->{'depth'} = 0;
|
||||||
if ( $this->{'ParentId'} != null ) {
|
if ( $this->{'ParentId'} != null ) {
|
||||||
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
|
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
|
||||||
|
@ -46,7 +46,7 @@ class Group extends ZM_Object {
|
||||||
} // end public function depth
|
} // end public function depth
|
||||||
|
|
||||||
public function MonitorIds( ) {
|
public function MonitorIds( ) {
|
||||||
if ( ! array_key_exists('MonitorIds', $this) ) {
|
if ( ! property_exists($this, 'MonitorIds') ) {
|
||||||
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
|
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
|
||||||
}
|
}
|
||||||
return $this->{'MonitorIds'};
|
return $this->{'MonitorIds'};
|
||||||
|
|
|
@ -9,125 +9,133 @@ require_once('Storage.php');
|
||||||
class Monitor extends ZM_Object {
|
class Monitor extends ZM_Object {
|
||||||
protected static $table = 'Monitors';
|
protected static $table = 'Monitors';
|
||||||
|
|
||||||
protected $defaults = array(
|
protected $defaults = array(
|
||||||
'Id' => null,
|
'Id' => null,
|
||||||
'Name' => '',
|
'Name' => array('type'=>'text','filter_regexp'=>'/[^\w\-\.\(\)\:\/ ]/'),
|
||||||
'ServerId' => 0,
|
'Notes' => '',
|
||||||
'StorageId' => 0,
|
'ServerId' => 0,
|
||||||
'Type' => 'Ffmpeg',
|
'StorageId' => 0,
|
||||||
'Function' => 'Mocord',
|
'Type' => 'Ffmpeg',
|
||||||
'Enabled' => array('type'=>'boolean','default'=>1),
|
'Function' => 'Mocord',
|
||||||
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
|
'Enabled' => array('type'=>'boolean','default'=>1),
|
||||||
'Triggers' => array('type'=>'set','default'=>''),
|
'LinkedMonitors' => array('type'=>'set', 'default'=>null),
|
||||||
'Device' => '',
|
'Triggers' => array('type'=>'set','default'=>''),
|
||||||
'Channel' => 0,
|
'Device' => '',
|
||||||
'Format' => '0',
|
'Channel' => 0,
|
||||||
'V4LMultiBuffer' => null,
|
'Format' => '0',
|
||||||
'V4LCapturesPerFrame' => 1,
|
'V4LMultiBuffer' => null,
|
||||||
'Protocol' => null,
|
'V4LCapturesPerFrame' => 1,
|
||||||
'Method' => '',
|
'Protocol' => null,
|
||||||
'Host' => null,
|
'Method' => '',
|
||||||
'Port' => '',
|
'Host' => null,
|
||||||
'SubPath' => '',
|
'Port' => '',
|
||||||
'Path' => null,
|
'SubPath' => '',
|
||||||
'Options' => null,
|
'Path' => null,
|
||||||
'User' => null,
|
'Options' => null,
|
||||||
'Pass' => null,
|
'User' => null,
|
||||||
// These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME
|
'Pass' => null,
|
||||||
'Width' => null,
|
// These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME
|
||||||
'Height' => null,
|
'Width' => null,
|
||||||
'Colours' => 4,
|
'Height' => null,
|
||||||
'Palette' => '0',
|
'Colours' => 4,
|
||||||
'Orientation' => null,
|
'Palette' => '0',
|
||||||
'Deinterlacing' => 0,
|
'Orientation' => null,
|
||||||
'DecoderHWAccelName' => null,
|
'Deinterlacing' => 0,
|
||||||
'DecoderHWAccelDevice' => null,
|
'DecoderHWAccelName' => null,
|
||||||
'SaveJPEGs' => 3,
|
'DecoderHWAccelDevice' => null,
|
||||||
'VideoWriter' => '0',
|
'SaveJPEGs' => 3,
|
||||||
'OutputCodec' => null,
|
'VideoWriter' => '0',
|
||||||
'OutputContainer' => null,
|
'OutputCodec' => null,
|
||||||
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
'OutputContainer' => null,
|
||||||
'RecordAudio' => array('type'=>'boolean', 'default'=>0),
|
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||||
'RTSPDescribe' => array('type'=>'boolean','default'=>0),
|
'RecordAudio' => array('type'=>'boolean', 'default'=>0),
|
||||||
'Brightness' => -1,
|
'RTSPDescribe' => array('type'=>'boolean','default'=>0),
|
||||||
'Contrast' => -1,
|
'Brightness' => -1,
|
||||||
'Hue' => -1,
|
'Contrast' => -1,
|
||||||
'Colour' => -1,
|
'Hue' => -1,
|
||||||
'EventPrefix' => 'Event-',
|
'Colour' => -1,
|
||||||
'LabelFormat' => '%N - %d/%m/%y %H:%M:%S',
|
'EventPrefix' => 'Event-',
|
||||||
'LabelX' => 0,
|
'LabelFormat' => '%N - %d/%m/%y %H:%M:%S',
|
||||||
'LabelY' => 0,
|
'LabelX' => 0,
|
||||||
'LabelSize' => 1,
|
'LabelY' => 0,
|
||||||
'ImageBufferCount' => 100,
|
'LabelSize' => 1,
|
||||||
'WarmupCount' => 0,
|
'ImageBufferCount' => 20,
|
||||||
'PreEventCount' => 0,
|
'WarmupCount' => 0,
|
||||||
'PostEventCount' => 0,
|
'PreEventCount' => 5,
|
||||||
'StreamReplayBuffer' => 0,
|
'PostEventCount' => 5,
|
||||||
'AlarmFrameCount' => 1,
|
'StreamReplayBuffer' => 0,
|
||||||
'SectionLength' => 600,
|
'AlarmFrameCount' => 1,
|
||||||
'MinSectionLength' => 10,
|
'SectionLength' => 600,
|
||||||
'FrameSkip' => 0,
|
'MinSectionLength' => 10,
|
||||||
'MotionFrameSkip' => 0,
|
'FrameSkip' => 0,
|
||||||
'AnalysisFPSLimit' => null,
|
'MotionFrameSkip' => 0,
|
||||||
'AnalysisUpdateDelay' => 0,
|
'AnalysisFPSLimit' => null,
|
||||||
'MaxFPS' => null,
|
'AnalysisUpdateDelay' => 0,
|
||||||
'AlarmMaxFPS' => null,
|
'MaxFPS' => null,
|
||||||
'FPSReportInterval' => 100,
|
'AlarmMaxFPS' => null,
|
||||||
'RefBlendPerc' => 6,
|
'FPSReportInterval' => 100,
|
||||||
'AlarmRefBlendPerc' => 6,
|
'RefBlendPerc' => 6,
|
||||||
'Controllable' => array('type'=>'boolean','default'=>0),
|
'AlarmRefBlendPerc' => 6,
|
||||||
'ControlId' => null,
|
'Controllable' => array('type'=>'boolean','default'=>0),
|
||||||
'ControlDevice' => null,
|
'ControlId' => null,
|
||||||
'ControlAddress' => null,
|
'ControlDevice' => null,
|
||||||
'AutoStopTimeout' => null,
|
'ControlAddress' => null,
|
||||||
'TrackMotion' => array('type'=>'boolean','default'=>0),
|
'AutoStopTimeout' => null,
|
||||||
'TrackDelay' => null,
|
'TrackMotion' => array('type'=>'boolean','default'=>0),
|
||||||
'ReturnLocation' => -1,
|
'TrackDelay' => null,
|
||||||
'ReturnDelay' => null,
|
'ReturnLocation' => -1,
|
||||||
'DefaultRate' => 100,
|
'ReturnDelay' => null,
|
||||||
'DefaultScale' => 100,
|
'DefaultRate' => 100,
|
||||||
'SignalCheckPoints' => 0,
|
'DefaultScale' => 100,
|
||||||
'SignalCheckColour' => '#0000BE',
|
'SignalCheckPoints' => 0,
|
||||||
'WebColour' => 'red',
|
'SignalCheckColour' => '#0000BE',
|
||||||
'Exif' => array('type'=>'boolean','default'=>0),
|
'WebColour' => '#ff0000',
|
||||||
'Sequence' => null,
|
'Exif' => array('type'=>'boolean','default'=>0),
|
||||||
'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'Sequence' => null,
|
||||||
'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'TotalEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'HourEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'HourEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'DayEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'DayEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'WeekEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'WeekEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'MonthEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'MonthEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'ArchivedEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'ZoneCount' => 0,
|
'ArchivedEventDiskSpace' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
'Refresh' => null,
|
'ZoneCount' => 0,
|
||||||
'DefaultCodec' => 'auto',
|
'Refresh' => null,
|
||||||
'GroupIds' => array('default'=>array(), 'do_not_update'=>1),
|
'DefaultCodec' => 'auto',
|
||||||
);
|
'GroupIds' => array('default'=>array(), 'do_not_update'=>1),
|
||||||
private $status_fields = array(
|
);
|
||||||
'Status' => null,
|
private $status_fields = array(
|
||||||
'AnalysisFPS' => null,
|
'Status' => null,
|
||||||
'CaptureFPS' => null,
|
'AnalysisFPS' => null,
|
||||||
'CaptureBandwidth' => null,
|
'CaptureFPS' => null,
|
||||||
);
|
'CaptureBandwidth' => null,
|
||||||
|
);
|
||||||
|
|
||||||
public function Control() {
|
public function Control() {
|
||||||
if ( !array_key_exists('Control', $this) ) {
|
if ( !property_exists($this, 'Control') ) {
|
||||||
if ( $this->ControlId() )
|
if ( $this->ControlId() )
|
||||||
$this->{'Control'} = Control::find_one(array('Id'=>$this->{'ControlId'}));
|
$this->{'Control'} = Control::find_one(array('Id'=>$this->{'ControlId'}));
|
||||||
|
|
||||||
if ( !(array_key_exists('Control', $this) and $this->{'Control'}) )
|
if ( !(property_exists($this, 'Control') and $this->{'Control'}) )
|
||||||
$this->{'Control'} = new Control();
|
$this->{'Control'} = new Control();
|
||||||
}
|
}
|
||||||
return $this->{'Control'};
|
return $this->{'Control'};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Server() {
|
public function Server() {
|
||||||
return new Server($this->{'ServerId'});
|
if ( !property_exists($this, 'Server') ) {
|
||||||
|
if ( $this->ServerId() )
|
||||||
|
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||||
|
if ( !property_exists($this, 'Server') ) {
|
||||||
|
$this->{'Server'} = new Server();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->{'Server'};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call($fn, array $args){
|
public function __call($fn, array $args){
|
||||||
|
@ -138,7 +146,7 @@ private $status_fields = array(
|
||||||
$this->{$fn} = $args[0];
|
$this->{$fn} = $args[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( array_key_exists($fn, $this) ) {
|
if ( property_exists($this, $fn) ) {
|
||||||
return $this->{$fn};
|
return $this->{$fn};
|
||||||
} else if ( array_key_exists($fn, $this->defaults) ) {
|
} else if ( array_key_exists($fn, $this->defaults) ) {
|
||||||
if ( is_array($this->defaults[$fn]) ) {
|
if ( is_array($this->defaults[$fn]) ) {
|
||||||
|
@ -211,9 +219,9 @@ private $status_fields = array(
|
||||||
$this->{'Width'} = $new;
|
$this->{'Width'} = $new;
|
||||||
|
|
||||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width';
|
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width';
|
||||||
if ( array_key_exists($field, $this) )
|
if ( property_exists($this, $field) )
|
||||||
return $this->{$field};
|
return $this->{$field};
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function Width
|
} // end function Width
|
||||||
|
|
||||||
public function ViewHeight($new=null) {
|
public function ViewHeight($new=null) {
|
||||||
|
@ -221,9 +229,9 @@ private $status_fields = array(
|
||||||
$this->{'Height'} = $new;
|
$this->{'Height'} = $new;
|
||||||
|
|
||||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height';
|
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height';
|
||||||
if ( array_key_exists($field, $this) )
|
if ( property_exists($this, $field) )
|
||||||
return $this->{$field};
|
return $this->{$field};
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function Height
|
} // end function Height
|
||||||
|
|
||||||
public function SignalCheckColour($new=null) {
|
public function SignalCheckColour($new=null) {
|
||||||
|
@ -234,10 +242,10 @@ private $status_fields = array(
|
||||||
|
|
||||||
// Validate that it's a valid colour (we seem to allow color names, not just hex).
|
// Validate that it's a valid colour (we seem to allow color names, not just hex).
|
||||||
// This also helps prevent XSS.
|
// This also helps prevent XSS.
|
||||||
if (array_key_exists($field, $this) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
if ( property_exists($this, $field) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
||||||
return $this->{$field};
|
return $this->{$field};
|
||||||
}
|
}
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function SignalCheckColour
|
} // end function SignalCheckColour
|
||||||
|
|
||||||
public static function find( $parameters = array(), $options = array() ) {
|
public static function find( $parameters = array(), $options = array() ) {
|
||||||
|
@ -253,7 +261,7 @@ private $status_fields = array(
|
||||||
Warning('Attempt to control a monitor with no Id');
|
Warning('Attempt to control a monitor with no Id');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||||
if ( $this->Type() == 'Local' ) {
|
if ( $this->Type() == 'Local' ) {
|
||||||
$zmcArgs = '-d '.$this->{'Device'};
|
$zmcArgs = '-d '.$this->{'Device'};
|
||||||
} else {
|
} else {
|
||||||
|
@ -276,12 +284,13 @@ private $status_fields = array(
|
||||||
$url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmc.json';
|
$url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmc.json';
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -306,7 +315,7 @@ private $status_fields = array(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||||
if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) {
|
if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) {
|
||||||
if ( ZM_OPT_CONTROL ) {
|
if ( ZM_OPT_CONTROL ) {
|
||||||
daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'});
|
daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'});
|
||||||
|
@ -334,12 +343,13 @@ private $status_fields = array(
|
||||||
$url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zma.json';
|
$url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zma.json';
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -367,8 +377,8 @@ private $status_fields = array(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !array_key_exists('GroupIds', $this) ) {
|
if ( !property_exists($this, 'GroupIds') ) {
|
||||||
if ( array_key_exists('Id', $this) and $this->{'Id'} ) {
|
if ( property_exists($this, 'Id') and $this->{'Id'} ) {
|
||||||
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
||||||
if ( ! $this->{'GroupIds'} )
|
if ( ! $this->{'GroupIds'} )
|
||||||
$this->{'GroupIds'} = array();
|
$this->{'GroupIds'} = array();
|
||||||
|
@ -417,7 +427,7 @@ private $status_fields = array(
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'Storage'} = $new;
|
$this->{'Storage'} = $new;
|
||||||
}
|
}
|
||||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
|
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) {
|
||||||
$this->{'Storage'} = isset($this->{'StorageId'}) ?
|
$this->{'Storage'} = isset($this->{'StorageId'}) ?
|
||||||
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
||||||
new Storage(NULL);
|
new Storage(NULL);
|
||||||
|
@ -467,61 +477,65 @@ private $status_fields = array(
|
||||||
return $source;
|
return $source;
|
||||||
} // end function Source
|
} // end function Source
|
||||||
|
|
||||||
public function UrlToIndex() {
|
public function UrlToIndex($port=null) {
|
||||||
return $this->Server()->UrlToIndex();
|
return $this->Server()->UrlToIndex($port);
|
||||||
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendControlCommand($command) {
|
public function sendControlCommand($command) {
|
||||||
// command is generally a command option list like --command=blah but might be just the word quit
|
// command is generally a command option list like --command=blah but might be just the word quit
|
||||||
|
|
||||||
$options = array();
|
$options = array();
|
||||||
# Convert from a command line params to an option array
|
# Convert from a command line params to an option array
|
||||||
foreach ( explode(' ', $command) as $option ) {
|
foreach ( explode(' ', $command) as $option ) {
|
||||||
if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) {
|
if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) {
|
||||||
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
||||||
} else if ( $option != '' and $option != 'quit' ) {
|
} else if ( $option != '' and $option != 'quit' ) {
|
||||||
Warning("Ignored command for zmcontrol $option in $command");
|
Warning("Ignored command for zmcontrol $option in $command");
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !count($options) ) {
|
|
||||||
if ( $command == 'quit' ) {
|
|
||||||
$options['command'] = 'quit';
|
|
||||||
} else {
|
|
||||||
Warning("No commands to send to zmcontrol from $command");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
|
||||||
# Local
|
|
||||||
Logger::Debug('Trying to send options ' . print_r($options, true));
|
|
||||||
|
|
||||||
$optionString = jsonEncode($options);
|
|
||||||
Logger::Debug("Trying to send options $optionString");
|
|
||||||
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
|
|
||||||
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if ( $socket < 0 ) {
|
|
||||||
Error('socket_create() failed: '.socket_strerror($socket));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock';
|
|
||||||
if ( @socket_connect($socket, $sockFile) ) {
|
|
||||||
if ( !socket_write($socket, $optionString) ) {
|
|
||||||
Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket)));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else if ( $command != 'quit' ) {
|
|
||||||
$command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'};
|
|
||||||
|
|
||||||
// Can't connect so use script
|
|
||||||
$ctrlOutput = exec(escapeshellcmd($command));
|
|
||||||
}
|
}
|
||||||
socket_close($socket);
|
if ( !count($options) ) {
|
||||||
} else if ( $this->ServerId() ) {
|
if ( $command == 'quit' ) {
|
||||||
|
$options['command'] = 'quit';
|
||||||
|
} else if ( $command == 'start' ) {
|
||||||
|
$options['command'] = 'start';
|
||||||
|
} else if ( $command == 'stop' ) {
|
||||||
|
$options['command'] = 'stop';
|
||||||
|
} else {
|
||||||
|
Warning("No commands to send to zmcontrol from $command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||||
|
# Local
|
||||||
|
Logger::Debug('Trying to send options ' . print_r($options, true));
|
||||||
|
|
||||||
|
$optionString = jsonEncode($options);
|
||||||
|
Logger::Debug("Trying to send options $optionString");
|
||||||
|
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
|
||||||
|
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if ( $socket < 0 ) {
|
||||||
|
Error('socket_create() failed: '.socket_strerror($socket));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock';
|
||||||
|
if ( @socket_connect($socket, $sockFile) ) {
|
||||||
|
if ( !socket_write($socket, $optionString) ) {
|
||||||
|
Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ( $command != 'quit' ) {
|
||||||
|
$command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id='.$this->{'Id'};
|
||||||
|
|
||||||
|
// Can't connect so use script
|
||||||
|
$ctrlOutput = exec(escapeshellcmd($command));
|
||||||
|
}
|
||||||
|
socket_close($socket);
|
||||||
|
} else if ( $this->ServerId() ) {
|
||||||
$Server = $this->Server();
|
$Server = $this->Server();
|
||||||
|
|
||||||
$url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmcontrol.json';
|
$url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json';
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||||
|
@ -537,13 +551,13 @@ public function sendControlCommand($command) {
|
||||||
$context = stream_context_create();
|
$context = stream_context_create();
|
||||||
try {
|
try {
|
||||||
$result = file_get_contents($url, false, $context);
|
$result = file_get_contents($url, false, $context);
|
||||||
if ($result === FALSE) { /* Handle error */
|
if ( $result === FALSE ) { /* Handle error */
|
||||||
Error("Error restarting zma using $url");
|
Error("Error sending command using $url");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
Error("Except $e thrown trying to restart zma");
|
Error("Exception $e thrown trying to send command to $url");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Error('Server not assigned to Monitor in a multi-server setup. Please assign a server to the Monitor.');
|
Error('Server not assigned to Monitor in a multi-server setup. Please assign a server to the Monitor.');
|
||||||
|
|
|
@ -1,133 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
namespace ZM;
|
namespace ZM;
|
||||||
|
require_once('Object.php');
|
||||||
|
|
||||||
require_once('database.php');
|
class MontageLayout extends ZM_Object {
|
||||||
|
protected static $table = 'MontageLayouts';
|
||||||
class MontageLayout {
|
protected $defaults = array(
|
||||||
|
|
||||||
private $defaults = array(
|
|
||||||
'Id' => null,
|
'Id' => null,
|
||||||
'Name' => '',
|
'Name' => '',
|
||||||
'Positions' => 0,
|
'Positions' => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct( $IdOrRow = NULL ) {
|
public static function find( $parameters = array(), $options = array() ) {
|
||||||
if ( $IdOrRow ) {
|
return ZM_Object::_find(get_class(), $parameters, $options);
|
||||||
$row = NULL;
|
|
||||||
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
|
|
||||||
$row = dbFetchOne( 'SELECT * FROM MontageLayouts WHERE Id=?', NULL, array( $IdOrRow ) );
|
|
||||||
if ( ! $row ) {
|
|
||||||
Error("Unable to load MontageLayout record for Id=" . $IdOrRow );
|
|
||||||
}
|
|
||||||
} else if ( is_array( $IdOrRow ) ) {
|
|
||||||
$row = $IdOrRow;
|
|
||||||
} else {
|
|
||||||
Error("Unknown argument passed to MontageLayout Constructor ($IdOrRow)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $row ) {
|
|
||||||
foreach ($row as $k => $v) {
|
|
||||||
$this->{$k} = $v;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Error('No row for MontageLayout ' . $IdOrRow );
|
|
||||||
}
|
|
||||||
} # end if isset($IdOrRow)
|
|
||||||
} // end function __construct
|
|
||||||
|
|
||||||
public function __call($fn, array $args){
|
|
||||||
if ( count($args) ) {
|
|
||||||
$this->{$fn} = $args[0];
|
|
||||||
}
|
|
||||||
if ( array_key_exists($fn, $this) ) {
|
|
||||||
return $this->{$fn};
|
|
||||||
} else {
|
|
||||||
if ( array_key_exists( $fn, $this->defaults ) ) {
|
|
||||||
return $this->defaults{$fn};
|
|
||||||
} else {
|
|
||||||
$backTrace = debug_backtrace();
|
|
||||||
$file = $backTrace[1]['file'];
|
|
||||||
$line = $backTrace[1]['line'];
|
|
||||||
Warning( "Unknown function call MontageLayout->$fn from $file:$line" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set( $data ) {
|
public static function find_one( $parameters = array(), $options = array() ) {
|
||||||
foreach ($data as $k => $v) {
|
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||||
if ( is_array( $v ) ) {
|
|
||||||
# perhaps should turn into a comma-separated string
|
|
||||||
$this->{$k} = implode(',',$v);
|
|
||||||
} else if ( is_string( $v ) ) {
|
|
||||||
$this->{$k} = trim( $v );
|
|
||||||
} else if ( is_integer( $v ) ) {
|
|
||||||
$this->{$k} = $v;
|
|
||||||
} else if ( is_bool( $v ) ) {
|
|
||||||
$this->{$k} = $v;
|
|
||||||
} else {
|
|
||||||
Error( "Unknown type $k => $v of var " . gettype( $v ) );
|
|
||||||
$this->{$k} = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public static function find( $parameters = null, $options = null ) {
|
|
||||||
$filters = array();
|
|
||||||
$sql = 'SELECT * FROM MontageLayouts ';
|
|
||||||
$values = array();
|
|
||||||
|
|
||||||
if ( $parameters ) {
|
|
||||||
$fields = array();
|
|
||||||
$sql .= 'WHERE ';
|
|
||||||
foreach ( $parameters as $field => $value ) {
|
|
||||||
if ( $value == null ) {
|
|
||||||
$fields[] = $field.' IS NULL';
|
|
||||||
} else if ( is_array( $value ) ) {
|
|
||||||
$func = function(){return '?';};
|
|
||||||
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
|
|
||||||
$values += $value;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$fields[] = $field.'=?';
|
|
||||||
$values[] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$sql .= implode(' AND ', $fields );
|
|
||||||
}
|
|
||||||
if ( $options and isset($options['order']) ) {
|
|
||||||
$sql .= ' ORDER BY ' . $options['order'];
|
|
||||||
}
|
|
||||||
$result = dbQuery($sql, $values);
|
|
||||||
if ( $result ) {
|
|
||||||
$results = $result->fetchALL();
|
|
||||||
foreach ( $results as $row ) {
|
|
||||||
$filters[] = new MontageLayout($row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $filters;
|
|
||||||
}
|
|
||||||
public function save( $new_values = null ) {
|
|
||||||
if ( $new_values ) {
|
|
||||||
foreach ( $new_values as $k=>$v ) {
|
|
||||||
$this->{$k} = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = array_values( array_filter( array_keys($this->defaults), function($field){return $field != 'Id';} ) );
|
|
||||||
$values = null;
|
|
||||||
if ( isset($this->{'Id'}) ) {
|
|
||||||
$sql = 'UPDATE MontageLayouts SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?';
|
|
||||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
|
||||||
$values[] = $this->{'Id'};
|
|
||||||
dbQuery($sql, $values);
|
|
||||||
} else {
|
|
||||||
$sql = 'INSERT INTO MontageLayouts ('.implode( ',', $fields ).') VALUES ('.implode(',',array_map( function(){return '?';}, $fields ) ).')';
|
|
||||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
|
||||||
dbQuery($sql, $values);
|
|
||||||
global $dbConn;
|
|
||||||
$this->{'Id'} = $dbConn->lastInsertId();
|
|
||||||
}
|
|
||||||
} // end function save
|
|
||||||
|
|
||||||
} // end class MontageLayout
|
} // end class MontageLayout
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -39,13 +39,16 @@ class ZM_Object {
|
||||||
public function __call($fn, array $args){
|
public function __call($fn, array $args){
|
||||||
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
||||||
if ( count($args) ) {
|
if ( count($args) ) {
|
||||||
if ( $type == 'set' and is_array($args[0]) )
|
if ( $type == 'set' and is_array($args[0]) ) {
|
||||||
$this->{$fn} = implode(',', $args[0]);
|
$this->{$fn} = implode(',', $args[0]);
|
||||||
else
|
} else if ( array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn]) && isset($this->defaults[$fn]['filter_regexp']) ) {
|
||||||
|
$this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]);
|
||||||
|
} else {
|
||||||
$this->{$fn} = $args[0];
|
$this->{$fn} = $args[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( array_key_exists($fn, $this) ) {
|
if ( property_exists($this, $fn) ) {
|
||||||
return $this->{$fn};
|
return $this->{$fn};
|
||||||
} else {
|
} else {
|
||||||
if ( array_key_exists($fn, $this->defaults) ) {
|
if ( array_key_exists($fn, $this->defaults) ) {
|
||||||
|
@ -63,7 +66,7 @@ class ZM_Object {
|
||||||
public static function _find($class, $parameters = null, $options = null ) {
|
public static function _find($class, $parameters = null, $options = null ) {
|
||||||
$table = $class::$table;
|
$table = $class::$table;
|
||||||
$filters = array();
|
$filters = array();
|
||||||
$sql = "SELECT * FROM `$table` ";
|
$sql = 'SELECT * FROM `'.$table.'` ';
|
||||||
$values = array();
|
$values = array();
|
||||||
|
|
||||||
if ( $parameters ) {
|
if ( $parameters ) {
|
||||||
|
@ -110,8 +113,9 @@ class ZM_Object {
|
||||||
|
|
||||||
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
||||||
global $object_cache;
|
global $object_cache;
|
||||||
if ( ! isset($object_cache[$class]) )
|
if ( ! isset($object_cache[$class]) ) {
|
||||||
$object_cache[$class] = array();
|
$object_cache[$class] = array();
|
||||||
|
}
|
||||||
$cache = &$object_cache[$class];
|
$cache = &$object_cache[$class];
|
||||||
if (
|
if (
|
||||||
( count($parameters) == 1 ) and
|
( count($parameters) == 1 ) and
|
||||||
|
@ -127,6 +131,11 @@ class ZM_Object {
|
||||||
return $results[0];
|
return $results[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function _clear_cache($class) {
|
||||||
|
global $object_cache;
|
||||||
|
$object_cache[$class] = array();
|
||||||
|
}
|
||||||
|
|
||||||
public static function Objects_Indexed_By_Id($class) {
|
public static function Objects_Indexed_By_Id($class) {
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
|
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
|
||||||
|
@ -140,10 +149,10 @@ class ZM_Object {
|
||||||
foreach ($this->defaults as $key => $value) {
|
foreach ($this->defaults as $key => $value) {
|
||||||
if ( is_callable(array($this, $key)) ) {
|
if ( is_callable(array($this, $key)) ) {
|
||||||
$json[$key] = $this->$key();
|
$json[$key] = $this->$key();
|
||||||
} else if ( array_key_exists($key, $this) ) {
|
} else if ( property_exists($this, $key) ) {
|
||||||
$json[$key] = $this->{$key};
|
$json[$key] = $this->{$key};
|
||||||
} else {
|
} else {
|
||||||
$json[$key] = $this->defaults{$key};
|
$json[$key] = $this->defaults[$key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return json_encode($json);
|
return json_encode($json);
|
||||||
|
@ -158,13 +167,10 @@ class ZM_Object {
|
||||||
# perhaps should turn into a comma-separated string
|
# perhaps should turn into a comma-separated string
|
||||||
$this->{$k} = implode(',', $v);
|
$this->{$k} = implode(',', $v);
|
||||||
} else if ( is_string($v) ) {
|
} else if ( is_string($v) ) {
|
||||||
if ( $v == '' and array_key_exists($k, $this->defaults) ) {
|
if ( array_key_exists($k, $this->defaults) && is_array($this->defaults[$k]) && isset($this->defaults[$k]['filter_regexp']) ) {
|
||||||
if ( is_array($this->defaults[$k]) )
|
$this->{$k} = preg_replace($this->defaults[$k]['filter_regexp'], '', trim($v));
|
||||||
$this->{$k} = $this->defaults[$k]['default'];
|
|
||||||
else
|
|
||||||
$this->{$k} = $this->defaults[$k];
|
|
||||||
} else {
|
} else {
|
||||||
$this->{$k} = trim($v);
|
$this->{$k} = trim($v);
|
||||||
}
|
}
|
||||||
} else if ( is_integer($v) ) {
|
} else if ( is_integer($v) ) {
|
||||||
$this->{$k} = $v;
|
$this->{$k} = $v;
|
||||||
|
@ -200,7 +206,8 @@ class ZM_Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} # end foreach default
|
} # end foreach default
|
||||||
}
|
} # end if defaults
|
||||||
|
|
||||||
foreach ( $new_values as $field => $value ) {
|
foreach ( $new_values as $field => $value ) {
|
||||||
|
|
||||||
if ( method_exists($this, $field) ) {
|
if ( method_exists($this, $field) ) {
|
||||||
|
@ -215,7 +222,7 @@ class ZM_Object {
|
||||||
} else if ( $this->$field() != $value ) {
|
} else if ( $this->$field() != $value ) {
|
||||||
$changes[$field] = $value;
|
$changes[$field] = $value;
|
||||||
}
|
}
|
||||||
} else if ( array_key_exists($field, $this) ) {
|
} else if ( property_exists($this, $field) ) {
|
||||||
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
||||||
Logger::Debug("Checking field $field => current ".
|
Logger::Debug("Checking field $field => current ".
|
||||||
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
||||||
|
@ -234,6 +241,9 @@ class ZM_Object {
|
||||||
# Input might be a command separated string, or an array
|
# Input might be a command separated string, or an array
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) {
|
||||||
|
$value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
||||||
|
}
|
||||||
if ( $this->{$field} != $value ) {
|
if ( $this->{$field} != $value ) {
|
||||||
$changes[$field] = $value;
|
$changes[$field] = $value;
|
||||||
}
|
}
|
||||||
|
@ -254,17 +264,6 @@ class ZM_Object {
|
||||||
$changes[$field] = $value;
|
$changes[$field] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ( (!array_key_exists($field, $this)) or ( $this->{$field} != $new_values[$field] ) ) {
|
|
||||||
#Logger::Debug("Checking default $field => $default_value changes becaause" . $new_values[$field].' != '.$new_values[$field]);
|
|
||||||
#$changes[$field] = $new_values[$field];
|
|
||||||
##} else if {
|
|
||||||
#Logger::Debug("Checking default $field => $default_value changes becaause " . $new_values[$field].' != '.$new_values[$field]);
|
|
||||||
##array_push( $changes, [$field=>$defaults[$field]] );
|
|
||||||
#}
|
|
||||||
#} else {
|
|
||||||
#Logger::Debug("Checking default $field => $default_value not in new_values");
|
|
||||||
#}
|
|
||||||
} # end foreach newvalue
|
} # end foreach newvalue
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,6 +279,18 @@ class ZM_Object {
|
||||||
$this->set($new_values);
|
$this->set($new_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set defaults. Note that we only replace "" with null, not other values
|
||||||
|
# because for example if we want to clear TimestampFormat, we clear it, but the default is a string value
|
||||||
|
foreach ( $this->defaults as $field => $default ) {
|
||||||
|
if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) {
|
||||||
|
if ( is_array($default) ) {
|
||||||
|
$this->{$field} = $default['default'];
|
||||||
|
} else if ( $default == null ) {
|
||||||
|
$this->{$field} = $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$fields = array_filter(
|
$fields = array_filter(
|
||||||
$this->defaults,
|
$this->defaults,
|
||||||
function($v) {
|
function($v) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ class Server extends ZM_Object {
|
||||||
|
|
||||||
public function PathToZMS( $new = null ) {
|
public function PathToZMS( $new = null ) {
|
||||||
if ( $new != null )
|
if ( $new != null )
|
||||||
$this{'PathToZMS'} = $new;
|
$this->{'PathToZMS'} = $new;
|
||||||
if ( $this->Id() and $this->{'PathToZMS'} ) {
|
if ( $this->Id() and $this->{'PathToZMS'} ) {
|
||||||
return $this->{'PathToZMS'};
|
return $this->{'PathToZMS'};
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -58,6 +58,13 @@ class Storage extends ZM_Object {
|
||||||
return $this->{'Events'};
|
return $this->{'Events'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function EventCount() {
|
||||||
|
if ( (! property_exists($this, 'EventCount')) or (!$this->{'EventCount'}) ) {
|
||||||
|
$this->{'EventCount'} = dbFetchOne('SELECT COUNT(*) AS EventCount FROM Events WHERE StorageId=?', 'EventCount', array($this->Id()));
|
||||||
|
}
|
||||||
|
return $this->{'EventCount'};
|
||||||
|
}
|
||||||
|
|
||||||
public function disk_usage_percent() {
|
public function disk_usage_percent() {
|
||||||
$path = $this->Path();
|
$path = $this->Path();
|
||||||
if ( ! $path ) {
|
if ( ! $path ) {
|
||||||
|
@ -80,7 +87,7 @@ class Storage extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function disk_total_space() {
|
public function disk_total_space() {
|
||||||
if ( !array_key_exists('disk_total_space', $this) ) {
|
if ( !property_exists($this, 'disk_total_space') ) {
|
||||||
$path = $this->Path();
|
$path = $this->Path();
|
||||||
if ( file_exists($path) ) {
|
if ( file_exists($path) ) {
|
||||||
$this->{'disk_total_space'} = disk_total_space($path);
|
$this->{'disk_total_space'} = disk_total_space($path);
|
||||||
|
@ -94,7 +101,7 @@ class Storage extends ZM_Object {
|
||||||
|
|
||||||
public function disk_used_space() {
|
public function disk_used_space() {
|
||||||
# This isn't a function like this in php, so we have to add up the space used in each event.
|
# This isn't a function like this in php, so we have to add up the space used in each event.
|
||||||
if ( ( !array_key_exists('disk_used_space', $this)) or !$this->{'disk_used_space'} ) {
|
if ( ( !property_exists($this, 'disk_used_space')) or !$this->{'disk_used_space'} ) {
|
||||||
if ( $this->{'Type'} == 's3fs' ) {
|
if ( $this->{'Type'} == 's3fs' ) {
|
||||||
$this->{'disk_used_space'} = $this->event_disk_space();
|
$this->{'disk_used_space'} = $this->event_disk_space();
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,17 +119,18 @@ class Storage extends ZM_Object {
|
||||||
|
|
||||||
public function event_disk_space() {
|
public function event_disk_space() {
|
||||||
# This isn't a function like this in php, so we have to add up the space used in each event.
|
# This isn't a function like this in php, so we have to add up the space used in each event.
|
||||||
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) {
|
if ( (! property_exists($this, 'DiskSpace')) or (!$this->{'DiskSpace'}) ) {
|
||||||
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
|
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
# Do in batches of 1000 so as to not useup all ram
|
# Do in batches of 1000 so as to not useup all ram, Event will do caching though...
|
||||||
$events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000));
|
$events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000));
|
||||||
foreach ( $events as $Event ) {
|
foreach ( $events as $Event ) {
|
||||||
$Event->Storage($this); // Prevent further db hit
|
$Event->Storage($this); // Prevent further db hit
|
||||||
# DiskSpace will update the event
|
# DiskSpace will update the event
|
||||||
$used += $Event->DiskSpace();
|
$used += $Event->DiskSpace();
|
||||||
} #end foreach
|
} #end foreach
|
||||||
|
Event::clear_cache();
|
||||||
} while ( count($events) == 1000 );
|
} while ( count($events) == 1000 );
|
||||||
$this->{'DiskSpace'} = $used;
|
$this->{'DiskSpace'} = $used;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +138,8 @@ class Storage extends ZM_Object {
|
||||||
} // end function event_disk_space
|
} // end function event_disk_space
|
||||||
|
|
||||||
public function Server() {
|
public function Server() {
|
||||||
if ( ! array_key_exists('Server',$this) ) {
|
if ( ! property_exists($this, 'Server') ) {
|
||||||
if ( array_key_exists('ServerId', $this) ) {
|
if ( property_exists($this, 'ServerId') ) {
|
||||||
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||||
|
|
||||||
if ( !$this->{'Server'} ) {
|
if ( !$this->{'Server'} ) {
|
||||||
|
|
|
@ -28,6 +28,65 @@ if ( $action == 'controlcap' ) {
|
||||||
require_once('includes/Control.php');
|
require_once('includes/Control.php');
|
||||||
$Control = new ZM\Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
|
$Control = new ZM\Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
|
||||||
|
|
||||||
|
$field_defaults = array(
|
||||||
|
'CanWake' => 0,
|
||||||
|
'CanSleep' => 0,
|
||||||
|
'CanReset' => 0,
|
||||||
|
'CanReboot' => 0,
|
||||||
|
'CanMove' => 0,
|
||||||
|
'CanMoveDiag' => 0,
|
||||||
|
'CanMoveMap' => 0,
|
||||||
|
'CanMoveRel' => 0,
|
||||||
|
'CanMoveAbs' => 0,
|
||||||
|
'CanMoveCon' => 0,
|
||||||
|
'CanPan' => 0,
|
||||||
|
'HasPanSpeed' => 0,
|
||||||
|
'HasTurboPan' => 0,
|
||||||
|
'CanTilt' => 0,
|
||||||
|
'HasTiltSpeed' => 0,
|
||||||
|
'HasTurboTilt' => 0,
|
||||||
|
'CanZoom' => 0,
|
||||||
|
'CanZoomRel' => 0,
|
||||||
|
'CanZoomAbs' => 0,
|
||||||
|
'CanZoomCon' => 0,
|
||||||
|
'HasZoomSpeed' => 0,
|
||||||
|
'CanFocus' => 0,
|
||||||
|
'CanAutoFocus' => 0,
|
||||||
|
'CanFocusRel' => 0,
|
||||||
|
'CanFocusAbs' => 0,
|
||||||
|
'CanFocusCon' => 0,
|
||||||
|
'HasFocusSpeed' => 0,
|
||||||
|
'CanGain' => 0,
|
||||||
|
'CanAutoGain' => 0,
|
||||||
|
'CanGainRel' => 0,
|
||||||
|
'CanGainAbs' => 0,
|
||||||
|
'CanGainCon' => 0,
|
||||||
|
'HasGainSpeed' => 0,
|
||||||
|
'CanWhite' => 0,
|
||||||
|
'CanAutoWhite' => 0,
|
||||||
|
'CanWhiteRel' => 0,
|
||||||
|
'CanWhiteAbs' => 0,
|
||||||
|
'CanWhiteCon' => 0,
|
||||||
|
'HasWhiteSpeed' => 0,
|
||||||
|
'CanIris' => 0,
|
||||||
|
'CanAutoIris' => 0,
|
||||||
|
'CanIrisRel' => 0,
|
||||||
|
'CanIrisAbs' => 0,
|
||||||
|
'CanIrisCon' => 0,
|
||||||
|
'HasIrisSpeed' => 0,
|
||||||
|
'HasPresets' => 0,
|
||||||
|
'HasHomePreset' => 0,
|
||||||
|
'CanSetPresets' => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
# Checkboxes don't return an element in the POST data, so won't be present in newControl.
|
||||||
|
# So force a value for these fields
|
||||||
|
foreach ( $field_defaults as $field => $value ) {
|
||||||
|
if ( ! (isset($_REQUEST['newControl'][$field]) and $_REQUEST['newControl'][$field]) ) {
|
||||||
|
$_REQUEST['newControl'][$field] = $value;
|
||||||
|
}
|
||||||
|
} # end foreach type
|
||||||
|
|
||||||
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
|
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
|
||||||
$Control->save($_REQUEST['newControl']);
|
$Control->save($_REQUEST['newControl']);
|
||||||
$refreshParent = true;
|
$refreshParent = true;
|
||||||
|
|
|
@ -64,35 +64,12 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
|
||||||
$_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
|
$_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
|
||||||
$_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
|
$_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
|
||||||
$changes = $filter->changes($_REQUEST['filter']);
|
$changes = $filter->changes($_REQUEST['filter']);
|
||||||
ZM\Logger::Debug("Changes: " . print_r($changes,true));
|
ZM\Logger::Debug('Changes: ' . print_r($changes,true));
|
||||||
|
|
||||||
if ( 0 ) {
|
|
||||||
$sql .= ', Query = '.dbEscape(jsonEncode($_REQUEST['filter']['Query']));
|
|
||||||
$sql .= ', AutoArchive = '.(!empty($_REQUEST['filter']['AutoArchive']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoVideo = '. ( !empty($_REQUEST['filter']['AutoVideo']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoUpload = '. ( !empty($_REQUEST['filter']['AutoUpload']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoEmail = '. ( !empty($_REQUEST['filter']['AutoEmail']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoMessage = '. ( !empty($_REQUEST['filter']['AutoMessage']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0);
|
|
||||||
$sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']);
|
|
||||||
$sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0);
|
|
||||||
if ( !empty($_REQUEST['filter']['AutoMove']) ? 1 : 0) {
|
|
||||||
$sql .= ', AutoMove = 1, AutoMoveTo='. validInt($_REQUEST['filter']['AutoMoveTo']);
|
|
||||||
} else {
|
|
||||||
$sql .= ', AutoMove = 0';
|
|
||||||
}
|
|
||||||
$sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0);
|
|
||||||
$sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
|
|
||||||
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
|
if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
|
||||||
if ( 0 ) {
|
|
||||||
dbQuery('UPDATE Filters SET '.$sql.' WHERE Id=?', array($_REQUEST['Id']));
|
|
||||||
}
|
|
||||||
$filter->save($changes);
|
|
||||||
if ( $filter->Background() )
|
if ( $filter->Background() )
|
||||||
$filter->control('stop');
|
$filter->control('stop');
|
||||||
|
$filter->save($changes);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if ( $action == 'execute' ) {
|
if ( $action == 'execute' ) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ if ( $action == 'function' ) {
|
||||||
$oldFunction = $monitor['Function'];
|
$oldFunction = $monitor['Function'];
|
||||||
$oldEnabled = $monitor['Enabled'];
|
$oldEnabled = $monitor['Enabled'];
|
||||||
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
|
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
|
||||||
dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?',
|
dbQuery('UPDATE Monitors SET `Function`=?, `Enabled`=? WHERE `Id`=?',
|
||||||
array($newFunction, $newEnabled, $mid));
|
array($newFunction, $newEnabled, $mid));
|
||||||
|
|
||||||
$monitor['Function'] = $newFunction;
|
$monitor['Function'] = $newFunction;
|
||||||
|
|
|
@ -21,42 +21,31 @@
|
||||||
// Group edit actions
|
// Group edit actions
|
||||||
# Should probably verify that each monitor id is a valid monitor, that we have access to.
|
# Should probably verify that each monitor id is a valid monitor, that we have access to.
|
||||||
# However at the moment, you have to have System permissions to do this
|
# However at the moment, you have to have System permissions to do this
|
||||||
if ( ! canEdit('Groups') ) {
|
if ( !canEdit('Groups') ) {
|
||||||
ZM\Warning('Need group edit permissions to edit groups');
|
ZM\Warning('Need group edit permissions to edit groups');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $action == 'Save' ) {
|
if ( $action == 'Save' ) {
|
||||||
$monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
|
|
||||||
$group_id = null;
|
$group_id = null;
|
||||||
if ( !empty($_POST['gid']) ) {
|
if ( !empty($_POST['gid']) )
|
||||||
$group_id = $_POST['gid'];
|
$group_id = $_POST['gid'];
|
||||||
dbQuery(
|
$group = new ZM\Group($group_id);
|
||||||
'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
|
$group->save(
|
||||||
array(
|
array(
|
||||||
$_POST['newGroup']['Name'],
|
'Name'=> $_POST['newGroup']['Name'],
|
||||||
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
|
'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
|
||||||
$group_id,
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id));
|
dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id));
|
||||||
} else {
|
$group_id = $group->Id();
|
||||||
dbQuery(
|
|
||||||
'INSERT INTO Groups (Name,ParentId) VALUES (?,?)',
|
|
||||||
array(
|
|
||||||
$_POST['newGroup']['Name'],
|
|
||||||
( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$group_id = dbInsertId();
|
|
||||||
}
|
|
||||||
if ( $group_id ) {
|
if ( $group_id ) {
|
||||||
foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
|
foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
|
||||||
dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid));
|
dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$view = 'none';
|
$view = 'none';
|
||||||
$refreshParent = true;
|
$refreshParent = true;
|
||||||
$closePopup = true;
|
$closePopup = true;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -29,8 +29,13 @@ if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == '
|
||||||
&& defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY')
|
&& defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY')
|
||||||
&& ZM_OPT_USE_GOOG_RECAPTCHA
|
&& ZM_OPT_USE_GOOG_RECAPTCHA
|
||||||
&& ZM_OPT_GOOG_RECAPTCHA_SECRETKEY
|
&& ZM_OPT_GOOG_RECAPTCHA_SECRETKEY
|
||||||
&& ZM_OPT_GOOG_RECAPTCHA_SITEKEY )
|
&& ZM_OPT_GOOG_RECAPTCHA_SITEKEY
|
||||||
{
|
) {
|
||||||
|
if ( !isset($_REQUEST['g-recaptcha-response']) ) {
|
||||||
|
ZM\Error('reCaptcha authentication failed. No g-recpatcha-response in REQUEST: ');
|
||||||
|
unset($user); // unset should be ok here because we aren't in a function
|
||||||
|
return;
|
||||||
|
}
|
||||||
$url = 'https://www.google.com/recaptcha/api/siteverify';
|
$url = 'https://www.google.com/recaptcha/api/siteverify';
|
||||||
$fields = array (
|
$fields = array (
|
||||||
'secret' => ZM_OPT_GOOG_RECAPTCHA_SECRETKEY,
|
'secret' => ZM_OPT_GOOG_RECAPTCHA_SECRETKEY,
|
||||||
|
@ -49,14 +54,23 @@ if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == '
|
||||||
// as it produces the same error as when you don't answer a recaptcha
|
// as it produces the same error as when you don't answer a recaptcha
|
||||||
if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
|
if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
|
||||||
if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) {
|
if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) {
|
||||||
Error('reCaptcha authentication failed');
|
ZM\Error('reCaptcha authentication failed. response was: ' . print_r($responseData['error-codes'],true));
|
||||||
unset($user); // unset should be ok here because we aren't in a function
|
unset($user); // unset should be ok here because we aren't in a function
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
Error('Invalid recaptcha secret detected');
|
ZM\Error('Invalid recaptcha secret detected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end if success==false
|
} // end if success==false
|
||||||
|
if ( ! (empty($_REQUEST['username']) or empty($_REQUEST['password'])) ) {
|
||||||
|
$ret = validateUser($_REQUEST['username'], $_REQUEST['password']);
|
||||||
|
if ( !$ret[0] ) {
|
||||||
|
ZM\Error($ret[1]);
|
||||||
|
unset($user); // unset should be ok here because we aren't in a function
|
||||||
|
} else {
|
||||||
|
$user = $ret[0];
|
||||||
|
}
|
||||||
|
} # end if have username and password
|
||||||
} // end if using reCaptcha
|
} // end if using reCaptcha
|
||||||
|
|
||||||
// if captcha existed, it was passed
|
// if captcha existed, it was passed
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue